diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..c976a7f03 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[{*.yaml,*.yml}] +indent_size = 2 diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..07d3b29d5 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +# Compilation/build outputs +build/ +dist/ +out/ diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..98d497f2c --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,13 @@ +module.exports = { + root: true, + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.eslint.json', + }, + settings: { + jest: { + version: 28, + }, + }, + extends: '@lars-reimann', +}; diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000..a6e0fb0c7 --- /dev/null +++ b/.flake8 @@ -0,0 +1,9 @@ +[flake8] +extend-ignore = + # line too long (black handles that) + E501, + # Line break occurred before a binary operator (seems to conflict with the linter) + W503 +per-file-ignores = + # imported but unused + __init__.py: F401 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..176a458f9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..5c9e7de0f --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @lars-reimann diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..fd24275c9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,32 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + # Root + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + + # DSL + - package-ecosystem: "gradle" + directory: "/DSL" + schedule: + interval: "monthly" + - package-ecosystem: "npm" + directory: "/DSL/de.unibonn.simpleml.vscode" + schedule: + interval: "monthly" + + # Runtime + - package-ecosystem: "pip" + directory: "/Runtime/safe-ds" + schedule: + interval: "monthly" + - package-ecosystem: "pip" + directory: "/Runtime/safe-ds-runner" + schedule: + interval: "monthly" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 000000000..72ffb6230 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,16 @@ +name: 'Dependency Review' + +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v3 + + - name: 'Dependency Review' + uses: actions/dependency-review-action@v1 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..8ba118716 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,125 @@ +name: Main +on: + push: + branches: [ main ] + +jobs: + # Build and test DSL component + build-dsl: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./DSL + strategy: + matrix: + java-version: [ 17 ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up JDK ${{ matrix.java-version }} + uses: actions/setup-java@v3 + with: + distribution: adopt + java-version: ${{ matrix.java-version }} + cache: gradle + + # See https://docs.github.com/en/actions/guides/building-and-testing-java-with-gradle + - name: Validate Gradle wrapper + uses: gradle/wrapper-validation-action@v1 + + - name: Test with Gradle + run: ./gradlew check + + - name: Upload test report + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: Test report + # upload-artifact does not use working-directory + path: DSL/de.unibonn.simpleml/build/reports/tests/test/ + + # Build and test Runtime > Runner component + build-runtime-runner: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./Runtime/safe-ds-runner + strategy: + matrix: + python-version: [ "3.10" ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Poetry + uses: snok/install-poetry@v1.3.1 + with: + virtualenvs-in-project: true + + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@v3.0.3 + with: + path: .venv + key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }} + + - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + run: poetry install --no-interaction --no-root + + - name: Install library + run: poetry install --no-interaction + + # Requires installation of pytest and pytest-cov + - name: Test with pytest + run: poetry run pytest --doctest-modules + + # Build and test Runtime > Stdlib component + build-runtime-stdlib: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./Runtime/safe-ds-runner + strategy: + matrix: + python-version: [ "3.10" ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Poetry + uses: snok/install-poetry@v1.3.1 + with: + virtualenvs-in-project: true + + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@v3.0.3 + with: + path: .venv + key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }} + + - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + run: poetry install --no-interaction --no-root + + - name: Install library + run: poetry install --no-interaction + + # Requires installation of pytest and pytest-cov + - name: Test with pytest + run: poetry run pytest --doctest-modules diff --git a/.github/workflows/megalinter.yml b/.github/workflows/megalinter.yml new file mode 100644 index 000000000..b3d2094f2 --- /dev/null +++ b/.github/workflows/megalinter.yml @@ -0,0 +1,11 @@ +name: MegaLinter + +on: + pull_request: + branches: [main, master] + +jobs: + run-megalinter: + uses: lars-reimann/.github/.github/workflows/megalinter-reusable.yml@main + secrets: + PAT: ${{ secrets.PAT }} diff --git a/.github/workflows/pr-format.yml b/.github/workflows/pr-format.yml new file mode 100644 index 000000000..3b1abc637 --- /dev/null +++ b/.github/workflows/pr-format.yml @@ -0,0 +1,13 @@ +name: Pull Request Format + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + - reopened + +jobs: + check-format: + uses: lars-reimann/.github/.github/workflows/pr-format-reusable.yml@main diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 000000000..5fc4635d1 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,155 @@ +name: Pull Request + +on: + pull_request: + branches: [ main ] + +concurrency: + group: ${{ github.head_ref }}-${{ github.workflow }} + cancel-in-progress: true + +jobs: + + # Build and test DSL component + build-dsl: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./DSL + strategy: + matrix: + java-version: [ 17 ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up JDK ${{ matrix.java-version }} + uses: actions/setup-java@v3 + with: + distribution: adopt + java-version: ${{ matrix.java-version }} + cache: gradle + + # See https://docs.github.com/en/actions/guides/building-and-testing-java-with-gradle + - name: Validate Gradle wrapper + uses: gradle/wrapper-validation-action@v1 + + - name: Test with Gradle + run: ./gradlew check + + - name: Upload test report + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: Test report + # upload-artifact does not use working-directory + path: | + DSL/de.unibonn.simpleml/build/reports/tests/test/ + DSL/de.unibonn.simpleml.ide/build/reports/tests/test/ + + - name: Upload test coverage + if: ${{ github.actor != 'dependabot[bot]' }} + uses: actions/upload-artifact@v3 + with: + name: DSL test coverage + # upload-artifact does not use working-directory + path: DSL/build/reports/kover/html/ + + # Build and test Runtime > Runner component + build-runtime-runner: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./Runtime/safe-ds-runner + strategy: + matrix: + python-version: [ "3.10" ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Poetry + uses: snok/install-poetry@v1.3.1 + with: + virtualenvs-in-project: true + + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@v3.0.3 + with: + path: .venv + key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }} + + - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + run: poetry install --no-interaction --no-root + + - name: Install library + run: poetry install --no-interaction + + # Requires installation of pytest and pytest-cov + - name: Test with pytest + run: poetry run pytest --doctest-modules --cov=package_parser --cov-report=html + + - name: Upload test coverage + if: ${{ github.actor != 'dependabot[bot]' }} + uses: actions/upload-artifact@v3 + with: + name: runner test coverage + path: Runtime/safe-ds-runner/htmlcov + + # Build and test Runtime > Stdlib component + build-runtime-stdlib: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./Runtime/safe-ds-runner + strategy: + matrix: + python-version: [ "3.10" ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Poetry + uses: snok/install-poetry@v1.3.1 + with: + virtualenvs-in-project: true + + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@v3.0.3 + with: + path: .venv + key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }} + + - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + run: poetry install --no-interaction --no-root + + - name: Install library + run: poetry install --no-interaction + + # Requires installation of pytest and pytest-cov + - name: Test with pytest + run: poetry run pytest --doctest-modules --cov=package_parser --cov-report=html + + - name: Upload test coverage + if: ${{ github.actor != 'dependabot[bot]' }} + uses: actions/upload-artifact@v3 + with: + name: stdlib test coverage + path: Runtime/safe-ds/htmlcov diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..0d78d097e --- /dev/null +++ b/.gitignore @@ -0,0 +1,47 @@ +# Eclipse (also used by Gradle support in VS Code) +.settings/ +.classpath +.project + +# IntelliJ +.idea/ +*.iml + +# VSCode Settings +.vscode/ + +# Compilation/build outputs +build/ +dist/ +out/ + +# Gradle +.gradle/ + +# Node +.npm/ +node_modules/ +.node_repl_history +npm-debug.log* +pnpm-lock.yaml +yarn.lock + +# Python binaries +*.egg-info/ +__pycache__/ +*.pyc + +# Python environment +venv/ + +# Pytest outputs +.pytest_cache/ +htmlcov/ +.coverage + +# MegaLinter +report/ + +# Other +.DS_Store/ +*.log diff --git a/.mega-linter.yml b/.mega-linter.yml new file mode 100644 index 000000000..65251a856 --- /dev/null +++ b/.mega-linter.yml @@ -0,0 +1,123 @@ +# See all available variables at https://megalinter.github.io/configuration/ and in linters documentation. + +# General Config +APPLY_FIXES: all +DEFAULT_BRANCH: main +FILEIO_REPORTER: false +FILTER_REGEX_EXCLUDE: (\.github/workflows/|DSL/de\.unibonn\.simpleml/src/test/resources/) +IGNORE_GITIGNORED_FILES: true +PRINT_ALPACA: false +SHOW_ELAPSED_TIME: true +VALIDATE_ALL_CODEBASE: false + +# JavaScript/TypeScript Config +JAVASCRIPT_DEFAULT_STYLE: prettier +JAVASCRIPT_PRETTIER_FILE_EXTENSIONS: + - .js + - .jsx +TYPESCRIPT_DEFAULT_STYLE: prettier +TYPESCRIPT_PRETTIER_FILE_EXTENSIONS: + - .ts + - .tsx + +# Commands +PRE_COMMANDS: + - command: npm install + cwd: workspace + +# Linters +ENABLE_LINTERS: + - ACTION_ACTIONLINT # GitHub actions + # - ANSIBLE_ANSIBLE_LINT + # - ARM_ARM_TTK + - BASH_EXEC + - BASH_SHELLCHECK + - BASH_SHFMT + - C_CPPLINT + # - CLOJURE_CLJ_KONDO + # - CLOUDFORMATION_CFN_LINT + # - COFFEE_COFFEELINT + # - COPYPASTE_JSCPD # Slow + copy & paste sometimes makes sense, e.g. for tests + - CPP_CPPLINT + - CREDENTIALS_SECRETLINT + - CSHARP_DOTNET_FORMAT + - CSS_STYLELINT + # - CSS_SCSS_LINT + # - DART_DARTANALYZER + - DOCKERFILE_DOCKERFILELINT + - DOCKERFILE_HADOLINT + # - EDITORCONFIG_EDITORCONFIG_CHECKER # Overlaps with formatters for specific languages + - ENV_DOTENV_LINTER + # - GHERKIN_GHERKIN_LINT + - GIT_GIT_DIFF + - GO_GOLANGCI_LINT + - GO_REVIVE + - GRAPHQL_GRAPHQL_SCHEMA_LINTER + - GROOVY_NPM_GROOVY_LINT + - HTML_HTMLHINT + - JAVA_CHECKSTYLE + - JAVASCRIPT_ES + # - JAVASCRIPT_STANDARD # We use prettier + - JAVASCRIPT_PRETTIER + - JSON_JSONLINT + - JSON_ESLINT_PLUGIN_JSONC + - JSON_V8R + - JSON_PRETTIER + - JSX_ESLINT + - KOTLIN_KTLINT + - KUBERNETES_KUBEVAL + - LATEX_CHKTEX + # - LUA_LUACHECK + - MARKDOWN_MARKDOWNLINT + - MARKDOWN_REMARK_LINT + - MARKDOWN_MARKDOWN_LINK_CHECK + - MARKDOWN_MARKDOWN_TABLE_FORMATTER + - OPENAPI_SPECTRAL + # - PERL_PERLCRITIC + # - PHP_BUILTIN + # - PHP_PHPCS + # - PHP_PHPSTAN + # - PHP_PSALM + - POWERSHELL_POWERSHELL + # - PROTOBUF_PROTOLINT + # - PUPPET_PUPPET_LINT + - PYTHON_PYLINT + - PYTHON_BLACK + - PYTHON_FLAKE8 + - PYTHON_ISORT + - PYTHON_BANDIT + - PYTHON_MYPY + - R_LINTR + # - RAKU_RAKU + - RST_RST_LINT + - RST_RSTCHECK + - RST_RSTFMT + # - RUBY_RUBOCOP + - RUST_CLIPPY + # - SALESFORCE_SFDX_SCANNER_APEX + # - SALESFORCE_SFDX_SCANNER_AURA + # - SALESFORCE_SFDX_SCANNER_LWC + # - SCALA_SCALAFIX + # - SNAKEMAKE_LINT + # - SNAKEMAKE_SNAKEFMT + # - SPELL_MISSPELL # Many false positives + # - SPELL_CSPELL # Many false positives + - SQL_SQL_LINT + - SQL_SQLFLUFF + - SQL_TSQLLINT + # - SWIFT_SWIFTLINT + # - TEKTON_TEKTON_LINT + # - TERRAFORM_TFLINT + # - TERRAFORM_TERRASCAN + # - TERRAFORM_TERRAGRUNT + # - TERRAFORM_TERRAFORM_FMT + # - TERRAFORM_CHECKOV + - TSX_ESLINT + - TYPESCRIPT_ES + # - TYPESCRIPT_STANDARD # We use prettier + - TYPESCRIPT_PRETTIER + # - VBDOTNET_DOTNET_FORMAT + - XML_XMLLINT + - YAML_PRETTIER + - YAML_YAMLLINT + - YAML_V8R diff --git a/DSL/.gitignore b/DSL/.gitignore new file mode 100644 index 000000000..66b17bceb --- /dev/null +++ b/DSL/.gitignore @@ -0,0 +1,2 @@ +# Xtext test module (gets created by workflow but removed afterwards) +de.unibonn.simpleml.tests/ diff --git a/DSL/README.md b/DSL/README.md new file mode 100644 index 000000000..0633ca0b9 --- /dev/null +++ b/DSL/README.md @@ -0,0 +1,26 @@ +# DSL + +## Installation for Developers + +1. Install [VS Code](https://code.visualstudio.com/). +2. Clone this repository. +3. Build everything: + ```shell + ./gradlew build + ``` + +## Execution + +### Running the VS Code Extension + +1. Ensure VS Code is closed. +2. Install the extension and launch VS Code: + ```shell + ./gradlew launchVSCode + ``` + +### Generating the Stdlib documentation + +```shell +./gradlew generateStdlibDocumentation +``` diff --git a/DSL/build.gradle.kts b/DSL/build.gradle.kts new file mode 100644 index 000000000..44c412125 --- /dev/null +++ b/DSL/build.gradle.kts @@ -0,0 +1,230 @@ +import de.unibonn.simpleml.xtextConfiguration.code +import de.unibonn.simpleml.xtextConfiguration.configuration +import de.unibonn.simpleml.xtextConfiguration.directoryCleaner +import de.unibonn.simpleml.xtextConfiguration.ecoreGenerator +import de.unibonn.simpleml.xtextConfiguration.execute +import de.unibonn.simpleml.xtextConfiguration.project +import de.unibonn.simpleml.xtextConfiguration.projectMapping +import de.unibonn.simpleml.xtextConfiguration.standaloneSetup +import de.unibonn.simpleml.xtextConfiguration.standardLanguage +import de.unibonn.simpleml.xtextConfiguration.workflow +import de.unibonn.simpleml.xtextConfiguration.xtextGenerator +import org.eclipse.xtext.xtext.generator.formatting.Formatter2Fragment2 +import org.eclipse.xtext.xtext.generator.generator.GeneratorFragment2 +import org.eclipse.xtext.xtext.generator.junit.JUnitFragment +import org.eclipse.xtext.xtext.generator.model.project.BundleProjectConfig +import org.eclipse.xtext.xtext.generator.model.project.RuntimeProjectConfig +import org.eclipse.xtext.xtext.generator.serializer.SerializerFragment2 +import org.eclipse.xtext.xtext.generator.validation.ValidatorFragment2 + +// Plugins ------------------------------------------------------------------------------------------------------------- + +plugins { + kotlin("jvm") version "1.6.21" apply false + id("com.github.node-gradle.node") version "3.2.1" apply false + + base + idea + id("org.jetbrains.kotlinx.kover") version "0.5.0" +} + +repositories { + mavenCentral() +} + +idea { + module { + excludeDirs.add(file("gradle")) + } +} + +kover { + coverageEngine.set(kotlinx.kover.api.CoverageEngine.INTELLIJ) + this.disabledProjects = setOf( + "Simple-ML.DSL", + "de.unibonn.simpleml.vscode" + ) +} + +val koverExcludes = listOf( + "de.unibonn.simpleml.parser.antlr.*", + "de.unibonn.simpleml.serializer.AbstractSimpleMLSemanticSequencer", + "de.unibonn.simpleml.serializer.AbstractSimpleMLSyntacticSequencer", + "de.unibonn.simpleml.services.*", + "de.unibonn.simpleml.simpleML.*", + "de.unibonn.simpleml.testing.*", + "de.unibonn.simpleml.ide.contentassist.antlr.*" +) + +// Variables ----------------------------------------------------------------------------------------------------------- + +val javaVersion by extra(17) +val xtextVersion by extra("2.27.0") + +// Subprojects --------------------------------------------------------------------------------------------------------- + +subprojects { + group = "de.unibonn.simpleml" + version = "1.0.0-SNAPSHOT" + + repositories { + mavenCentral() + } +} + +// Tasks --------------------------------------------------------------------------------------------------------------- + +tasks.register("generateXtextLanguage") { + val rootPath = this.project.rootDir.path + + group = "Build" + description = "Generate language files (e.g. EMF classes)" + + outputs.cacheIf { true } + + inputs.files( + "$rootPath/de.unibonn.simpleml/model/SimpleML.ecore", + "$rootPath/de.unibonn.simpleml/model/SimpleML.genmodel", + "$rootPath/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/SimpleML.xtext" + ) + outputs.dirs( + "$rootPath/de.unibonn.simpleml/META-INF", + "$rootPath/de.unibonn.simpleml/emf-gen", + "$rootPath/de.unibonn.simpleml/src-gen", + "$rootPath/de.unibonn.simpleml.ide/src-gen", + "$rootPath/de.unibonn.simpleml.tests" + ) + outputs.files( + "$rootPath/de.unibonn.simpleml/build.properties", + "$rootPath/de.unibonn.simpleml/plugin.properties", + "$rootPath/de.unibonn.simpleml/plugin.xml" + ) + + doFirst { + workflow { + standaloneSetup { + setPlatformUri(rootPath) + setScanClassPath(true) + + projectMapping( + projectName = "de.unibonn.simpleml", + path = "$rootPath/de.unibonn.simpleml" + ) + + projectMapping( + projectName = "de.unibonn.simpleml.ide", + path = "$rootPath/de.unibonn.simpleml.ide" + ) + } + + directoryCleaner("$rootPath/de.unibonn.simpleml/emf-gen") + + ecoreGenerator( + genModel = "platform:/resource/de.unibonn.simpleml/model/SimpleML.genmodel", + srcPaths = listOf("platform:/resource/de.unibonn.simpleml/src/main/kotlin") + ) + + xtextGenerator { + configuration { + project { + baseName = "de.unibonn.simpleml" + this.rootPath = rootPath + + runtime = RuntimeProjectConfig().apply { + setSrc("$rootPath/de.unibonn.simpleml/src/main/kotlin") + } + + genericIde = BundleProjectConfig().apply { + isEnabled = true + setSrc("$rootPath/de.unibonn.simpleml.ide/src/main/kotlin") + } + + runtimeTest = BundleProjectConfig().apply { + isEnabled = false + } + + isCreateEclipseMetaData = false + } + + code { + encoding = "UTF-8" + lineDelimiter = "\n" + fileHeader = "/*\n * generated by Xtext \${version}\n */" + isPreferXtendStubs = true + } + } + + standardLanguage { + setName("de.unibonn.simpleml.SimpleML") + setFileExtensions("smlflow,smlstub,smltest") + addReferencedResource("platform:/resource/de.unibonn.simpleml/model/SimpleML.genmodel") + + setFormatter( + Formatter2Fragment2().apply { + isGenerateStub = true + } + ) + + setGenerator( + GeneratorFragment2().apply { + isGenerateXtendMain = false + } + ) + + setSerializer( + SerializerFragment2().apply { + isGenerateStub = true + } + ) + + setValidator( + ValidatorFragment2().apply { + isGenerateDeprecationValidation = true + } + ) + + setJunitSupport( + JUnitFragment().apply { + setJunitVersion("5") + isGenerateStub = false + } + ) + } + } + }.execute() + } + + doLast { + delete( + fileTree("$rootPath/de.unibonn.simpleml/src") { + include("**/*.xtend") + } + ) + delete( + fileTree("$rootPath/de.unibonn.simpleml.ide/src") { + include("**/*.xtend") + } + ) + delete(file("$rootPath/de.unibonn.simpleml.tests")) + } +} + +tasks { + koverMergedHtmlReport { + excludes = koverExcludes + } + + koverMergedXmlReport { + excludes = koverExcludes + } + + koverMergedVerify { + excludes = koverExcludes + rule { + name = "Minimal line coverage rate in percents" + bound { + minValue = 80 + } + } + } +} diff --git a/DSL/buildSrc/build.gradle.kts b/DSL/buildSrc/build.gradle.kts new file mode 100644 index 000000000..01d1a2d93 --- /dev/null +++ b/DSL/buildSrc/build.gradle.kts @@ -0,0 +1,37 @@ +val javaVersion by extra(11) +val xtextVersion by extra("2.26.0.M2") + +// Plugins ------------------------------------------------------------------------------------------------------------- + +plugins { + kotlin("jvm") version "1.6.0" + idea +} + +repositories { + mavenCentral() +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(javaVersion)) + } +} + +// Dependencies -------------------------------------------------------------------------------------------------------- + +dependencies { + api(platform("org.eclipse.xtext:xtext-dev-bom:$xtextVersion")) + implementation("org.eclipse.xtext:org.eclipse.xtext:$xtextVersion") + + implementation("org.eclipse.emf:org.eclipse.emf.mwe2.launch:2.12.2.M1") + implementation("org.eclipse.xtext:org.eclipse.xtext.common.types:$xtextVersion") + implementation("org.eclipse.xtext:org.eclipse.xtext.xtext.generator:$xtextVersion") + implementation("org.eclipse.xtext:xtext-antlr-generator:2.1.1") +} + +// Tasks --------------------------------------------------------------------------------------------------------------- + +tasks.withType().configureEach { + kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn" +} diff --git a/DSL/buildSrc/src/main/kotlin/de/unibonn/simpleml/xtextConfiguration/XtextConfigurationDsl.kt b/DSL/buildSrc/src/main/kotlin/de/unibonn/simpleml/xtextConfiguration/XtextConfigurationDsl.kt new file mode 100644 index 000000000..adc31a233 --- /dev/null +++ b/DSL/buildSrc/src/main/kotlin/de/unibonn/simpleml/xtextConfiguration/XtextConfigurationDsl.kt @@ -0,0 +1,74 @@ +@file:Suppress("unused") + +package de.unibonn.simpleml.xtextConfiguration + +import org.eclipse.emf.mwe.utils.DirectoryCleaner +import org.eclipse.emf.mwe.utils.ProjectMapping +import org.eclipse.emf.mwe.utils.StandaloneSetup +import org.eclipse.emf.mwe2.ecore.EcoreGenerator +import org.eclipse.emf.mwe2.runtime.workflow.Workflow +import org.eclipse.emf.mwe2.runtime.workflow.WorkflowContextImpl +import org.eclipse.xtext.xtext.generator.CodeConfig +import org.eclipse.xtext.xtext.generator.DefaultGeneratorModule +import org.eclipse.xtext.xtext.generator.StandardLanguage +import org.eclipse.xtext.xtext.generator.XtextGenerator +import org.eclipse.xtext.xtext.generator.model.project.StandardProjectConfig + +fun workflow(init: Workflow.() -> Unit): Workflow { + return Workflow().apply(init) +} + +fun Workflow.standaloneSetup(init: StandaloneSetup.() -> Unit) { + addBean(StandaloneSetup().apply(init)) +} + +fun StandaloneSetup.projectMapping(projectName: String, path: String) { + addProjectMapping( + ProjectMapping().apply { + this.projectName = projectName + this.path = path + } + ) +} + +fun Workflow.directoryCleaner(directory: String) { + addComponent( + DirectoryCleaner().apply { + setDirectory(directory) + } + ) +} + +fun Workflow.ecoreGenerator(genModel: String, srcPaths: List, init: EcoreGenerator.() -> Unit = {}) { + addComponent( + EcoreGenerator().apply { + setGenModel(genModel) + srcPaths.forEach { addSrcPath(it) } + init() + } + ) +} + +fun Workflow.xtextGenerator(init: XtextGenerator.() -> Unit) { + addComponent(XtextGenerator().apply(init)) +} + +fun XtextGenerator.configuration(init: DefaultGeneratorModule.() -> Unit) { + configuration = DefaultGeneratorModule().apply(init) +} + +fun DefaultGeneratorModule.project(init: StandardProjectConfig.() -> Unit) { + project = StandardProjectConfig().apply(init) +} + +fun DefaultGeneratorModule.code(init: CodeConfig.() -> Unit) { + code = CodeConfig().apply(init) +} + +fun XtextGenerator.standardLanguage(init: StandardLanguage.() -> Unit) { + addLanguage(StandardLanguage().apply(init)) +} + +fun Workflow.execute() { + run(WorkflowContextImpl()) +} diff --git a/DSL/de.unibonn.simpleml.ide/.gitignore b/DSL/de.unibonn.simpleml.ide/.gitignore new file mode 100644 index 000000000..c441ecaf1 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/.gitignore @@ -0,0 +1,3 @@ +/src-gen +/test-data +*.xtend diff --git a/DSL/de.unibonn.simpleml.ide/build.gradle.kts b/DSL/de.unibonn.simpleml.ide/build.gradle.kts new file mode 100644 index 000000000..973e51a78 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/build.gradle.kts @@ -0,0 +1,83 @@ +val javaVersion: Int by rootProject.extra +val xtextVersion: String by rootProject.extra + +// Plugins ------------------------------------------------------------------------------------------------------------- + +plugins { + java + kotlin("jvm") + application +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(javaVersion)) + } +} + +application { + mainClass.set("de.unibonn.simpleml.ide.ServerLauncher2") +} + +// Dependencies -------------------------------------------------------------------------------------------------------- + +dependencies { + implementation(project(":de.unibonn.simpleml")) + implementation("org.eclipse.xtext:org.eclipse.xtext.ide:$xtextVersion") + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + testImplementation(testFixtures(project(":de.unibonn.simpleml"))) + testImplementation("org.junit.jupiter:junit-jupiter-api") + testImplementation("org.eclipse.xtext:org.eclipse.xtext.testing:$xtextVersion") + testImplementation("org.eclipse.xtext:org.eclipse.xtext.xbase.testing:$xtextVersion") + testImplementation("io.kotest:kotest-assertions-core-jvm:5.2.3") +} + +// Source sets --------------------------------------------------------------------------------------------------------- + +sourceSets { + main { + java.srcDirs("src-gen") + resources.srcDirs("src-gen") + resources.include("**/*.ISetup") + } +} + +// Tasks --------------------------------------------------------------------------------------------------------------- + +val koverExcludes = listOf( + "de.unibonn.simpleml.ide.contentassist.antlr.*" +) + +tasks { + processResources { + val generateXtextLanguage = rootProject.tasks.named("generateXtextLanguage") + dependsOn(generateXtextLanguage) + } + + test { + useJUnitPlatform() + + extensions.configure(kotlinx.kover.api.KoverTaskExtension::class) { + excludes = koverExcludes + } + } + + koverHtmlReport { + excludes = koverExcludes + } + + koverXmlReport { + excludes = koverExcludes + } + + koverVerify { + excludes = koverExcludes + rule { + name = "Minimal line coverage rate in percents" + bound { + minValue = 33 + } + } + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/CustomServerModule.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/CustomServerModule.kt new file mode 100644 index 000000000..a81d3df38 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/CustomServerModule.kt @@ -0,0 +1,12 @@ +package de.unibonn.simpleml.ide + +import de.unibonn.simpleml.ide.server.project.SimpleMLProjectManager +import org.eclipse.xtext.ide.server.ProjectManager +import org.eclipse.xtext.ide.server.ServerModule + +class CustomServerModule : ServerModule() { + override fun configure() { + super.configure() + bind(ProjectManager::class.java).to(SimpleMLProjectManager::class.java) + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/ServerLauncher2.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/ServerLauncher2.kt new file mode 100644 index 000000000..28b4b3bf8 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/ServerLauncher2.kt @@ -0,0 +1,13 @@ +package de.unibonn.simpleml.ide + +import org.eclipse.xtext.ide.server.ServerLauncher + +class ServerLauncher2 { + companion object { + + @JvmStatic + fun main(args: Array) { + ServerLauncher.launch(ServerLauncher2::class.java.name, args, CustomServerModule()) + } + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/SimpleMLIdeModule.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/SimpleMLIdeModule.kt new file mode 100644 index 000000000..d223cdd4d --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/SimpleMLIdeModule.kt @@ -0,0 +1,57 @@ +package de.unibonn.simpleml.ide + +import de.unibonn.simpleml.ide.editor.contentassist.SimpleMLIdeContentProposalProvider +import de.unibonn.simpleml.ide.server.codelens.SimpleMLCodeLensProvider +import de.unibonn.simpleml.ide.server.commands.SimpleMLExecutableCommandService +import de.unibonn.simpleml.ide.server.hover.SimpleMLHoverService +import de.unibonn.simpleml.ide.server.symbol.SimpleMLDocumentSymbolDeprecationInfoProvider +import de.unibonn.simpleml.ide.server.symbol.SimpleMLDocumentSymbolDetailsProvider +import de.unibonn.simpleml.ide.server.symbol.SimpleMLDocumentSymbolKindProvider +import de.unibonn.simpleml.ide.server.symbol.SimpleMLDocumentSymbolNameProvider +import org.eclipse.xtext.ide.editor.contentassist.IdeContentProposalProvider +import org.eclipse.xtext.ide.server.codelens.ICodeLensResolver +import org.eclipse.xtext.ide.server.codelens.ICodeLensService +import org.eclipse.xtext.ide.server.commands.IExecutableCommandService +import org.eclipse.xtext.ide.server.hover.HoverService +import org.eclipse.xtext.ide.server.symbol.DocumentSymbolMapper + +/** + * Use this class to register IDE components. + */ +class SimpleMLIdeModule : AbstractSimpleMLIdeModule() { + fun bindICodeLensResolver(): Class { + return SimpleMLCodeLensProvider::class.java + } + + fun bindICodeLensService(): Class { + return SimpleMLCodeLensProvider::class.java + } + + fun bindIExecutableCommandService(): Class { + return SimpleMLExecutableCommandService::class.java + } + + fun bindDocumentSymbolDeprecationInfoProvider(): Class { + return SimpleMLDocumentSymbolDeprecationInfoProvider::class.java + } + + fun bindDocumentSymbolDetailsProvider(): Class { + return SimpleMLDocumentSymbolDetailsProvider::class.java + } + + fun bindDocumentSymbolKindProvider(): Class { + return SimpleMLDocumentSymbolKindProvider::class.java + } + + fun bindDocumentSymbolNameProvider(): Class { + return SimpleMLDocumentSymbolNameProvider::class.java + } + + fun bindIdeContentProposalProvider(): Class { + return SimpleMLIdeContentProposalProvider::class.java + } + + fun bindHoverService(): Class { + return SimpleMLHoverService::class.java + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/SimpleMLIdeSetup.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/SimpleMLIdeSetup.kt new file mode 100644 index 000000000..8b270ff4e --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/SimpleMLIdeSetup.kt @@ -0,0 +1,16 @@ +package de.unibonn.simpleml.ide + +import com.google.inject.Guice +import com.google.inject.Injector +import de.unibonn.simpleml.SimpleMLRuntimeModule +import de.unibonn.simpleml.SimpleMLStandaloneSetup +import org.eclipse.xtext.util.Modules2 + +/** + * Initialization support for running Xtext languages as language servers. + */ +class SimpleMLIdeSetup : SimpleMLStandaloneSetup() { + override fun createInjector(): Injector? { + return Guice.createInjector(Modules2.mixin(SimpleMLRuntimeModule(), SimpleMLIdeModule())) + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/editor/contentassist/Proposals.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/editor/contentassist/Proposals.kt new file mode 100644 index 000000000..d51b29589 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/editor/contentassist/Proposals.kt @@ -0,0 +1,162 @@ +package de.unibonn.simpleml.ide.editor.contentassist + +import de.unibonn.simpleml.emf.classMembersOrEmpty +import de.unibonn.simpleml.emf.containingClassOrNull +import de.unibonn.simpleml.emf.isClassMember +import de.unibonn.simpleml.emf.isGlobal +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.variantsOrEmpty +import de.unibonn.simpleml.scoping.allGlobalDeclarations +import de.unibonn.simpleml.simpleML.SmlAbstractCallable +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.staticAnalysis.typing.Type +import de.unibonn.simpleml.staticAnalysis.typing.hasPrimitiveType +import de.unibonn.simpleml.staticAnalysis.typing.isSubstitutableFor +import de.unibonn.simpleml.staticAnalysis.typing.type +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtext.EcoreUtil2 + +/** + * Suggests callables that only require primitive values as arguments when called. + * + * @param context + * Any EObject in the current file, e.g. the [SmlCompilationUnit]. This is used to determine which declarations are + * visible from here. + * + * @return + * A map of URIs to EObjects (SmlClass, SmlFunction, or SmlWorkflowStep). + */ +fun listCallablesWithOnlyPrimitiveParameters(context: EObject): Map { + return context.allCallables() + .filterValues { obj -> + when (obj) { + is SmlClass -> { + obj.parameterList != null && obj.parametersOrEmpty().all { + it.hasPrimitiveType() + } + } + is SmlFunction -> { + obj.isGlobal() && obj.parametersOrEmpty().all { + it.hasPrimitiveType() + } + } + is SmlStep -> { + obj.parametersOrEmpty().all { + it.hasPrimitiveType() + } + } + else -> false + } + } +} + +/** + * Suggests callables that can accept all the given [declarations] as parameters. These callables can still have + * additional parameters that are not yet assigned. + * + * @param context + * Any EObject in the current file, e.g. the [SmlCompilationUnit]. This is used to determine which declarations are + * visible from here. + * + * @param declarations + * The declarations that correspond to the result port the user clicked on or null if a new initial call should + * be added. They should be either SmlPlaceholders or SmlResults. If multiple declarations are specified, a callable + * must have one matching input port for each. + * + * @return + * A map of URIs to EObjects (SmlClass, SmlFunction, or SmlWorkflowStep). + */ +fun listCallablesWithMatchingParameters( + context: EObject, + declarations: List +): Map { + val requiredTypes = declarations.map { it.type() } + + return context.allCallables() + .filterValues { obj -> + val availableTypes = when (obj) { + is SmlClass -> { + if (obj.parameterList == null) { + return@filterValues false + } + + obj.parametersOrEmpty().map { it.type() } + } + is SmlEnumVariant -> { + obj.parametersOrEmpty().map { it.type() } + } + is SmlFunction -> { + val parameterTypes = obj.parametersOrEmpty().map { it.type() } + if (obj.isClassMember()) { + parameterTypes + obj.containingClassOrNull()!!.type() + } else { + parameterTypes + } + } + is SmlStep -> { + obj.parametersOrEmpty().map { it.type() } + } + else -> return@filterValues false + } + + typesMatch(requiredTypes, availableTypes) + } +} + +private fun typesMatch(requiredTypes: List, availableTypes: List): Boolean { + if (requiredTypes.isEmpty()) { + return true + } + + val requiredType = requiredTypes.first() + + val matchingAvailableTypes = availableTypes.filter { requiredType.isSubstitutableFor(it) } + if (matchingAvailableTypes.isEmpty()) { + return false + } + + return matchingAvailableTypes.any { + typesMatch(requiredTypes.drop(1), availableTypes - it) + } +} + +/** + * Lists all [SmlAbstractCallable]s that can be called from the given context. + */ +private fun EObject.allCallables(): Map { + return allGlobalDeclarations() + .flatMap { + when (val obj = it.eObjectOrProxy) { + is SmlClass -> obj.allNestedCallables().toList() + is SmlEnum -> obj.variantsOrEmpty() + is SmlFunction -> listOf(obj) + is SmlStep -> listOf(obj) + else -> emptyList() + } + } + .associateBy { EcoreUtil2.getURI(it) } +} + +/** + * Lists all [SmlAbstractCallable]s nested in an [SmlClass]. + */ +private fun SmlClass.allNestedCallables(): Sequence = sequence { + if (parameterList != null) { + yield(this@allNestedCallables) + } + + classMembersOrEmpty().forEach { + when (it) { + is SmlClass -> yieldAll(it.allNestedCallables()) + is SmlEnum -> yieldAll(it.variantsOrEmpty()) + is SmlFunction -> yield(it) + } + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/editor/contentassist/SimpleMLIdeContentProposalProvider.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/editor/contentassist/SimpleMLIdeContentProposalProvider.kt new file mode 100644 index 000000000..de7f57d40 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/editor/contentassist/SimpleMLIdeContentProposalProvider.kt @@ -0,0 +1,91 @@ +package de.unibonn.simpleml.ide.editor.contentassist + +import com.google.inject.Inject +import de.unibonn.simpleml.services.SimpleMLGrammarAccess +import de.unibonn.simpleml.simpleML.SmlArgumentList +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.staticAnalysis.linking.parametersOrNull +import org.eclipse.xtext.Assignment +import org.eclipse.xtext.Keyword +import org.eclipse.xtext.RuleCall +import org.eclipse.xtext.ide.editor.contentassist.ContentAssistContext +import org.eclipse.xtext.ide.editor.contentassist.IIdeContentProposalAcceptor +import org.eclipse.xtext.ide.editor.contentassist.IdeContentProposalProvider +import org.eclipse.xtext.scoping.IScopeProvider + +class SimpleMLIdeContentProposalProvider @Inject constructor( + private val grammarAccess: SimpleMLGrammarAccess, + private val scopeProvider2: IScopeProvider +) : IdeContentProposalProvider() { + + private val crossReferencePriority = 500 + private val snippetPriority = 450 + private val defaultPriority = 400 + + override fun _createProposals( + ruleCall: RuleCall, + context: ContentAssistContext, + acceptor: IIdeContentProposalAcceptor + ) { + val rule = ruleCall.rule + val model = context.currentModel + + println("Auto-completion rule: $rule") + println("Auto-completion model: $model") + + when { + model is SmlCompilationUnit -> { + completeGlobalSnippets(context, acceptor) + } + model is SmlArgumentList && rule == grammarAccess.smlCallArgumentRule -> { + completeSmlCallArguments(model, context, acceptor) + } + } + } + + override fun _createProposals( + assignment: Assignment, + context: ContentAssistContext, + acceptor: IIdeContentProposalAcceptor + ) { + println("Auto-completion assignment: $assignment") + + // Intentionally left blank so assignments don't get suggested + } + + override fun _createProposals( + keyword: Keyword, + context: ContentAssistContext, + acceptor: IIdeContentProposalAcceptor + ) { + println("Auto-completion keyword: $keyword") + + // Intentionally left blank so keywords don't get suggested + } + + private fun completeGlobalSnippets( + context: ContentAssistContext, + acceptor: IIdeContentProposalAcceptor + ) { + val workflow = """ + |workflow ${'$'}{1:name} { + | ${'$'}{2:body} + |} + """.trimMargin() + + acceptor.accept(proposalCreator.createSnippet(workflow, "workflow", context), snippetPriority) + } + + private fun completeSmlCallArguments( + model: SmlArgumentList, + context: ContentAssistContext, + acceptor: IIdeContentProposalAcceptor + ) { + val usedParameters = model.arguments.map { it.parameter }.toSet() + model.parametersOrNull() + ?.filter { it !in usedParameters && !it.isVariadic } + ?.forEach { + acceptor.accept(proposalCreator.createProposal("${it.name} = ", context), crossReferencePriority) + } + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/codelens/SimpleMLCodeLensProvider.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/codelens/SimpleMLCodeLensProvider.kt new file mode 100644 index 000000000..9f561b2bb --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/codelens/SimpleMLCodeLensProvider.kt @@ -0,0 +1,39 @@ +package de.unibonn.simpleml.ide.server.codelens + +import com.google.inject.Inject +import org.eclipse.lsp4j.CodeLens +import org.eclipse.lsp4j.CodeLensParams +import org.eclipse.xtext.ide.server.Document +import org.eclipse.xtext.ide.server.codelens.ICodeLensResolver +import org.eclipse.xtext.ide.server.codelens.ICodeLensService +import org.eclipse.xtext.ide.server.symbol.DocumentSymbolMapper +import org.eclipse.xtext.resource.XtextResource +import org.eclipse.xtext.service.OperationCanceledManager +import org.eclipse.xtext.util.CancelIndicator + +class SimpleMLCodeLensProvider : ICodeLensResolver, ICodeLensService { + + @Inject + private lateinit var operationCanceledManager: OperationCanceledManager + + @Inject + private lateinit var rangeProvider: DocumentSymbolMapper.DocumentSymbolRangeProvider + + override fun computeCodeLenses( + document: Document, + resource: XtextResource, + params: CodeLensParams, + indicator: CancelIndicator + ): List { + return emptyList() + } + + override fun resolveCodeLens( + document: Document, + resource: XtextResource, + codeLens: CodeLens, + indicator: CancelIndicator + ): CodeLens { + return codeLens + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/commands/CommandId.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/commands/CommandId.kt new file mode 100644 index 000000000..ffc000ea8 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/commands/CommandId.kt @@ -0,0 +1,9 @@ +package de.unibonn.simpleml.ide.server.commands + +enum class CommandId { + RemoveOnceOtherCommandsAreAdded; + + override fun toString(): String { + return "simple-ml." + this.name + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/commands/SimpleMLExecutableCommandService.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/commands/SimpleMLExecutableCommandService.kt new file mode 100644 index 000000000..0ca27d020 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/commands/SimpleMLExecutableCommandService.kt @@ -0,0 +1,32 @@ +package de.unibonn.simpleml.ide.server.commands + +import com.google.inject.Inject +import org.eclipse.lsp4j.ExecuteCommandParams +import org.eclipse.xtext.ide.server.ILanguageServerAccess +import org.eclipse.xtext.ide.server.commands.IExecutableCommandService +import org.eclipse.xtext.service.OperationCanceledManager +import org.eclipse.xtext.util.CancelIndicator + +class SimpleMLExecutableCommandService : IExecutableCommandService { + + @Inject + private lateinit var operationCanceledManager: OperationCanceledManager + + override fun initialize(): List { + return emptyList() + } + + override fun execute( + params: ExecuteCommandParams, + access: ILanguageServerAccess, + cancelIndicator: CancelIndicator + ): Any { + + return when (params.command) { + CommandId.RemoveOnceOtherCommandsAreAdded.toString() -> {} + else -> { + throw IllegalArgumentException("Unknown command '${params.command}'.") + } + } + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/hover/SimpleMLHoverService.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/hover/SimpleMLHoverService.kt new file mode 100644 index 000000000..e05b7081c --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/hover/SimpleMLHoverService.kt @@ -0,0 +1,31 @@ +package de.unibonn.simpleml.ide.server.hover + +import com.google.inject.Inject +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtext.documentation.IEObjectDocumentationProvider +import org.eclipse.xtext.ide.labels.INameLabelProvider +import org.eclipse.xtext.ide.server.hover.HoverService + +class SimpleMLHoverService @Inject constructor( + private val documentationProvider: IEObjectDocumentationProvider, + private val nameLabelProvider: INameLabelProvider +) : HoverService() { + + override fun getContents(obj: EObject): String { + val documentation = documentationProvider.getDocumentation(obj) + return if (documentation == null) { + getFirstLine(obj) + } else { + "${getFirstLine(obj)} \n$documentation" + } + } + + private fun getFirstLine(obj: EObject): String { + val label = nameLabelProvider.getNameLabel(obj) + return if (label == null) { + obj.eClass().name + } else { + "${obj.eClass().name} **$label**" + } + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/project/SimpleMLProjectManager.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/project/SimpleMLProjectManager.kt new file mode 100644 index 000000000..3e5af458c --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/project/SimpleMLProjectManager.kt @@ -0,0 +1,19 @@ +package de.unibonn.simpleml.ide.server.project + +import de.unibonn.simpleml.stdlibAccess.listStdlibFiles +import org.eclipse.xtext.build.IncrementalBuilder +import org.eclipse.xtext.ide.server.ProjectManager +import org.eclipse.xtext.util.CancelIndicator + +class SimpleMLProjectManager : ProjectManager() { + + override fun doInitialBuild(cancelIndicator: CancelIndicator): IncrementalBuilder.Result { + + // Load Stdlib first to prevent errors when it is edited in VS Code (`simple.lang` would be overridden) + val uris = listStdlibFiles().map { it.second }.toMutableList() + uris += projectConfig.sourceFolders + .flatMap { srcFolder -> srcFolder.getAllResources(fileSystemScanner) } + + return doBuild(uris, emptyList(), emptyList(), cancelIndicator) + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/symbol/SimpleMLDocumentSymbolDeprecationInfoProvider.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/symbol/SimpleMLDocumentSymbolDeprecationInfoProvider.kt new file mode 100644 index 000000000..b48a2ac15 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/symbol/SimpleMLDocumentSymbolDeprecationInfoProvider.kt @@ -0,0 +1,22 @@ +package de.unibonn.simpleml.ide.server.symbol + +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.stdlibAccess.isDeprecated +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtext.ide.server.symbol.DocumentSymbolMapper +import org.eclipse.xtext.resource.IEObjectDescription + +class SimpleMLDocumentSymbolDeprecationInfoProvider : DocumentSymbolMapper.DocumentSymbolDeprecationInfoProvider() { + + override fun isDeprecated(obj: EObject): Boolean { + if (obj !is SmlAbstractDeclaration) { + return false + } + + return obj.isDeprecated() + } + + override fun isDeprecated(description: IEObjectDescription): Boolean { + return isDeprecated(description.eObjectOrProxy) + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/symbol/SimpleMLDocumentSymbolDetailsProvider.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/symbol/SimpleMLDocumentSymbolDetailsProvider.kt new file mode 100644 index 000000000..83a518c2f --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/symbol/SimpleMLDocumentSymbolDetailsProvider.kt @@ -0,0 +1,10 @@ +package de.unibonn.simpleml.ide.server.symbol + +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtext.ide.server.symbol.DocumentSymbolMapper + +class SimpleMLDocumentSymbolDetailsProvider : DocumentSymbolMapper.DocumentSymbolDetailsProvider() { + override fun getDetails(obj: EObject?): String { + return "" + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/symbol/SimpleMLDocumentSymbolKindProvider.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/symbol/SimpleMLDocumentSymbolKindProvider.kt new file mode 100644 index 000000000..de83c1d4e --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/symbol/SimpleMLDocumentSymbolKindProvider.kt @@ -0,0 +1,39 @@ +package de.unibonn.simpleml.ide.server.symbol + +import de.unibonn.simpleml.emf.isClassMember +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlFunction +import org.eclipse.emf.ecore.EClass +import org.eclipse.emf.ecore.EObject +import org.eclipse.lsp4j.SymbolKind +import org.eclipse.xtext.ide.server.symbol.DocumentSymbolMapper +import org.eclipse.xtext.resource.IEObjectDescription + +class SimpleMLDocumentSymbolKindProvider : DocumentSymbolMapper.DocumentSymbolKindProvider() { + override fun getSymbolKind(obj: EObject?): SymbolKind? { + if (obj is SmlFunction && obj.isClassMember()) { + return SymbolKind.Method + } + + return obj?.let { getSymbolKind(it.eClass()) } + } + + override fun getSymbolKind(description: IEObjectDescription?): SymbolKind? { + return getSymbolKind(description?.eObjectOrProxy) + } + + override fun getSymbolKind(clazz: EClass): SymbolKind? { + return when (clazz) { + Literals.SML_ANNOTATION -> SymbolKind.Interface // Not ideal but matches @interface in Java + Literals.SML_ATTRIBUTE -> SymbolKind.Field + Literals.SML_CLASS -> SymbolKind.Class + Literals.SML_COMPILATION_UNIT -> SymbolKind.Package + Literals.SML_ENUM -> SymbolKind.Enum + Literals.SML_ENUM_VARIANT -> SymbolKind.EnumMember + Literals.SML_FUNCTION -> SymbolKind.Function + Literals.SML_STEP -> SymbolKind.Function + Literals.SML_WORKFLOW -> SymbolKind.Function + else -> null + } + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/symbol/SimpleMLDocumentSymbolNameProvider.kt b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/symbol/SimpleMLDocumentSymbolNameProvider.kt new file mode 100644 index 000000000..6423a5415 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/main/kotlin/de/unibonn/simpleml/ide/server/symbol/SimpleMLDocumentSymbolNameProvider.kt @@ -0,0 +1,16 @@ +package de.unibonn.simpleml.ide.server.symbol + +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtext.ide.server.symbol.DocumentSymbolMapper +import org.eclipse.xtext.resource.IEObjectDescription + +class SimpleMLDocumentSymbolNameProvider : DocumentSymbolMapper.DocumentSymbolNameProvider() { + override fun getName(obj: EObject): String? { + return (obj as? SmlAbstractDeclaration)?.name + } + + override fun getName(description: IEObjectDescription): String? { + return getName(description.eObjectOrProxy) + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/test/kotlin/de/unibonn/simpleml/ide/AbstractSimpleMLLanguageServerTest.kt b/DSL/de.unibonn.simpleml.ide/src/test/kotlin/de/unibonn/simpleml/ide/AbstractSimpleMLLanguageServerTest.kt new file mode 100644 index 000000000..4b2966b3b --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/kotlin/de/unibonn/simpleml/ide/AbstractSimpleMLLanguageServerTest.kt @@ -0,0 +1,21 @@ +package de.unibonn.simpleml.ide + +import org.eclipse.lsp4j.InitializeParams +import org.eclipse.lsp4j.InitializeResult +import org.eclipse.xtext.testing.AbstractLanguageServerTest +import org.eclipse.xtext.xbase.lib.Procedures.Procedure1 +import java.lang.IllegalStateException + +abstract class AbstractSimpleMLLanguageServerTest : AbstractLanguageServerTest("smltest") { + + /** + * This override is necessary since `LanguageServerImpl` throws if it is initialized twice. + */ + override fun initialize(initializer: Procedure1?): InitializeResult? { + return try { + this.initialize(initializer, true) + } catch (e: IllegalStateException) { + null + } + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/test/kotlin/de/unibonn/simpleml/ide/editor/contentassist/ProposalsTest.kt b/DSL/de.unibonn.simpleml.ide/src/test/kotlin/de/unibonn/simpleml/ide/editor/contentassist/ProposalsTest.kt new file mode 100644 index 000000000..e4014a215 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/kotlin/de/unibonn/simpleml/ide/editor/contentassist/ProposalsTest.kt @@ -0,0 +1,181 @@ +package de.unibonn.simpleml.ide.editor.contentassist + +import com.google.inject.Inject +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.assertions.findUniqueDeclarationOrFail +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.maps.shouldContainValue +import io.kotest.matchers.maps.shouldContainValues +import io.kotest.matchers.maps.shouldNotContainValue +import io.kotest.matchers.nulls.shouldNotBeNull +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class ProposalsTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + private val testProgram = """ + |package test + | + |step primitive_empty() {} + |step primitive_boolean(b: Boolean) {} + |step primitive_float(f: Float) {} + |step primitive_int(i: Int) {} + |step primitive_string(s: String) {} + | + |class A() + |class B() + |class C() { + | fun someMethod() + |} + |class D() sub C + | + |step matching_a(a: A) {} + |step matching_b(b: B) {} + |step matching_multiple_c(c1: C, c2: C) {} + |step not_matching_multiple_c(c: C) {} + |step matching_multiple_c_d(c: C, d: D) {} + |step matching_multiple_d_c(d: D, c: C) {} + | + |step test_callee() -> (test_result_a: A, test_result_c: C) { + | val test_placeholder_a = A(); + | val test_placeholder_d = D(); + |} + """.trimMargin() + + @Nested + inner class ListCallablesWithOnlyPrimitiveParameters { + + @Test + fun `should contain steps with primitive parameters`() { + val context = parseHelper.parseProgramText(testProgram) + context.shouldNotBeNull() + + val steps = context.members + .asSequence() + .filterIsInstance() + .filter { it.name.startsWith("primitive") } + .toList() + steps.shouldHaveSize(5) + + val descriptions = listCallablesWithOnlyPrimitiveParameters(context) + descriptions.shouldContainValues(*steps.toTypedArray()) + } + } + + @Nested + inner class ListCallablesWithMatchingParameters { + + @Test + fun `should contain only steps with matching parameters when a placeholder is passed`() { + val context = parseHelper.parseProgramText(testProgram) + context.shouldNotBeNull() + + val placeholder = context.findUniqueDeclarationOrFail("test_placeholder_a") + val workflowStepA = context.findUniqueDeclarationOrFail("matching_a") + val workflowStepB = context.findUniqueDeclarationOrFail("matching_b") + + val descriptions = listCallablesWithMatchingParameters(context, listOf(placeholder)) + descriptions.shouldContainValue(workflowStepA) + descriptions.shouldNotContainValue(workflowStepB) + } + + @Test + fun `should contain only steps with matching parameters when a result is passed`() { + val context = parseHelper.parseProgramText(testProgram) + context.shouldNotBeNull() + + val result = context.findUniqueDeclarationOrFail("test_result_a") + val workflowStepA = context.findUniqueDeclarationOrFail("matching_a") + val workflowStepB = context.findUniqueDeclarationOrFail("matching_b") + + val descriptions = listCallablesWithMatchingParameters(context, listOf(result)) + descriptions.shouldContainValue(workflowStepA) + descriptions.shouldNotContainValue(workflowStepB) + } + + @Test + fun `should contain only steps with matching parameters when multiple declarations are passed (1)`() { + val context = parseHelper.parseProgramText(testProgram) + context.shouldNotBeNull() + + val result = context.findUniqueDeclarationOrFail("test_result_c") + val matchingWorkflow = context.findUniqueDeclarationOrFail("matching_multiple_c") + val nonMatchingWorkflow = context.findUniqueDeclarationOrFail("not_matching_multiple_c") + + val descriptions = listCallablesWithMatchingParameters(context, listOf(result, result)) + descriptions.shouldContainValue(matchingWorkflow) + descriptions.shouldNotContainValue(nonMatchingWorkflow) + } + + @Test + fun `should contain only steps with matching parameters when multiple declarations are passed (2)`() { + val context = parseHelper.parseProgramText(testProgram) + context.shouldNotBeNull() + + val result = context.findUniqueDeclarationOrFail("test_result_c") + val placeholder = context.findUniqueDeclarationOrFail("test_placeholder_d") + val matchingWorkflow1 = context.findUniqueDeclarationOrFail("matching_multiple_c_d") + val matchingWorkflow2 = context.findUniqueDeclarationOrFail("matching_multiple_d_c") + + // Inverse order of placeholder and result compared to (3) + val descriptions = listCallablesWithMatchingParameters(context, listOf(result, placeholder)) + descriptions.shouldContainValue(matchingWorkflow1) + descriptions.shouldContainValue(matchingWorkflow2) + } + + @Test + fun `should contain only steps with matching parameters when multiple declarations are passed (3)`() { + val context = parseHelper.parseProgramText(testProgram) + context.shouldNotBeNull() + + val result = context.findUniqueDeclarationOrFail("test_result_c") + val placeholder = context.findUniqueDeclarationOrFail("test_placeholder_d") + val matchingWorkflow1 = context.findUniqueDeclarationOrFail("matching_multiple_c_d") + val matchingWorkflow2 = context.findUniqueDeclarationOrFail("matching_multiple_d_c") + + // Inverse order of placeholder and result compared to (2) + val descriptions = listCallablesWithMatchingParameters(context, listOf(placeholder, result)) + descriptions.shouldContainValue(matchingWorkflow1) + descriptions.shouldContainValue(matchingWorkflow2) + } + + @Test + fun `should contain methods defined directly on class`() { + val context = parseHelper.parseProgramText(testProgram) + context.shouldNotBeNull() + + val result = context.findUniqueDeclarationOrFail("test_result_c") + val matchingMethod = context.findUniqueDeclarationOrFail("someMethod") + + // Inverse order of placeholder and result compared to (2) + val descriptions = listCallablesWithMatchingParameters(context, listOf(result)) + descriptions.shouldContainValue(matchingMethod) + } + + @Test + fun `should contain methods defined on superclass`() { + val context = parseHelper.parseProgramText(testProgram) + context.shouldNotBeNull() + + val placeholder = context.findUniqueDeclarationOrFail("test_placeholder_d") + val matchingMethod = context.findUniqueDeclarationOrFail("someMethod") + + // Inverse order of placeholder and result compared to (2) + val descriptions = listCallablesWithMatchingParameters(context, listOf(placeholder)) + descriptions.shouldContainValue(matchingMethod) + } + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/test/kotlin/de/unibonn/simpleml/ide/server/symbol/DocumentSymbolTest.kt b/DSL/de.unibonn.simpleml.ide/src/test/kotlin/de/unibonn/simpleml/ide/server/symbol/DocumentSymbolTest.kt new file mode 100644 index 000000000..ca7a8eae5 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/kotlin/de/unibonn/simpleml/ide/server/symbol/DocumentSymbolTest.kt @@ -0,0 +1,133 @@ +package de.unibonn.simpleml.ide.server.symbol + +import de.unibonn.simpleml.ide.AbstractSimpleMLLanguageServerTest +import de.unibonn.simpleml.location.LspRange +import de.unibonn.simpleml.testing.CategorizedTest +import de.unibonn.simpleml.testing.FindTestRangesResult +import de.unibonn.simpleml.testing.createDynamicTestsFromResourceFolder +import de.unibonn.simpleml.testing.findTestRanges +import de.unibonn.simpleml.testing.getResourcePath +import de.unibonn.simpleml.testing.testDisplayName +import org.eclipse.lsp4j.SymbolKind +import org.junit.jupiter.api.DynamicNode +import org.junit.jupiter.api.DynamicTest +import org.junit.jupiter.api.TestFactory +import java.nio.file.Path +import java.util.stream.Stream + +class DocumentSymbolTest : AbstractSimpleMLLanguageServerTest() { + + @TestFactory + fun `should provide correct symbols`(): Stream { + return javaClass.classLoader + .getResourcePath("symbols") + ?.createDynamicTestsFromResourceFolder(::validateTestFile, ::createTest) + ?: Stream.empty() + } + + /** + * Checks if the given program is a valid test. If there are issues a description of the issue is returned, otherwise + * this returns null. + */ + @Suppress("UNUSED_PARAMETER") + private fun validateTestFile(resourcePath: Path, filePath: Path, program: String): String? { + val symbolComments = try { + symbolComments(program) + } catch (e: IllegalArgumentException) { + return e.message + } + + // Must contain at least one comment + if (symbolComments.isEmpty()) { + return "No expected symbol is specified." + } + + // Opening and closing test markers must match + val locations = when (val locationsResult = findTestRanges(program)) { + is FindTestRangesResult.Success -> locationsResult.ranges + is FindTestRangesResult.Failure -> return locationsResult.message + } + + // Must contain the same amount of test markers and symbols + if (symbolComments.size != locations.size) { + return "Test file contains ${symbolComments.size} symbol comments but ${locations.size} ranges." + } + + // Must be able to parse the test file + // This code fails with Guice configuration errors: + // + // 1) [Guice/MissingImplementation]: No implementation for String annotated with + // @Named(value="file.extensions") was bound. + // val compilationUnit = parseHelper.parse(program) + // ?: return "Could not parse test file." + // + // // Must not have syntax errors + // val syntaxErrors = validationHelper.validate(compilationUnit).filter { it.isSyntaxError } + // if (syntaxErrors.isNotEmpty()) { + // return "File has syntax errors${syntaxErrors.stringify()}" + // } + + return null + } + + private fun createTest(resourcePath: Path, filePath: Path, program: String) = sequence { + val expectedSymbols = expectedSymbols(program) + yield( + CategorizedTest( + "symbol tests", + DynamicTest.dynamicTest(testDisplayName(resourcePath, filePath), filePath.toUri()) { + testDocumentSymbol { + it.model = program + it.expectedSymbols = expectedSymbols.joinToString("") + } + } + ) + ) + } + + private fun symbolComments(program: String): List { + return """//\s*(?\S+)\s*"(?[^"]*)"\s*(?:in\s*"(?[^"]*)")?""" + .toRegex() + .findAll(program) + .map { + SymbolComment( + enumValueOf(it.groupValues[1]), + it.groupValues[2], + it.groupValues[3] + ) + } + .toList() + } + + private fun expectedSymbols(program: String): List { + val ranges = findTestRanges(program) as? FindTestRangesResult.Success ?: return emptyList() + val symbolComments = symbolComments(program) + + return ranges.ranges.zip(symbolComments) { range, comment -> + ExpectedSymbol(comment.kind, comment.name, range.toLspRange(), comment.containerName) + } + } +} + +private data class SymbolComment(val kind: SymbolKind, val name: String, val containerName: String?) + +private data class ExpectedSymbol( + val kind: SymbolKind, + val name: String, + val range: LspRange, + val containerName: String? +) { + private val indent = " " + + override fun toString() = buildString { + appendLine("symbol \"$name\" {") + appendLine("${indent}kind: ${kind.value}") + appendLine("${indent}location: MyModel.smltest $range") + + if (!containerName.isNullOrEmpty()) { + appendLine("${indent}container: \"$containerName\"") + } + + appendLine("}") + } +} diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/annotations.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/annotations.smltest new file mode 100644 index 000000000..1921d7bdb --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/annotations.smltest @@ -0,0 +1,2 @@ +// Interface "MyAnnotation" +annotation »MyAnnotation« diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/attributes.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/attributes.smltest new file mode 100644 index 000000000..cf4d166cf --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/attributes.smltest @@ -0,0 +1,5 @@ +// Class "MyClass" +class »MyClass« { + // Field "myAttribute" in "MyClass" + attr »myAttribute«: Int +} diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/classes.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/classes.smltest new file mode 100644 index 000000000..4c0b1dbcb --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/classes.smltest @@ -0,0 +1,2 @@ +// Class "MyClass" +class »MyClass« diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/compilationUnitMembers.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/compilationUnitMembers.smltest new file mode 100644 index 000000000..a3259451a --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/compilationUnitMembers.smltest @@ -0,0 +1,5 @@ +// Package "myPackage" +package »myPackage« + +// Function "myWorkflow" in "myPackage" +workflow »myWorkflow« {} diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/compilationUnits.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/compilationUnits.smltest new file mode 100644 index 000000000..d686e1af4 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/compilationUnits.smltest @@ -0,0 +1,2 @@ +// Package "myPackage" +package »myPackage« diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/enumVariants.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/enumVariants.smltest new file mode 100644 index 000000000..d9c01fc2d --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/enumVariants.smltest @@ -0,0 +1,5 @@ +// Enum "MyEnum" +enum »MyEnum« { + // EnumMember "EnumVariant" in "MyEnum" + »EnumVariant« +} diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/enums.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/enums.smltest new file mode 100644 index 000000000..990a8bfd0 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/enums.smltest @@ -0,0 +1,2 @@ +// Enum "MyEnum" +enum »MyEnum« diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/globalFunctions.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/globalFunctions.smltest new file mode 100644 index 000000000..a1095a83f --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/globalFunctions.smltest @@ -0,0 +1,2 @@ +// Function "myFunction" +fun »myFunction«() diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/lambdaResults.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/lambdaResults.smltest new file mode 100644 index 000000000..77ed09f71 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/lambdaResults.smltest @@ -0,0 +1,6 @@ +// Function "myWorkflow" +workflow »myWorkflow« { + lambda { + yield a = 1; + }; +} diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/methods.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/methods.smltest new file mode 100644 index 000000000..2fbf6fdd4 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/methods.smltest @@ -0,0 +1,5 @@ +// Class "MyClass" +class »MyClass« { + // Method "myMethod" in "MyClass" + fun »myMethod«() +} diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/parameters.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/parameters.smltest new file mode 100644 index 000000000..9b823de56 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/parameters.smltest @@ -0,0 +1,2 @@ +// Function "myFunction" +fun »myFunction«(a: Int) diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/placeholders.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/placeholders.smltest new file mode 100644 index 000000000..8b77785e8 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/placeholders.smltest @@ -0,0 +1,4 @@ +// Function "myWorkflow" +workflow »myWorkflow« { + val a = 1; +} diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/results.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/results.smltest new file mode 100644 index 000000000..c91adb81c --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/results.smltest @@ -0,0 +1,2 @@ +// Function "myFunction" +fun »myFunction«() -> a: Int diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/typeParameters.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/typeParameters.smltest new file mode 100644 index 000000000..e3e351eee --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/typeParameters.smltest @@ -0,0 +1,2 @@ +// Function "myFunction" +fun »myFunction«() diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/workflowSteps.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/workflowSteps.smltest new file mode 100644 index 000000000..b994775b9 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/workflowSteps.smltest @@ -0,0 +1,2 @@ +// Function "myStep" +step »myStep« () {} diff --git a/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/workflows.smltest b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/workflows.smltest new file mode 100644 index 000000000..785485df1 --- /dev/null +++ b/DSL/de.unibonn.simpleml.ide/src/test/resources/symbols/workflows.smltest @@ -0,0 +1,2 @@ +// Function "myWorkflow" +workflow »myWorkflow« {} diff --git a/DSL/de.unibonn.simpleml.vscode/.gitattributes b/DSL/de.unibonn.simpleml.vscode/.gitattributes new file mode 100644 index 000000000..13bc9c2ee --- /dev/null +++ b/DSL/de.unibonn.simpleml.vscode/.gitattributes @@ -0,0 +1,3 @@ +# Set default behavior to automatically normalize line endings. +* text=auto + diff --git a/DSL/de.unibonn.simpleml.vscode/.gitignore b/DSL/de.unibonn.simpleml.vscode/.gitignore new file mode 100644 index 000000000..f0308a903 --- /dev/null +++ b/DSL/de.unibonn.simpleml.vscode/.gitignore @@ -0,0 +1,5 @@ +/dist/ +/ls/ +/out/ +/node_modules/ +/src/simpleml/ diff --git a/DSL/de.unibonn.simpleml.vscode/.vscodeignore b/DSL/de.unibonn.simpleml.vscode/.vscodeignore new file mode 100644 index 000000000..53bc0f10c --- /dev/null +++ b/DSL/de.unibonn.simpleml.vscode/.vscodeignore @@ -0,0 +1,20 @@ +# Git +.gitignore + +# Gradle +build.gradle.kts + +# Typescript +**/*.ts +**/tsconfig.json + +# NPM +**/node_modules + +# VS Code +.vscode/** +.vscode-test/** +vsc-extension-quickstart.md + +# Sources +src/ diff --git a/DSL/de.unibonn.simpleml.vscode/CHANGELOG.md b/DSL/de.unibonn.simpleml.vscode/CHANGELOG.md new file mode 100644 index 000000000..043d0fef9 --- /dev/null +++ b/DSL/de.unibonn.simpleml.vscode/CHANGELOG.md @@ -0,0 +1,9 @@ +# Change Log + +## [Unreleased] +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security diff --git a/DSL/de.unibonn.simpleml.vscode/README.md b/DSL/de.unibonn.simpleml.vscode/README.md new file mode 100644 index 000000000..6dc4da04a --- /dev/null +++ b/DSL/de.unibonn.simpleml.vscode/README.md @@ -0,0 +1,7 @@ +# Simple-ML + +A preview of the language support for the [Simple-ML](https://simple-ml.de/) DSL. + +## Requirements + +You need to install [OpenJDK 17 LTS](https://adoptium.net/temurin/releases). Later releases should also work fine. diff --git a/DSL/de.unibonn.simpleml.vscode/build.gradle.kts b/DSL/de.unibonn.simpleml.vscode/build.gradle.kts new file mode 100644 index 000000000..8c6cc13f2 --- /dev/null +++ b/DSL/de.unibonn.simpleml.vscode/build.gradle.kts @@ -0,0 +1,104 @@ +import com.github.gradle.node.npm.task.NpxTask + +// Plugins ------------------------------------------------------------------------------------------------------------- + +plugins { + base + id("com.github.node-gradle.node") + idea +} + +node { + version.set("17.9.0") + download.set(true) +} + +idea { + module { + sourceDirs.add(file("src")) + sourceDirs.add(file("syntaxes")) + + excludeDirs.add(file("dist")) + excludeDirs.add(file("ls")) + excludeDirs.add(file("node_modules")) + } +} + +// Tasks --------------------------------------------------------------------------------------------------------------- + +val extensionPath = "dist/simple-ml-${project.version}.vsix" + +tasks.register("copyApplication") { + val installDistTask = project(":de.unibonn.simpleml.ide").tasks.named("installDist") + dependsOn(installDistTask) + + from(installDistTask.get().outputs) + into("ls") +} + +tasks { + npmInstall { + dependsOn("copyApplication") + } +} + +tasks.register("vsCodeExtension") { + group = "Build" + description = "Generate an extension for VS Code" + + dependsOn("npmInstall") + + inputs.dir("icons") + inputs.dir("ls") + inputs.dir("src") + inputs.dir("syntaxes") + inputs.files( + ".vscodeignore", + "CHANGELOG.md", + "language-configuration.json", + "package.json", + "README.md", + "tsconfig.json" + ) + outputs.dirs("dist") + + command.set("vsce") + args.set(listOf("package", "--out", extensionPath)) +} + +tasks.register("installExtension") { + dependsOn("vsCodeExtension") + + inputs.files(extensionPath) + outputs.dirs() + + if (System.getProperty("os.name").toLowerCase().contains("windows")) { + commandLine("powershell", "code", "--install-extension", extensionPath) + } else { + commandLine("code", "--install-extension", extensionPath) + } +} + +tasks.register("launchVSCode") { + group = "Run" + description = "Launch VS Code with the extension installed" + + dependsOn("installExtension") + + if (System.getProperty("os.name").toLowerCase().contains("windows")) { + commandLine("powershell", "code", "-n", "../de.unibonn.simpleml/src/main/resources/stdlib") + } else { + commandLine("code", "-n", "../de.unibonn.simpleml/src/main/resources/stdlib") + } +} + +tasks { + build { + dependsOn("vsCodeExtension") + } + + clean { + delete(named("copyApplication").get().outputs) + delete(named("vsCodeExtension").get().outputs) + } +} diff --git a/DSL/de.unibonn.simpleml.vscode/icons/icon.png b/DSL/de.unibonn.simpleml.vscode/icons/icon.png new file mode 100644 index 000000000..a47c996af Binary files /dev/null and b/DSL/de.unibonn.simpleml.vscode/icons/icon.png differ diff --git a/DSL/de.unibonn.simpleml.vscode/language-configuration.json b/DSL/de.unibonn.simpleml.vscode/language-configuration.json new file mode 100644 index 000000000..1756c1c1b --- /dev/null +++ b/DSL/de.unibonn.simpleml.vscode/language-configuration.json @@ -0,0 +1,33 @@ +{ + "comments": { + // symbol used for single line comment. Remove this entry if your language does not support line comments + "lineComment": "//", + // symbols used for start and end a block comment. Remove this entry if your language does not support block comments + "blockComment": [ "/*", "*/" ] + }, + // symbols used as brackets + "brackets": [ + ["{", "}"], + ["(", ")"], + ["<", ">"], + ["»", "«"] + ], + // symbols that are auto closed when typing + "autoClosingPairs": [ + ["{", "}"], + ["(", ")"], + ["<", ">"], + ["»", "«"], + ["\"", "\""], + ["`", "`"] + ], + // symbols that can be used to surround a selection + "surroundingPairs": [ + ["{", "}"], + ["(", ")"], + ["<", ">"], + ["»", "«"], + ["\"", "\""], + ["`", "`"] + ] +} diff --git a/DSL/de.unibonn.simpleml.vscode/package-lock.json b/DSL/de.unibonn.simpleml.vscode/package-lock.json new file mode 100644 index 000000000..96b9178d4 --- /dev/null +++ b/DSL/de.unibonn.simpleml.vscode/package-lock.json @@ -0,0 +1,927 @@ +{ + "name": "simple-ml", + "version": "0.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "simple-ml", + "version": "0.0.1", + "dependencies": { + "vscode-languageclient": "^7.0.0" + }, + "devDependencies": { + "@types/node": "^17.0.31", + "@types/vscode": "^1.62.0", + "esbuild": "^0.14.38", + "typescript": "^4.6.4", + "vscode-test": "^1.6.1" + }, + "engines": { + "vscode": "^1.62.0" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/node": { + "version": "17.0.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz", + "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==", + "dev": true + }, + "node_modules/@types/vscode": { + "version": "1.62.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.62.0.tgz", + "integrity": "sha512-iGlQJ1w5e3qPUryroO6v4lxg3ql1ztdTCwQW3xEwFawdyPLoeUSv48SYfMwc7kQA7h6ThUqflZIjgKAykeF9oA==", + "dev": true + }, + "node_modules/agent-base": { + "version": "6.0.2", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "node_modules/big-integer": { + "version": "1.6.48", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "dev": true, + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", + "dev": true, + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", + "dev": true, + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.1", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/esbuild": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.38.tgz", + "integrity": "sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "esbuild-android-64": "0.14.38", + "esbuild-android-arm64": "0.14.38", + "esbuild-darwin-64": "0.14.38", + "esbuild-darwin-arm64": "0.14.38", + "esbuild-freebsd-64": "0.14.38", + "esbuild-freebsd-arm64": "0.14.38", + "esbuild-linux-32": "0.14.38", + "esbuild-linux-64": "0.14.38", + "esbuild-linux-arm": "0.14.38", + "esbuild-linux-arm64": "0.14.38", + "esbuild-linux-mips64le": "0.14.38", + "esbuild-linux-ppc64le": "0.14.38", + "esbuild-linux-riscv64": "0.14.38", + "esbuild-linux-s390x": "0.14.38", + "esbuild-netbsd-64": "0.14.38", + "esbuild-openbsd-64": "0.14.38", + "esbuild-sunos-64": "0.14.38", + "esbuild-windows-32": "0.14.38", + "esbuild-windows-64": "0.14.38", + "esbuild-windows-arm64": "0.14.38" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.38.tgz", + "integrity": "sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fstream": { + "version": "1.0.12", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.6", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/isarray": { + "version": "1.0.0", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/listenercount": { + "version": "1.0.1", + "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/semver": { + "version": "7.3.4", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/traverse": { + "version": "0.3.9", + "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unzipper": { + "version": "0.10.11", + "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/vscode-jsonrpc": { + "version": "6.0.0", + "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==", + "engines": { + "node": ">=8.0.0 || >=10.0.0" + } + }, + "node_modules/vscode-languageclient": { + "version": "7.0.0", + "integrity": "sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==", + "dependencies": { + "minimatch": "^3.0.4", + "semver": "^7.3.4", + "vscode-languageserver-protocol": "3.16.0" + }, + "engines": { + "vscode": "^1.52.0" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.16.0", + "integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==", + "dependencies": { + "vscode-jsonrpc": "6.0.0", + "vscode-languageserver-types": "3.16.0" + } + }, + "node_modules/vscode-languageserver-types": { + "version": "3.16.0", + "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==" + }, + "node_modules/vscode-test": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.6.1.tgz", + "integrity": "sha512-086q88T2ca1k95mUzffvbzb7esqQNvJgiwY4h29ukPhFo8u+vXOOmelUoU5EQUHs3Of8+JuQ3oGdbVCqaxuTXA==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "rimraf": "^3.0.2", + "unzipper": "^0.10.11" + }, + "engines": { + "node": ">=8.9.3" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + }, + "dependencies": { + "@tootallnate/once": { + "version": "1.1.2", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, + "@types/node": { + "version": "17.0.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz", + "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==", + "dev": true + }, + "@types/vscode": { + "version": "1.62.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.62.0.tgz", + "integrity": "sha512-iGlQJ1w5e3qPUryroO6v4lxg3ql1ztdTCwQW3xEwFawdyPLoeUSv48SYfMwc7kQA7h6ThUqflZIjgKAykeF9oA==", + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "balanced-match": { + "version": "1.0.0", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "big-integer": { + "version": "1.6.48", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", + "dev": true + }, + "binary": { + "version": "0.3.0", + "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "dev": true, + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } + }, + "bluebird": { + "version": "3.4.7", + "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-indexof-polyfill": { + "version": "1.0.2", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true + }, + "buffers": { + "version": "0.1.1", + "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", + "dev": true + }, + "chainsaw": { + "version": "0.1.0", + "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", + "dev": true, + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, + "concat-map": { + "version": "0.0.1", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "core-util-is": { + "version": "1.0.2", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "debug": { + "version": "4.3.1", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "duplexer2": { + "version": "0.1.4", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "esbuild": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.38.tgz", + "integrity": "sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA==", + "dev": true, + "requires": { + "esbuild-android-64": "0.14.38", + "esbuild-android-arm64": "0.14.38", + "esbuild-darwin-64": "0.14.38", + "esbuild-darwin-arm64": "0.14.38", + "esbuild-freebsd-64": "0.14.38", + "esbuild-freebsd-arm64": "0.14.38", + "esbuild-linux-32": "0.14.38", + "esbuild-linux-64": "0.14.38", + "esbuild-linux-arm": "0.14.38", + "esbuild-linux-arm64": "0.14.38", + "esbuild-linux-mips64le": "0.14.38", + "esbuild-linux-ppc64le": "0.14.38", + "esbuild-linux-riscv64": "0.14.38", + "esbuild-linux-s390x": "0.14.38", + "esbuild-netbsd-64": "0.14.38", + "esbuild-openbsd-64": "0.14.38", + "esbuild-sunos-64": "0.14.38", + "esbuild-windows-32": "0.14.38", + "esbuild-windows-64": "0.14.38", + "esbuild-windows-arm64": "0.14.38" + } + }, + "esbuild-windows-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.38.tgz", + "integrity": "sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw==", + "dev": true, + "optional": true + }, + "fs.realpath": { + "version": "1.0.0", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fstream": { + "version": "1.0.12", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "glob": { + "version": "7.1.6", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.6", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true + }, + "http-proxy-agent": { + "version": "4.0.1", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "inflight": { + "version": "1.0.6", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "listenercount": { + "version": "1.0.1", + "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "once": { + "version": "1.4.0", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "3.0.2", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "semver": { + "version": "7.3.4", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "setimmediate": { + "version": "1.0.5", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "traverse": { + "version": "0.3.9", + "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", + "dev": true + }, + "typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "dev": true + }, + "unzipper": { + "version": "0.10.11", + "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", + "dev": true, + "requires": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "util-deprecate": { + "version": "1.0.2", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "vscode-jsonrpc": { + "version": "6.0.0", + "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==" + }, + "vscode-languageclient": { + "version": "7.0.0", + "integrity": "sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==", + "requires": { + "minimatch": "^3.0.4", + "semver": "^7.3.4", + "vscode-languageserver-protocol": "3.16.0" + } + }, + "vscode-languageserver-protocol": { + "version": "3.16.0", + "integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==", + "requires": { + "vscode-jsonrpc": "6.0.0", + "vscode-languageserver-types": "3.16.0" + } + }, + "vscode-languageserver-types": { + "version": "3.16.0", + "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==" + }, + "vscode-test": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.6.1.tgz", + "integrity": "sha512-086q88T2ca1k95mUzffvbzb7esqQNvJgiwY4h29ukPhFo8u+vXOOmelUoU5EQUHs3Of8+JuQ3oGdbVCqaxuTXA==", + "dev": true, + "requires": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "rimraf": "^3.0.2", + "unzipper": "^0.10.11" + } + }, + "wrappy": { + "version": "1.0.2", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/DSL/de.unibonn.simpleml.vscode/package.json b/DSL/de.unibonn.simpleml.vscode/package.json new file mode 100644 index 000000000..d169acd10 --- /dev/null +++ b/DSL/de.unibonn.simpleml.vscode/package.json @@ -0,0 +1,66 @@ +{ + "name": "simple-ml", + "displayName": "Simple-ML", + "description": "Machine Learning made simple.", + "publisher": "Simple-ML", + "repository": { + "url": "https://github.com/Simple-ML/Simple-ML" + }, + "icon": "icons/icon.png", + "galleryBanner": { + "color": "#ffffff" + }, + "version": "0.0.1", + "engines": { + "vscode": "^1.62.0" + }, + "categories": [ + "Programming Languages", + "Machine Learning", + "Data Science" + ], + "contributes": { + "languages": [ + { + "id": "simple-ml", + "aliases": [ + "Simple-ML", + "simple-ml", + "SimpleML", + "simpleml" + ], + "extensions": [ + ".smlflow", + ".smlstub", + ".smltest" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "simple-ml", + "scopeName": "source.simpleml", + "path": "./syntaxes/simple-ml.tmLanguage.json" + } + ] + }, + "activationEvents": [ + "onLanguage:simple-ml" + ], + "main": "./dist/extension.js", + "scripts": { + "vscode:prepublish": "esbuild src/extension.ts --bundle --platform=node --external:vscode --outfile=dist/extension.js", + "vscode:package": "vsce package" + }, + "dependencies": { + "vscode-languageclient": "^7.0.0" + }, + "devDependencies": { + "@types/node": "^17.0.31", + "@types/vscode": "^1.62.0", + "esbuild": "^0.14.38", + "typescript": "^4.6.4", + "vscode-test": "^1.6.1" + } +} diff --git a/DSL/de.unibonn.simpleml.vscode/src/extension.ts b/DSL/de.unibonn.simpleml.vscode/src/extension.ts new file mode 100644 index 000000000..70b0bb0c8 --- /dev/null +++ b/DSL/de.unibonn.simpleml.vscode/src/extension.ts @@ -0,0 +1,52 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import * as os from 'os' +import * as path from 'path' +import { workspace, ExtensionContext } from 'vscode' + +import { LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node' + +let client: LanguageClient + +export const activate = (context: ExtensionContext) => { + let launcher = os.platform() === 'win32' ? 'de.unibonn.simpleml.ide.bat' : 'de.unibonn.simpleml.ide'; + let script = context.asAbsolutePath(path.join('ls', 'bin', launcher)); + + const serverOptions: ServerOptions = { + run: { + command: script, + args: ['-log', 'debug', '--trace-deprecation'], + }, + debug: { + command: script, + args: ['-log', 'debug', '--trace-deprecation'], + }, + } + + // Options to control the language client + let clientOptions: LanguageClientOptions = { + // Register the server for plain text documents + documentSelector: [{ scheme: 'file', language: 'simple-ml' }], + synchronize: { + // Notify the server about file changes to '.clientrc files contained in the workspace + fileEvents: workspace.createFileSystemWatcher('**/.clientrc'), + }, + outputChannelName: 'Simple-ML Language Server', + } + + // Create the language client and start the client. + client = new LanguageClient('simpleml', 'Simple-ML', serverOptions, clientOptions) + + // Start the client. This will also launch the server + client.start() +}; + +export const deactivate = (): Thenable | undefined => { + if (!client) { + return undefined + } + return client.stop() +}; diff --git a/DSL/de.unibonn.simpleml.vscode/syntaxes/simple-ml.tmLanguage.json b/DSL/de.unibonn.simpleml.vscode/syntaxes/simple-ml.tmLanguage.json new file mode 100644 index 000000000..77e079c4c --- /dev/null +++ b/DSL/de.unibonn.simpleml.vscode/syntaxes/simple-ml.tmLanguage.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": "Simple-ML", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#keywords" + }, + { + "include": "#strings" + } + ], + "repository": { + "comment": { + "patterns": [ + { + "name": "comment.block.simpleml", + "begin": "/\\*", + "end": "\\*/" + }, + { + "name": "comment.line.double-slash.simpleml", + "begin": "//", + "end": "(?=$)" + } + ] + }, + "keywords": { + "patterns": [ + { + "name": "keyword.control.simple-ml", + "match": "\\b(_|and|annotation|as|attr|class|enum|false|fun|import|in|internal|not|null|or|out|package|private|protocol|static|step|sub|subterm|super|true|union|val|vararg|where|workflow|yield)\\b" + } + ] + }, + "strings": { + "name": "string.quoted.double.simple-ml", + "begin": "\"", + "end": "\"", + "patterns": [ + { + "name": "constant.character.escape.simple-ml", + "match": "\\\\." + } + ] + } + }, + "scopeName": "source.simpleml" +} diff --git a/DSL/de.unibonn.simpleml.vscode/tsconfig.json b/DSL/de.unibonn.simpleml.vscode/tsconfig.json new file mode 100644 index 000000000..876a7bfa1 --- /dev/null +++ b/DSL/de.unibonn.simpleml.vscode/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2019", + "lib": ["ES2019"], + "outDir": "out", + "rootDir": "src", + "sourceMap": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", ".vscode-test"] +} diff --git a/DSL/de.unibonn.simpleml.vscode/vsc-extension-quickstart.md b/DSL/de.unibonn.simpleml.vscode/vsc-extension-quickstart.md new file mode 100644 index 000000000..2892013ff --- /dev/null +++ b/DSL/de.unibonn.simpleml.vscode/vsc-extension-quickstart.md @@ -0,0 +1,29 @@ +# Welcome to your VS Code Extension + +## What's in the folder + +* This folder contains all of the files necessary for your extension. +* `package.json` - this is the manifest file in which you declare your language support and define the location of the grammar file that has been copied into your extension. +* `syntaxes/simple-ml.tmLanguage.json` - this is the Text mate grammar file that is used for tokenization. +* `language-configuration.json` - this is the language configuration, defining the tokens that are used for comments and brackets. + +## Get up and running straight away + +* Make sure the language configuration settings in `language-configuration.json` are accurate. +* Press `F5` to open a new window with your extension loaded. +* Create a new file with a file name suffix matching your language. +* Verify that syntax highlighting works and that the language configuration settings are working. + +## Make changes + +* You can relaunch the extension from the debug toolbar after making changes to the files listed above. +* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. + +## Add more language features + +* To add features such as intellisense, hovers and validators check out the VS Code extenders documentation at https://code.visualstudio.com/docs + +## Install your extension + +* To start using your extension with Visual Studio Code copy it into the `/.vscode/extensions` folder and restart Code. +* To share your extension with the world, read on https://code.visualstudio.com/docs about publishing an extension. diff --git a/DSL/de.unibonn.simpleml/.gitignore b/DSL/de.unibonn.simpleml/.gitignore new file mode 100644 index 000000000..5ade2c876 --- /dev/null +++ b/DSL/de.unibonn.simpleml/.gitignore @@ -0,0 +1,7 @@ +/emf-gen +/META-INF +/src-gen +build.properties +plugin.properties +plugin.xml +*.xtend diff --git a/DSL/de.unibonn.simpleml/build.gradle.kts b/DSL/de.unibonn.simpleml/build.gradle.kts new file mode 100644 index 000000000..2f4f93738 --- /dev/null +++ b/DSL/de.unibonn.simpleml/build.gradle.kts @@ -0,0 +1,139 @@ +val javaVersion: Int by rootProject.extra +val xtextVersion: String by rootProject.extra + +// Plugins ------------------------------------------------------------------------------------------------------------- + +plugins { + `java-library` + `java-test-fixtures` + kotlin("jvm") + idea +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(javaVersion)) + } +} + +idea { + module { + excludeDirs.add(file("META-INF")) + } +} + +// Dependencies -------------------------------------------------------------------------------------------------------- + +dependencies { + api(platform("org.eclipse.xtext:xtext-dev-bom:$xtextVersion")) + implementation("org.eclipse.xtext:org.eclipse.xtext:$xtextVersion") + + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.8.2") + testImplementation("org.eclipse.xtext:org.eclipse.xtext.testing:$xtextVersion") + testImplementation("org.eclipse.xtext:org.eclipse.xtext.xbase.testing:$xtextVersion") + testImplementation("io.kotest:kotest-assertions-core-jvm:5.2.3") + + testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") + testFixturesImplementation("org.eclipse.xtext:org.eclipse.xtext.testing:$xtextVersion") + testFixturesImplementation("org.eclipse.xtext:org.eclipse.xtext.xbase.testing:$xtextVersion") + testFixturesImplementation("io.kotest:kotest-assertions-core-jvm:5.2.3") +} + +// Source sets --------------------------------------------------------------------------------------------------------- + +sourceSets { + main { + java.srcDirs("emf-gen", "src-gen") + resources.srcDirs("src-gen") + resources.include( + "**/*.smlflow", + "**/*.smlstub", + "**/*.tokens", + "**/*.xtextbin" + ) + } +} + +// Tasks --------------------------------------------------------------------------------------------------------------- + +val koverExcludes = listOf( + "de.unibonn.simpleml.parser.antlr.*", + "de.unibonn.simpleml.serializer.AbstractSimpleMLSemanticSequencer", + "de.unibonn.simpleml.serializer.AbstractSimpleMLSyntacticSequencer", + "de.unibonn.simpleml.services.*", + "de.unibonn.simpleml.simpleML.*", + "de.unibonn.simpleml.testing.*" +) + +tasks { + build { + dependsOn(project.tasks.named("generateStdlibDocumentation")) + } + + compileJava { + dependsOn(rootProject.tasks.named("generateXtextLanguage")) + } + + compileKotlin { + dependsOn(rootProject.tasks.named("generateXtextLanguage")) + } + + processResources { + dependsOn(rootProject.tasks.named("generateXtextLanguage")) + } + + clean { + dependsOn(rootProject.tasks.named("cleanGenerateXtextLanguage")) + } + + test { + useJUnitPlatform() + + minHeapSize = "512m" + maxHeapSize = "1024m" + + extensions.configure(kotlinx.kover.api.KoverTaskExtension::class) { + excludes = koverExcludes + } + } + + koverHtmlReport { + excludes = koverExcludes + } + + koverXmlReport { + excludes = koverExcludes + } + + koverVerify { + excludes = koverExcludes + rule { + name = "Minimal line coverage rate in percents" + bound { + minValue = 80 + } + } + } +} + +tasks.withType().configureEach { + kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" +} + +tasks.register("generateStdlibDocumentation") { + group = "documentation" + description = "Generate documentation for the standard library." + + val inputDirectory = project.file("src/main/resources/stdlib") + val outputDirectory = rootProject.file("../docs/Stdlib/API").absolutePath + + inputs.dir(inputDirectory) + outputs.dirs(outputDirectory) + + dependsOn(sourceSets.main.get().runtimeClasspath) + classpath = sourceSets.main.get().runtimeClasspath.filter { it.exists() } + mainClass.set("de.unibonn.simpleml.stdlibDocumentation.MainKt") + args = listOf(outputDirectory) +} diff --git a/DSL/de.unibonn.simpleml/model/SimpleML.ecore b/DSL/de.unibonn.simpleml/model/SimpleML.ecore new file mode 100644 index 000000000..831832351 --- /dev/null +++ b/DSL/de.unibonn.simpleml/model/SimpleML.ecore @@ -0,0 +1,271 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DSL/de.unibonn.simpleml/model/SimpleML.genmodel b/DSL/de.unibonn.simpleml/model/SimpleML.genmodel new file mode 100644 index 000000000..11b9f0f5a --- /dev/null +++ b/DSL/de.unibonn.simpleml/model/SimpleML.genmodel @@ -0,0 +1,272 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/SimpleML.xtext b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/SimpleML.xtext new file mode 100644 index 000000000..4f1314540 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/SimpleML.xtext @@ -0,0 +1,714 @@ +grammar de.unibonn.simpleml.SimpleML hidden ( + WS, + SL_COMMENT, + ML_COMMENT, + TEST_MARKER +) +import 'http://www.eclipse.org/emf/2002/Ecore' as ecore +import 'http://www.unibonn.de/simpleml/SimpleML' + + + +/********************************************************************************************************************** + * Declarations + **********************************************************************************************************************/ + +// Compilations Units -------------------------------------------------------------------------------------------------- + +SmlCompilationUnit + : {SmlCompilationUnit} + =>annotationCalls+=SmlAnnotationCall* // Annotation calls before a package declaration belong to the compilation unit + ('package' name=QualifiedName)? + imports+=SmlImport* + members+=SmlCompilationUnitMember* + ; + +SmlCompilationUnitMember returns SmlAbstractAnnotatedObject + : {SmlAnnotationCallList} annotationCalls+=SmlAnnotationCall* + + ( {SmlAnnotation.annotationCallList=current} + 'annotation' name=ID + parameterList=SmlParameterList? + constraintList=SmlConstraintList? + + | {SmlClass.annotationCallList=current} + 'class' name=ID + typeParameterList=SmlTypeParameterList? + parameterList=SmlParameterList? + parentTypeList=SmlParentTypeList? + constraintList=SmlConstraintList? + body=SmlClassBody? + + | {SmlEnum.annotationCallList=current} + 'enum' name=ID + body=SmlEnumBody? + + | {SmlFunction.annotationCallList=current} + 'fun' name=ID + typeParameterList=SmlTypeParameterList? + parameterList=SmlParameterList + resultList=SmlResultList? + constraintList=SmlConstraintList? + + | {SmlStep.annotationCallList=current} + visibility=('internal'|'private')? + 'step' name=ID + parameterList=SmlParameterList + resultList=SmlResultList? + body=SmlBlock + + | {SmlWorkflow.annotationCallList=current} + 'workflow' name=ID + body=SmlBlock + ) + ; + +SmlImport + : 'import' importedNamespace=QualifiedNameWithWildcard alias=SmlImportAlias? + ; + +SmlImportAlias + : 'as' name=ID + ; + + +// Annotations --------------------------------------------------------------------------------------------------------- + +SmlAnnotationCall + : '@' annotation=[SmlAnnotation] argumentList=SmlAnnotationCallArgumentList? + ; + +SmlAnnotationCallArgumentList returns SmlArgumentList + : {SmlArgumentList} '(' (arguments+=SmlAnnotationCallArgument (',' arguments+=SmlAnnotationCallArgument)* ','?)? ')' + ; + +SmlAnnotationCallArgument returns SmlArgument + : (parameter=[SmlParameter] '=')? value=SmlExpression + ; + + +// Classes ------------------------------------------------------------------------------------------------------------- + +SmlParentTypeList + : 'sub' parentTypes+=SmlParentType (',' parentTypes+=SmlParentType)* ','? + ; + +SmlParentType returns SmlAbstractType + : SmlParentPrimaryType =>({SmlMemberType.receiver=current} '.' member=SmlNamedType)* + ; + +SmlParentPrimaryType returns SmlAbstractType + : SmlNamedType + | SmlParentParenthesizedType + ; + +SmlParentParenthesizedType returns SmlParenthesizedType + : '(' type=SmlParentType ')' + ; + +SmlClassBody + : {SmlClassBody} '{' members+=SmlClassMember* '}' + ; + +SmlClassMember returns SmlAbstractObject + : SmlAnnotatedClassMember + | SmlProtocol + ; + +SmlAnnotatedClassMember returns SmlAbstractAnnotatedObject + : {SmlAnnotationCallList} annotationCalls+=SmlAnnotationCall* + + ( {SmlAttribute.annotationCallList=current} + static?='static'? + 'attr' name=ID (':' type=SmlType)? + + | {SmlClass.annotationCallList=current} + 'class' name=ID + typeParameterList=SmlTypeParameterList? + parameterList=SmlParameterList? + parentTypeList=SmlParentTypeList? + constraintList=SmlConstraintList? + body=SmlClassBody? + + | {SmlEnum.annotationCallList=current} + 'enum' name=ID + body=SmlEnumBody? + + | {SmlFunction.annotationCallList=current} + static?='static'? + 'fun' name=ID + typeParameterList=SmlTypeParameterList? + parameterList=SmlParameterList + resultList=SmlResultList? + constraintList=SmlConstraintList? + ) + ; + +SmlProtocol + : 'protocol' body=SmlProtocolBody + ; + +SmlProtocolBody + : {SmlProtocolBody} + '{' + subtermList=SmlProtocolSubtermList? + term=SmlProtocolTerm? + '}' + ; + +SmlProtocolSubtermList + : subterms+=SmlProtocolSubterm+ + ; + +SmlProtocolSubterm + : 'subterm' name=ID '=' term=SmlProtocolTerm ';' + ; + +SmlProtocolTerm returns SmlAbstractProtocolTerm + : SmlProtocolAlternative + ; + +SmlProtocolAlternative returns SmlAbstractProtocolTerm + : SmlProtocolSequence + ( + {SmlProtocolAlternative.terms+=current} '|' terms+=SmlProtocolSequence + ('|' terms+=SmlProtocolSequence)* + )? + ; + +SmlProtocolSequence returns SmlAbstractProtocolTerm + : SmlProtocolQuantifiedTerm + ( + {SmlProtocolSequence.terms+=current} terms+=SmlProtocolQuantifiedTerm + (terms+=SmlProtocolQuantifiedTerm)* + )? + ; + +SmlProtocolQuantifiedTerm returns SmlAbstractProtocolTerm + : SmlProtocolPrimaryElement + ({SmlProtocolQuantifiedTerm.term=current} quantifier=SmlProtocolQuantifier)? + ; + +SmlProtocolQuantifier + : '?' + | '*' + | '+' + ; + +SmlProtocolPrimaryElement returns SmlAbstractProtocolTerm + : SmlProtocolComplement + | SmlProtocolReference + | SmlProtocolTokenClass + | SmlProtocolParenthesizedTerm + ; + +SmlProtocolComplement + : {SmlProtocolComplement} + '[' + universe=SmlProtocolTokenClass? + '^' + referenceList=SmlProtocolReferenceList? + ']' + ; + +SmlProtocolReferenceList + : references+=SmlProtocolReference+ + ; + +SmlProtocolReference + : token=[SmlAbstractProtocolToken] + ; + +SmlProtocolTokenClass + : value=SmlProtocolTokenClassValue + ; + +SmlProtocolTokenClassValue + : '.' + | '\\a' + | '\\f' + ; + +SmlProtocolParenthesizedTerm + : '(' term=SmlProtocolTerm ')' + ; + + +// Enums --------------------------------------------------------------------------------------------------------------- + +SmlEnumBody + : {SmlEnumBody} '{' variants+=SmlEnumVariant* '}' + ; + +SmlEnumVariant + : annotationCalls+=SmlAnnotationCall* + name=ID + typeParameterList=SmlTypeParameterList? + parameterList=SmlParameterList? + constraintList=SmlConstraintList? + ; + + +// Parameters ---------------------------------------------------------------------------------------------------------- + +SmlParameterList + : {SmlParameterList} '(' (parameters+=SmlParameter (',' parameters+=SmlParameter)* ','?)? ')' + ; + +// Used while parsing lambdas to avoid left-recursion. Can be treated like a normal parameter list. +SmlLambdaParameterList + : {SmlLambdaParameterList} '(' (parameters+=SmlParameter (',' parameters+=SmlParameter)* ','?)? ')' + ; + +SmlParameter + : annotationCalls+=SmlAnnotationCall* + variadic?='vararg'? + name=ID (':' type=SmlType)? + ('=' defaultValue=SmlExpression)? + ; + + +// Results ------------------------------------------------------------------------------------------------------------- + +SmlResultList + : {SmlResultList} '->' results+=SmlResult // If there is exactly one result we need no parentheses + | {SmlResultList} '->' '(' (results+=SmlResult (',' results+=SmlResult)* ','?)? ')' + ; + +SmlResult + : annotationCalls+=SmlAnnotationCall* + name=ID (':' type=SmlType)? + ; + + + +/********************************************************************************************************************** + * Statements + **********************************************************************************************************************/ + +// Functions and workflows -------------------------------------------------------------------------------------------- + +SmlBlock + : {SmlBlock} '{' statements+=SmlStatement* '}' + ; + +SmlStatement returns SmlAbstractStatement + : SmlAssignment + | SmlExpressionStatement + ; + +SmlAssignment + : assigneeList=SmlAssigneeList '=' expression=SmlExpression ';' + ; + +SmlAssigneeList + : assignees+=SmlAssignee (',' assignees+=SmlAssignee)* ','? + ; + +SmlAssignee returns SmlAbstractAssignee + : {SmlPlaceholder} 'val' name=ID + | {SmlWildcard} '_' + | {SmlYield} 'yield' result=[SmlResult] + ; + +SmlExpressionStatement + : expression=SmlExpression ';' + ; + +/********************************************************************************************************************** + * Expressions + **********************************************************************************************************************/ + +SmlExpression returns SmlAbstractExpression + : SmlLambda + | SmlOrExpression + ; + +SmlLambda returns SmlAbstractExpression + : SmlLambdaParameterList + ( {SmlBlockLambda.parameterList=current} body=SmlBlockLambdaBlock + | {SmlExpressionLambda.parameterList=current} '->' result=SmlExpression + ) + ; + +SmlBlockLambdaBlock returns SmlBlock + : {SmlBlock} '{' statements+=SmlBlockLambdaStatement* '}' + ; + +SmlBlockLambdaStatement returns SmlAbstractStatement + : SmlBlockLambdaAssignment + | SmlExpressionStatement + ; + +SmlBlockLambdaAssignment returns SmlAssignment + : assigneeList=SmlBlockLambdaAssigneeList '=' expression=SmlExpression ';' + ; + +SmlBlockLambdaAssigneeList returns SmlAssigneeList + : assignees+=SmlBlockLambdaAssignee (',' assignees+=SmlBlockLambdaAssignee)* ','? + ; + +SmlBlockLambdaAssignee returns SmlAbstractAssignee + : {SmlWildcard} '_' + | {SmlPlaceholder} 'val' name=ID + | {SmlBlockLambdaResult} 'yield' name=ID + ; + +SmlOrExpression returns SmlAbstractExpression + : SmlAndExpression ({SmlInfixOperation.leftOperand=current} operator='or' rightOperand=SmlAndExpression)* + ; + +SmlAndExpression returns SmlAbstractExpression + : SmlNotExpression ({SmlInfixOperation.leftOperand=current} operator='and' rightOperand=SmlNotExpression)* + ; + +SmlNotExpression returns SmlAbstractExpression + : {SmlPrefixOperation} operator='not' operand=SmlNotExpression + | SmlEqualityExpression + ; + +SmlEqualityExpression returns SmlAbstractExpression + : SmlComparisonExpression ({SmlInfixOperation.leftOperand=current} operator=SmlEqualityOperator rightOperand=SmlComparisonExpression)? + ; + +SmlEqualityOperator + : '==' + | '!=' + | '===' + | '!==' + ; + +SmlComparisonExpression returns SmlAbstractExpression + : SmlAdditiveExpression ({SmlInfixOperation.leftOperand=current} operator=SmlComparisonOperator rightOperand=SmlAdditiveExpression)? + ; + +SmlComparisonOperator + : '<' + | '<=' + | '>=' + | '>' + ; + +SmlAdditiveExpression returns SmlAbstractExpression + : SmlMultiplicativeExpression ({SmlInfixOperation.leftOperand=current} operator=SmlAdditiveOperator rightOperand=SmlMultiplicativeExpression)* + ; + +SmlAdditiveOperator + : '+' + | '-' + ; + +SmlMultiplicativeExpression returns SmlAbstractExpression + : SmlElvisExpression ({SmlInfixOperation.leftOperand=current} operator=SmlMultiplicativeOperator rightOperand=SmlElvisExpression)* + ; + +SmlMultiplicativeOperator + : '*' + | '/' + ; + +SmlElvisExpression returns SmlAbstractExpression + : SmlUnaryOperation ({SmlInfixOperation.leftOperand=current} operator='?:' rightOperand=SmlUnaryOperation)* + ; + +SmlUnaryOperation returns SmlAbstractExpression + : {SmlPrefixOperation} operator='-' operand=SmlUnaryOperation + | SmlChainedExpression + ; + +SmlChainedExpression returns SmlAbstractExpression + : SmlPrimaryExpression =>( + {SmlCall.receiver=current} typeArgumentList=SmlTypeArgumentList? argumentList=SmlCallArgumentList + | {SmlIndexedAccess.receiver=current} '[' index=SmlExpression ']' + | {SmlMemberAccess.receiver=current} (nullSafe?='?')? '.' member=SmlReference + )* + ; + +SmlCallArgumentList returns SmlArgumentList + : {SmlArgumentList} '(' (arguments+=SmlCallArgument (',' arguments+=SmlCallArgument)* ','?)? ')' + ; + +SmlCallArgument returns SmlArgument + : (parameter=[SmlParameter] '=')? value=SmlExpression + ; + +SmlPrimaryExpression returns SmlAbstractExpression + : SmlLiteral + | SmlParenthesizedExpression + | SmlReference + | SmlTemplateString + ; + +SmlLiteral returns SmlAbstractLiteral + : SmlBoolean + | SmlFloat + | SmlInt + | SmlNull + | SmlString + ; + +SmlBoolean + : true?='true' + | {SmlBoolean} 'false' + ; + +SmlFloat + : value=FLOAT + ; + +SmlInt + : value=INT + ; + +SmlNull + : {SmlNull} 'null' + ; + +SmlString + : value=STRING + ; + +SmlReference + : declaration=[SmlAbstractDeclaration] + ; + +SmlParenthesizedExpression + : '(' expression=SmlExpression ')' + ; + + +// Template strings ---------------------------------------------------------------------------------------------------- + +SmlTemplateString + : expressions+=SmlTemplateStringStart expressions+=SmlExpression? + (expressions+=SmlTemplateStringInner expressions+=SmlExpression?)* + expressions+=SmlTemplateStringEnd + ; + +SmlTemplateStringStart + : value=TEMPLATE_STRING_START + ; + +SmlTemplateStringInner + : value=TEMPLATE_STRING_INNER + ; + +SmlTemplateStringEnd + : value=TEMPLATE_STRING_END + ; + + + +/********************************************************************************************************************** + * Names + **********************************************************************************************************************/ + +QualifiedName + : ID ('.' ID)* + ; + +QualifiedNameWithWildcard + : QualifiedName ('.' '*')? + ; + + + +/********************************************************************************************************************** + * Types + **********************************************************************************************************************/ + +SmlType returns SmlAbstractType + : SmlPrimaryType =>({SmlMemberType.receiver=current} '.' member=SmlNamedType)* + ; + +SmlPrimaryType returns SmlAbstractType + : SmlCallableType + | SmlNamedType + | SmlUnionType + | SmlParenthesizedType + ; + +SmlParenthesizedType + : '(' type=SmlType ')' + ; + +// Callable Types ------------------------------------------------------------------------------------------------------ + +SmlCallableType + : parameterList=SmlParameterList + resultList=SmlResultList + ; + + +// Named Types --------------------------------------------------------------------------------------------------------- + +SmlNamedType + : declaration=[SmlAbstractNamedTypeDeclaration] + typeArgumentList=SmlTypeArgumentList? + (nullable?='?')? + ; + + +// Union Types --------------------------------------------------------------------------------------------------------- + +SmlUnionType + : 'union' typeArgumentList=SmlUnionTypeArgumentList + ; + +SmlUnionTypeArgumentList returns SmlTypeArgumentList + : {SmlTypeArgumentList} ('<' (typeArguments+=SmlUnionTypeArgument (',' typeArguments+=SmlUnionTypeArgument)* ','?)? '>') + ; + +SmlUnionTypeArgument returns SmlTypeArgument + : value=SmlUnionTypeArgumentValue + ; + +SmlUnionTypeArgumentValue returns SmlAbstractTypeArgumentValue + : {SmlTypeProjection} type=SmlType + ; + + +// Generics ------------------------------------------------------------------------------------------------------------ + +SmlTypeParameterList + : {SmlTypeParameterList} ('<' (typeParameters+=SmlTypeParameter (',' typeParameters+=SmlTypeParameter)* ','?)? '>') + ; + +SmlTypeParameter + : annotationCalls+=SmlAnnotationCall* + variance=SmlTypeParameterVariance? + name=ID + ; + +SmlTypeParameterVariance + : 'in' + | 'out' + ; + +SmlConstraintList + : 'where' constraints+=SmlTypeParameterConstraint (',' constraints+=SmlTypeParameterConstraint)* ','? + ; + +SmlTypeParameterConstraint + : leftOperand=[SmlTypeParameter] operator=SmlTypeParameterConstraintOperator rightOperand=SmlType + ; + +SmlTypeParameterConstraintOperator + : 'sub' + | 'super' + ; + +SmlTypeArgumentList + : {SmlTypeArgumentList} '<' (typeArguments+=SmlTypeArgument (',' typeArguments+=SmlTypeArgument)* ','?)? '>' + ; + +SmlTypeArgument + : (typeParameter=[SmlTypeParameter] '=')? value=SmlTypeArgumentValue + ; + +SmlTypeArgumentValue returns SmlAbstractTypeArgumentValue + : {SmlStarProjection} '*' + | {SmlTypeProjection} variance=SmlTypeParameterVariance? type=SmlType + ; + + + +/********************************************************************************************************************** + * Terminals + **********************************************************************************************************************/ + +terminal FLOAT returns ecore::EDouble + : DECIMAL_DIGIT+ '.' DECIMAL_DIGIT+ FLOAT_EXPONENT? + | DECIMAL_DIGIT+ FLOAT_EXPONENT + ; + +terminal fragment DECIMAL_DIGIT + : '0'..'9' + ; + +terminal fragment FLOAT_EXPONENT + : ('e'|'E') ('+'|'-')? DECIMAL_DIGIT+ + ; + +terminal ID + : IDENTIFIER + | '`' IDENTIFIER '`' + ; + +terminal fragment IDENTIFIER + : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')* + ; + +terminal INT returns ecore::EInt + : DECIMAL_DIGIT+ + ; + +terminal ML_COMMENT + : '/*' -> '*/' + ; + +terminal SL_COMMENT + : '//' !('\r'|'\n')* ('\r'? '\n')? + ; + +terminal STRING returns ecore::EString + : STRING_START + STRING_TEXT* + STRING_END + ; + +terminal fragment STRING_START + : STRING_DELIMITER + ; + +terminal fragment STRING_END + : '{'? STRING_DELIMITER + ; + +terminal fragment STRING_DELIMITER + : '"' + ; + +terminal fragment STRING_TEXT + : '{'? ESCAPE_SEQUENCE + | '{'? !('\\'|STRING_DELIMITER|'{') + ; + +terminal fragment ESCAPE_SEQUENCE + : '\\' . /* 'b'|'t'|'n'|'f'|'r'|'u'|'"'|"'"|'\\'|'{' */ + ; + +terminal fragment TEMPLATE_EXPRESSION_START + : '{{' + ; + +terminal fragment TEMPLATE_EXPRESSION_END + : '}}' + ; + +terminal TEMPLATE_STRING_START + : STRING_START + STRING_TEXT* + TEMPLATE_EXPRESSION_START + ; + +terminal TEMPLATE_STRING_INNER + : TEMPLATE_EXPRESSION_END + STRING_TEXT* + TEMPLATE_EXPRESSION_START + ; + +terminal TEMPLATE_STRING_END + : TEMPLATE_EXPRESSION_END + STRING_TEXT* + STRING_END + ; + +terminal TEST_MARKER + : '»' + | '«' + ; + +terminal WS + : (' '|'\t'|'\r'|'\n')+ + ; diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/SimpleMLRuntimeModule.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/SimpleMLRuntimeModule.kt new file mode 100644 index 000000000..b799b60f1 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/SimpleMLRuntimeModule.kt @@ -0,0 +1,78 @@ +package de.unibonn.simpleml + +import com.google.inject.Binder +import com.google.inject.name.Names +import de.unibonn.simpleml.conversion.SimpleMLIDValueConverter +import de.unibonn.simpleml.conversion.SimpleMLQualifiedNameValueConverter +import de.unibonn.simpleml.conversion.SimpleMLSTRINGValueConverter +import de.unibonn.simpleml.conversion.SimpleMLValueConverterService +import de.unibonn.simpleml.naming.QualifiedNameProviderInjectionTarget +import de.unibonn.simpleml.scoping.IndexExtensionsInjectionTarget +import de.unibonn.simpleml.scoping.SimpleMLImportedNamespaceAwareLocalScopeProvider +import de.unibonn.simpleml.scoping.SimpleMLResourceDescriptionStrategy +import de.unibonn.simpleml.serializer.SerializerExtensionsInjectionTarget +import de.unibonn.simpleml.serializer.SimpleMLCrossReferenceSerializer +import de.unibonn.simpleml.serializer.SimpleMLHiddenTokenSequencer +import de.unibonn.simpleml.services.SimpleMLGrammarAccess +import org.eclipse.xtext.IGrammarAccess +import org.eclipse.xtext.conversion.IValueConverterService +import org.eclipse.xtext.conversion.impl.IDValueConverter +import org.eclipse.xtext.conversion.impl.QualifiedNameValueConverter +import org.eclipse.xtext.conversion.impl.STRINGValueConverter +import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy +import org.eclipse.xtext.scoping.IScopeProvider +import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider +import org.eclipse.xtext.serializer.sequencer.IHiddenTokenSequencer +import org.eclipse.xtext.serializer.tokens.ICrossReferenceSerializer + +/** + * Use this class to register components to be used at runtime / without the Equinox extension registry. + */ +@Suppress("unused") +open class SimpleMLRuntimeModule : AbstractSimpleMLRuntimeModule() { + fun bindICrossReferenceSerializer(): Class { + return SimpleMLCrossReferenceSerializer::class.java + } + + fun bindIDefaultResourceDescriptionStrategy(): Class { + return SimpleMLResourceDescriptionStrategy::class.java + } + + override fun bindIGrammarAccess(): Class { + return SimpleMLGrammarAccess::class.java + } + + fun bindIHiddenTokenSequencer(): Class { + return SimpleMLHiddenTokenSequencer::class.java + } + + override fun bindIValueConverterService(): Class { + return SimpleMLValueConverterService::class.java + } + + fun bindIDValueConverter(): Class { + return SimpleMLIDValueConverter::class.java + } + + fun bindSTRINGValueConverter(): Class { + return SimpleMLSTRINGValueConverter::class.java + } + + fun bindQualifiedNameValueConverter(): Class { + return SimpleMLQualifiedNameValueConverter::class.java + } + + override fun configureIScopeProviderDelegate(binder: Binder) { + binder.bind(IScopeProvider::class.java) + .annotatedWith(Names.named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE)) + .to(SimpleMLImportedNamespaceAwareLocalScopeProvider::class.java) + } + + override fun configure(binder: Binder) { + binder.requestStaticInjection(IndexExtensionsInjectionTarget::class.java) + binder.requestStaticInjection(SerializerExtensionsInjectionTarget::class.java) + binder.requestStaticInjection(QualifiedNameProviderInjectionTarget::class.java) + + super.configure(binder) + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/SimpleMLStandaloneSetup.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/SimpleMLStandaloneSetup.kt new file mode 100644 index 000000000..2c419274c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/SimpleMLStandaloneSetup.kt @@ -0,0 +1,23 @@ +package de.unibonn.simpleml + +import com.google.inject.Injector +import de.unibonn.simpleml.simpleML.SimpleMLPackage +import org.eclipse.emf.ecore.EPackage + +/** + * Initialization support for running Xtext languages without Equinox extension registry. + */ +@Suppress("unused") +open class SimpleMLStandaloneSetup : SimpleMLStandaloneSetupGenerated() { + + override fun register(injector: Injector) { + EPackage.Registry.INSTANCE.putIfAbsent(SimpleMLPackage.eNS_URI, SimpleMLPackage.eINSTANCE) + super.register(injector) + } + + companion object { + fun doSetup() { + SimpleMLStandaloneSetup().createInjectorAndDoEMFRegistration() + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlFileExtension.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlFileExtension.kt new file mode 100644 index 000000000..4895f290c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlFileExtension.kt @@ -0,0 +1,84 @@ +package de.unibonn.simpleml.constant + +import de.unibonn.simpleml.emf.OriginalFilePath +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.resource.Resource + +/** + * Different file extensions associated with Simple-ML programs. The dot that separates file name and file extension is + * not included. + */ +enum class SmlFileExtension(val extension: String) { + + /** + * Marks the file as a workflow file, which can be executed by our runtime component. + * + * @see isInFlowFile + * @see isFlowFile + */ + Flow("smlflow"), + + /** + * Marks the file as a stub file, which describes an external API. + * + * @see isInStubFile + * @see isStubFile + */ + Stub("smlstub"), + + /** + * Marks the file as a test file, which disables some checks to simplify its use as input of test cases. This file + * type is only used by language developers. + * + * @see isInTestFile + * @see isTestFile + */ + Test("smltest"); + + override fun toString(): String { + return extension + } +} + +/** + * Returns whether the object is contained in flow file. + */ +fun EObject.isInFlowFile() = this.eResource().isFlowFile() + +/** + * Returns whether the object is contained in stub file. + */ +fun EObject.isInStubFile() = this.eResource().isStubFile() + +/** + * Returns whether the object is contained in test file. + */ +fun EObject.isInTestFile() = this.eResource().isTestFile() + +/** + * Returns whether the resource represents a flow file. + */ +fun Resource.isFlowFile() = this.hasExtension(SmlFileExtension.Flow) + +/** + * Returns whether the resource represents a stub file. + */ +fun Resource.isStubFile() = this.hasExtension(SmlFileExtension.Stub) + +/** + * Returns whether the resource represents a test file. + */ +fun Resource.isTestFile() = this.hasExtension(SmlFileExtension.Test) + +/** + * Returns whether the resource represents a file with the given extension. + */ +private fun Resource.hasExtension(fileExtension: SmlFileExtension): Boolean { + + // The original file path is normally lost for dynamic tests, so it's attached as an EMF adapter + this.eAdapters().filterIsInstance().firstOrNull()?.let { + return it.path.endsWith(".$fileExtension") + } + + return this.uri.toString().endsWith(".$fileExtension") +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlInfixOperationOperator.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlInfixOperationOperator.kt new file mode 100644 index 000000000..ea71ff31b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlInfixOperationOperator.kt @@ -0,0 +1,86 @@ +package de.unibonn.simpleml.constant + +import de.unibonn.simpleml.simpleML.SmlInfixOperation + +/** + * The possible operators for an [SmlInfixOperation]. + */ +enum class SmlInfixOperationOperator(val operator: String) { + + /** + * Disjunction. + */ + Or("or"), + + /** + * Conjunction. + */ + And("and"), + + /** + * Structural equality. + */ + Equals("=="), + + /** + * Negated structural equality. + */ + NotEquals("!="), + + /** + * Both operands point to exactly the same object (referential equality). + */ + IdenticalTo("==="), + + /** + * The two operands point to different objects (negated referential equality). + */ + NotIdenticalTo("!=="), + + LessThan("<"), + + LessThanOrEquals("<="), + + GreaterThanOrEquals(">="), + + GreaterThan(">"), + + /** + * Addition. + */ + Plus("+"), + + /** + * Subtraction. + */ + Minus("-"), + + /** + * Multiplication. + */ + Times("*"), + + /** + * Division. + */ + By("/"), + + /** + * Returns the left operand unless it is null, in which case the right operand is returned. + */ + Elvis("?:"); + + override fun toString(): String { + return operator + } +} + +/** + * Returns the [SmlInfixOperationOperator] of this [SmlInfixOperation]. + * + * @throws IllegalArgumentException If the operator is unknown. + */ +fun SmlInfixOperation.operator(): SmlInfixOperationOperator { + return SmlInfixOperationOperator.values().firstOrNull { it.operator == this.operator } + ?: throw IllegalArgumentException("Unknown infix operator '$operator'.") +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlPrefixOperationOperator.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlPrefixOperationOperator.kt new file mode 100644 index 000000000..9069fdb70 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlPrefixOperationOperator.kt @@ -0,0 +1,33 @@ +package de.unibonn.simpleml.constant + +import de.unibonn.simpleml.simpleML.SmlPrefixOperation + +/** + * The possible operators for an [SmlPrefixOperation]. + */ +enum class SmlPrefixOperationOperator(val operator: String) { + + /** + * Logical negation. + */ + Not("not"), + + /** + * Arithmetic negation. + */ + Minus("-"); + + override fun toString(): String { + return operator + } +} + +/** + * Returns the [SmlPrefixOperationOperator] of this [SmlPrefixOperation]. + * + * @throws IllegalArgumentException If the operator is unknown. + */ +fun SmlPrefixOperation.operator(): SmlPrefixOperationOperator { + return SmlPrefixOperationOperator.values().firstOrNull { it.operator == this.operator } + ?: throw IllegalArgumentException("Unknown prefix operator '$operator'.") +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlProtocolQuantifiedTermQuantifier.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlProtocolQuantifiedTermQuantifier.kt new file mode 100644 index 000000000..8506d520a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlProtocolQuantifiedTermQuantifier.kt @@ -0,0 +1,26 @@ +package de.unibonn.simpleml.constant + +import de.unibonn.simpleml.simpleML.SmlProtocolQuantifiedTerm + +/** + * The possible quantifiers for an [SmlProtocolQuantifiedTerm]. + */ +enum class SmlProtocolQuantifiedTermQuantifier(val quantifier: String) { + ZeroOrOne("?"), + ZeroOrMore("*"), + OneOrMore("+"); + + override fun toString(): String { + return quantifier + } +} + +/** + * Returns the [SmlProtocolQuantifiedTermQuantifier] of this [SmlProtocolQuantifiedTerm]. + * + * @throws IllegalArgumentException If the quantifier is unknown. + */ +fun SmlProtocolQuantifiedTerm.quantifier(): SmlProtocolQuantifiedTermQuantifier { + return SmlProtocolQuantifiedTermQuantifier.values().firstOrNull { it.quantifier == this.quantifier } + ?: throw IllegalArgumentException("Unknown quantified term quantifier '$quantifier'.") +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlProtocolTokenClassValue.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlProtocolTokenClassValue.kt new file mode 100644 index 000000000..610ce736a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlProtocolTokenClassValue.kt @@ -0,0 +1,38 @@ +package de.unibonn.simpleml.constant + +import de.unibonn.simpleml.simpleML.SmlProtocolTokenClass + +/** + * The possible values for an [SmlProtocolTokenClass]. + */ +enum class SmlProtocolTokenClassValue(val value: String) { + + /** + * Matches any attribute or function. + */ + Anything("."), + + /** + * Matches any attribute. + */ + AnyAttribute("\\a"), + + /** + * Matches any function. + */ + AnyFunction("\\f"); + + override fun toString(): String { + return value + } +} + +/** + * Returns the [SmlProtocolTokenClassValue] of this [SmlProtocolTokenClass]. + * + * @throws IllegalArgumentException If the value is unknown. + */ +fun SmlProtocolTokenClass.value(): SmlProtocolTokenClassValue { + return SmlProtocolTokenClassValue.values().firstOrNull { it.value == this.value } + ?: throw IllegalArgumentException("Unknown token class value value '$value'.") +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlTypeParameterConstraintOperator.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlTypeParameterConstraintOperator.kt new file mode 100644 index 000000000..95e3d0cf9 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlTypeParameterConstraintOperator.kt @@ -0,0 +1,35 @@ +package de.unibonn.simpleml.constant + +import de.unibonn.simpleml.simpleML.SmlTypeParameterConstraint + +/** + * The possible operators for an [SmlTypeParameterConstraint]. + */ +enum class SmlTypeParameterConstraintOperator(val operator: String) { + + /** + * Left operand is a subclass of the right operand. Each class is a subclass of itself for the purpose of this + * operator. + */ + SubclassOf("sub"), + + /** + * Left operand is a superclass of the right operand. Each class is a superclass of itself for the purpose of this + * operator. + */ + SuperclassOf("super"); + + override fun toString(): String { + return operator + } +} + +/** + * Returns the [SmlTypeParameterConstraintOperator] of this [SmlTypeParameterConstraint]. + * + * @throws IllegalArgumentException If the operator is unknown. + */ +fun SmlTypeParameterConstraint.operator(): SmlTypeParameterConstraintOperator { + return SmlTypeParameterConstraintOperator.values().firstOrNull { it.operator == this.operator } + ?: throw IllegalArgumentException("Unknown type parameter constraint operator '$operator'.") +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlVariance.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlVariance.kt new file mode 100644 index 000000000..acd7b69ff --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlVariance.kt @@ -0,0 +1,68 @@ +package de.unibonn.simpleml.constant + +import de.unibonn.simpleml.simpleML.SmlTypeParameter +import de.unibonn.simpleml.simpleML.SmlTypeProjection + +/** + * The possible variances for an [SmlTypeParameter] or [SmlTypeProjection]. + */ +enum class SmlVariance(val variance: String?) { + + /** + * A complex type `G` is invariant if it is neither covariant nor contravariant. + * + * **Example:** A `Transformer` reads and writes values of type `T`. This means we cannot use a + * `Transformer` if we want a `Transformer`, since it cannot deal with `Double` values as input. + * Likewise, we cannot use a `Transformer` if we want a `Transformer`, since it might create `Double` + * values as output. + */ + Invariant(null), + + /** + * A complex type `G` is covariant if `A <= B` implies `G <= G`. The ordering of types is preserved. + * + * **Positive example:** A `Producer` only ever writes values of type `T` values and never reads them. This means + * we can use a `Producer`, where a `Producer` is expected, since `Ints` are just special `Numbers` + * (`Int <= Number`). + * + * **Negative example:** A `Transformer` reads and writes values of type `T`. This means we cannot use a + * `Transformer` if we want a `Transformer`, since it cannot deal with `Double` values as input. + */ + Covariant("out"), + + /** + * A complex type `G` is covariant if `A <= B` implies `G <= G`. The ordering of types is inverted. + * + * **Positive example:** A `Consumer` only ever reads values of type `T` values and never writes them. This means + * we can use a `Consumer`, where a `Consumer` is expected, since it is able to deal with integers + * (`Int <= Number`). + * + * **Negative example:** A `Transformer` reads and writes values of type `T`. This means we cannot use a + * `Transformer` if we want a `Transformer`, since it might create `Double` values as output. + */ + Contravariant("in"); + + override fun toString(): String { + return name + } +} + +/** + * Returns the [SmlVariance] of this [SmlTypeParameter]. + * + * @throws IllegalArgumentException If the variance is unknown. + */ +fun SmlTypeParameter.variance(): SmlVariance { + return SmlVariance.values().firstOrNull { it.variance == this.variance } + ?: throw IllegalArgumentException("Unknown variance '$variance'.") +} + +/** + * Returns the [SmlVariance] of this [SmlTypeProjection]. + * + * @throws IllegalArgumentException If the variance is unknown. + */ +fun SmlTypeProjection.variance(): SmlVariance { + return SmlVariance.values().firstOrNull { it.variance == this.variance } + ?: throw IllegalArgumentException("Unknown variance '$variance'.") +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlVisibility.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlVisibility.kt new file mode 100644 index 000000000..f2e65ba86 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant/SmlVisibility.kt @@ -0,0 +1,38 @@ +package de.unibonn.simpleml.constant + +import de.unibonn.simpleml.simpleML.SmlStep + +/** + * The possible visibilities of an [SmlStep]. + */ +enum class SmlVisibility(val visibility: String?) { + + /** + * The [SmlStep] is visible everywhere. + */ + Public(null), + + /** + * The [SmlStep] is only visible in the same package. + */ + Internal("internal"), + + /** + * The [SmlStep] is only visible in the same file. + */ + Private("private"); + + override fun toString(): String { + return name + } +} + +/** + * Returns the [SmlVisibility] of this [SmlStep]. + * + * @throws IllegalArgumentException If the visibility is unknown. + */ +fun SmlStep.visibility(): SmlVisibility { + return SmlVisibility.values().firstOrNull { it.visibility == this.visibility } + ?: throw IllegalArgumentException("Unknown visibility '$visibility'.") +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/AbstractSimpleMLStringValueConverter.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/AbstractSimpleMLStringValueConverter.kt new file mode 100644 index 000000000..ae14fda84 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/AbstractSimpleMLStringValueConverter.kt @@ -0,0 +1,150 @@ +package de.unibonn.simpleml.conversion + +import com.google.inject.Singleton +import org.eclipse.xtext.conversion.ValueConverterException +import org.eclipse.xtext.conversion.ValueConverterWithValueException +import org.eclipse.xtext.conversion.impl.STRINGValueConverter +import org.eclipse.xtext.nodemodel.INode + +/** + * Handles the conversion between the textual representation of a string (including delimiters and escape sequences) to + * and its actual value. + * + * Example: The string `"myString \{"` in a DSL program has the value `myString {`. + */ +@Singleton +abstract class AbstractSimpleMLStringValueConverter( + private val startDelimiter: String, + private val endDelimiter: String +) : STRINGValueConverter() { + + /** + * Lazily initializes a single converter implementation. + */ + private val converter by lazy { + Implementation() + } + + /** + * Converts the value to its textual representation by adding delimiters and escaping characters. + */ + override fun toEscapedString(value: String?): String { + return startDelimiter + converter.convertToJavaString(value, false) + endDelimiter + } + + /** + * Converts the textual representation to its value by removing delimiters and unescaping characters. + */ + override fun toValue(string: String?, node: INode?): String? { + if (string == null) { + return null + } + + return try { + if (string.length < startDelimiter.length + endDelimiter.length) { + throw ValueConverterWithValueException(stringNotClosedMessage, node, "", null) + } + convertFromString(string, node) + } catch (e: IllegalArgumentException) { + throw ValueConverterException(e.message, node, e) + } + } + + /** + * Creates the implementation of the converter to unescape characters. This method is **not** called for escaping + * by the superclass, so we need to override [toEscapedString]. + */ + override fun createConverter(): STRINGValueConverter.Implementation { + return converter + } + + /** + * The implementation of the converter to escape/unescape characters. + */ + private inner class Implementation : STRINGValueConverter.Implementation() { + + /** + * Converts the textual representation to its value by removing delimiters and unescaping characters. + */ + override fun convertFromJavaString(literal: String): String? { + val valueBetweenDelimiters = literal.substring(startDelimiter.length, literal.length - endDelimiter.length) + + return if (!valueBetweenDelimiters.contains('\\')) { + valueBetweenDelimiters + } else { + convertFromJavaString( + "\"$valueBetweenDelimiters\"", + true, + 1, + StringBuilder(literal.length) + ) + } + } + + /** + * Escapes the character and adds it to the builder. This is necessary for serialization. + */ + override fun escapeAndAppendTo(c: Char, useUnicode: Boolean, result: StringBuilder) { + val appendMe: String + when (c) { + '\b' -> appendMe = "\\b" + '\t' -> appendMe = "\\t" + '\n' -> appendMe = "\\n" + '\u000c' -> appendMe = "\\u000c" + '\r' -> appendMe = "\\r" + '"' -> appendMe = "\\\"" + // Don't escape "'" + '\\' -> appendMe = "\\\\" + '{' -> appendMe = "\\{" // Necessary due to string templates + else -> { + if (useUnicode && mustEncodeAsEscapeSequence(c)) { + result.append("\\u") + var i = 12 + while (i >= 0) { + result.append(toHex(c.code shr i and 0xF)) + i -= 4 + } + } else { + result.append(c) + } + return + } + } + result.append(appendMe) + } + + /** + * Unescapes the character and adds it to the builder. This method defines which escape sequences are legal. + */ + override fun doUnescapeCharAndAppendTo( + string: String, + useUnicode: Boolean, + index: Int, + result: StringBuilder + ): Int { + var c = string[index] + when (c) { + 'b' -> c = '\b' + 't' -> c = '\t' + 'n' -> c = '\n' + 'f' -> c = '\u000c' + 'r' -> c = '\r' + '"', '\'', '\\', '{' -> {} // '{' necessary due to string templates + 'u' -> return when { + useUnicode -> unescapeUnicodeSequence(string, index + 1, result) + else -> handleUnknownEscapeSequence(string, c, false, index + 1, result) + } + else -> return handleUnknownEscapeSequence(string, c, useUnicode, index + 1, result) + } + validateAndAppendChar(c, result) + return index + 1 + } + } + + /** + * Error message that is shown if an invalid escape sequence is encountered. + */ + override fun getInvalidEscapeSequenceMessage(): String { + return "Invalid escape sequence (valid ones are \\b \\t \\n \\f \\r \\\" \\' \\\\ \\{ )." + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLIDValueConverter.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLIDValueConverter.kt new file mode 100644 index 000000000..17089bf7c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLIDValueConverter.kt @@ -0,0 +1,54 @@ +package de.unibonn.simpleml.conversion + +import com.google.inject.Singleton +import org.eclipse.xtext.GrammarUtil +import org.eclipse.xtext.conversion.impl.IDValueConverter +import org.eclipse.xtext.nodemodel.INode + +/** + * Handles the conversion between the textual representation of an ID (including the escaping of keywords) and its + * actual value. + * + * __Example__: The ID ``` `fun` ``` in a DSL program has the value `fun`. + * + * __Note__: This converter is not called for qualified names since this is a data type rule and requires another + * converter. See the Eclipse forum for more information: [https://www.eclipse.org/forums/index.php/t/1088504/]. + */ +@Singleton +class SimpleMLIDValueConverter : IDValueConverter() { + + /** + * Syntax of valid identifiers. + */ + private val identifier = Regex("[a-zA-Z_][a-zA-Z_0-9]*") + + /** + * Adds surrounding backticks as necessary. + */ + override fun toEscapedString(value: String): String { + return if (mustEscape(value)) "`$value`" else value + } + + /** + * Removes surrounding backticks. + */ + override fun toValue(string: String?, node: INode?): String? { + return string?.removeSurrounding("`") + } + + /** + * Keywords that could be valid identifiers, e.g. `fun`. + */ + private val textualKeywords: Set by lazy { + GrammarUtil.getAllKeywords(grammarAccess.grammar) + .filter { it.matches(identifier) } + .toSet() + } + + /** + * Checks whether the identifier must be escaped because it clashes with a keyword. + */ + override fun mustEscape(value: String): Boolean { + return value in textualKeywords + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLQualifiedNameValueConverter.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLQualifiedNameValueConverter.kt new file mode 100644 index 000000000..d9958ccd4 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLQualifiedNameValueConverter.kt @@ -0,0 +1,7 @@ +package de.unibonn.simpleml.conversion + +import com.google.inject.Singleton +import org.eclipse.xtext.conversion.impl.QualifiedNameValueConverter + +@Singleton +class SimpleMLQualifiedNameValueConverter : QualifiedNameValueConverter() diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLSTRINGValueConverter.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLSTRINGValueConverter.kt new file mode 100644 index 000000000..53157f907 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLSTRINGValueConverter.kt @@ -0,0 +1,12 @@ +package de.unibonn.simpleml.conversion + +import com.google.inject.Singleton + +/** + * Handles the conversion between the textual representation of a string (including delimiters and escape sequences) to + * its actual value. + * + * Example: The string `"myString \{"` in a DSL program has the value `myString {`. + */ +@Singleton +class SimpleMLSTRINGValueConverter : AbstractSimpleMLStringValueConverter("\"", "\"") diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_ENDValueConverter.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_ENDValueConverter.kt new file mode 100644 index 000000000..a122fccdc --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_ENDValueConverter.kt @@ -0,0 +1,14 @@ +@file:Suppress("ClassName") + +package de.unibonn.simpleml.conversion + +import com.google.inject.Singleton + +/** + * Handles the conversion between the textual representation of a template string end (including delimiters and escape + * sequences) to its actual value. + * + * Example: The template string end `}}end"` in a DSL program has the value `end`. + */ +@Singleton +class SimpleMLTEMPLATE_STRING_ENDValueConverter : AbstractSimpleMLStringValueConverter("}}", "\"") diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_INNERValueConverter.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_INNERValueConverter.kt new file mode 100644 index 000000000..720b21319 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_INNERValueConverter.kt @@ -0,0 +1,14 @@ +@file:Suppress("ClassName") + +package de.unibonn.simpleml.conversion + +import com.google.inject.Singleton + +/** + * Handles the conversion between the textual representation of a template string inner part (including delimiters and + * escape sequences) to its actual value. + * + * Example: The template string inner part `}}inner{{` in a DSL program has the value `inner`. + */ +@Singleton +class SimpleMLTEMPLATE_STRING_INNERValueConverter : AbstractSimpleMLStringValueConverter("}}", "{{") diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_STARTValueConverter.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_STARTValueConverter.kt new file mode 100644 index 000000000..130cc4695 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_STARTValueConverter.kt @@ -0,0 +1,14 @@ +@file:Suppress("ClassName") + +package de.unibonn.simpleml.conversion + +import com.google.inject.Singleton + +/** + * Handles the conversion between the textual representation of a template string start (including delimiters and escape + * sequences) to its actual value. + * + * Example: The template string start `"start{{` in a DSL program has the value `start`. + */ +@Singleton +class SimpleMLTEMPLATE_STRING_STARTValueConverter : AbstractSimpleMLStringValueConverter("\"", "{{") diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLValueConverterService.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLValueConverterService.kt new file mode 100644 index 000000000..8b8a41e77 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion/SimpleMLValueConverterService.kt @@ -0,0 +1,67 @@ +@file:Suppress("FunctionName") + +package de.unibonn.simpleml.conversion + +import com.google.inject.Inject +import com.google.inject.Singleton +import org.eclipse.xtext.conversion.IValueConverter +import org.eclipse.xtext.conversion.ValueConverter +import org.eclipse.xtext.conversion.impl.AbstractDeclarativeValueConverterService +import org.eclipse.xtext.conversion.impl.IDValueConverter +import org.eclipse.xtext.conversion.impl.INTValueConverter +import org.eclipse.xtext.conversion.impl.QualifiedNameValueConverter +import org.eclipse.xtext.conversion.impl.STRINGValueConverter + +/** + * Converters for ID, INT, and STRING. + */ +@Singleton +open class SimpleMLValueConverterService : AbstractDeclarativeValueConverterService() { + + @Inject + private lateinit var idValueConverter: IDValueConverter + + @ValueConverter(rule = "ID") + fun ID() = idValueConverter + + @Inject + private lateinit var intValueConverter: INTValueConverter + + @ValueConverter(rule = "INT") + fun INT(): IValueConverter { + return intValueConverter + } + + @Inject + private lateinit var stringValueConverter: STRINGValueConverter + + @ValueConverter(rule = "STRING") + fun STRING() = stringValueConverter + + @Inject + private lateinit var templateStringStartValueConverter: SimpleMLTEMPLATE_STRING_STARTValueConverter + + @ValueConverter(rule = "TEMPLATE_STRING_START") + fun TEMPLATE_STRING_START() = templateStringStartValueConverter + + @Inject + private lateinit var templateStringInnerValueConverter: SimpleMLTEMPLATE_STRING_INNERValueConverter + + @ValueConverter(rule = "TEMPLATE_STRING_INNER") + fun TEMPLATE_STRING_INNER() = templateStringInnerValueConverter + + @Inject + private lateinit var templateStringEndValueConverter: SimpleMLTEMPLATE_STRING_ENDValueConverter + + @ValueConverter(rule = "TEMPLATE_STRING_END") + fun TEMPLATE_STRING_END() = templateStringEndValueConverter + + @Inject + private lateinit var qualifiedNameValueConverter: QualifiedNameValueConverter + + @ValueConverter(rule = "QualifiedName") + fun QualifiedName() = qualifiedNameValueConverter + + @ValueConverter(rule = "QualifiedNameWithWildcard") + fun QualifiedNameWithWildcard() = qualifiedNameValueConverter +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/emf/Creators.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/emf/Creators.kt new file mode 100644 index 000000000..c8f5db4cf --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/emf/Creators.kt @@ -0,0 +1,1419 @@ +@file:Suppress("unused") + +package de.unibonn.simpleml.emf + +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.constant.SmlInfixOperationOperator +import de.unibonn.simpleml.constant.SmlPrefixOperationOperator +import de.unibonn.simpleml.constant.SmlProtocolQuantifiedTermQuantifier +import de.unibonn.simpleml.constant.SmlProtocolTokenClassValue +import de.unibonn.simpleml.constant.SmlTypeParameterConstraintOperator +import de.unibonn.simpleml.constant.SmlVariance +import de.unibonn.simpleml.constant.SmlVisibility +import de.unibonn.simpleml.simpleML.SimpleMLFactory +import de.unibonn.simpleml.simpleML.SmlAbstractAssignee +import de.unibonn.simpleml.simpleML.SmlAbstractClassMember +import de.unibonn.simpleml.simpleML.SmlAbstractCompilationUnitMember +import de.unibonn.simpleml.simpleML.SmlAbstractConstraint +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlAbstractExpression +import de.unibonn.simpleml.simpleML.SmlAbstractLambda +import de.unibonn.simpleml.simpleML.SmlAbstractNamedTypeDeclaration +import de.unibonn.simpleml.simpleML.SmlAbstractObject +import de.unibonn.simpleml.simpleML.SmlAbstractProtocolTerm +import de.unibonn.simpleml.simpleML.SmlAbstractProtocolToken +import de.unibonn.simpleml.simpleML.SmlAbstractStatement +import de.unibonn.simpleml.simpleML.SmlAbstractType +import de.unibonn.simpleml.simpleML.SmlAbstractTypeArgumentValue +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.simpleML.SmlAnnotationCall +import de.unibonn.simpleml.simpleML.SmlAnnotationCallList +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlArgumentList +import de.unibonn.simpleml.simpleML.SmlAssigneeList +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.simpleML.SmlBlock +import de.unibonn.simpleml.simpleML.SmlBlockLambda +import de.unibonn.simpleml.simpleML.SmlBlockLambdaResult +import de.unibonn.simpleml.simpleML.SmlBoolean +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlCallableType +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlConstraintList +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlExpressionLambda +import de.unibonn.simpleml.simpleML.SmlExpressionStatement +import de.unibonn.simpleml.simpleML.SmlFloat +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlImport +import de.unibonn.simpleml.simpleML.SmlImportAlias +import de.unibonn.simpleml.simpleML.SmlIndexedAccess +import de.unibonn.simpleml.simpleML.SmlInfixOperation +import de.unibonn.simpleml.simpleML.SmlInt +import de.unibonn.simpleml.simpleML.SmlLambdaParameterList +import de.unibonn.simpleml.simpleML.SmlMemberAccess +import de.unibonn.simpleml.simpleML.SmlMemberType +import de.unibonn.simpleml.simpleML.SmlNamedType +import de.unibonn.simpleml.simpleML.SmlNull +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlParameterList +import de.unibonn.simpleml.simpleML.SmlParentTypeList +import de.unibonn.simpleml.simpleML.SmlParenthesizedExpression +import de.unibonn.simpleml.simpleML.SmlParenthesizedType +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlPrefixOperation +import de.unibonn.simpleml.simpleML.SmlProtocol +import de.unibonn.simpleml.simpleML.SmlProtocolAlternative +import de.unibonn.simpleml.simpleML.SmlProtocolComplement +import de.unibonn.simpleml.simpleML.SmlProtocolParenthesizedTerm +import de.unibonn.simpleml.simpleML.SmlProtocolQuantifiedTerm +import de.unibonn.simpleml.simpleML.SmlProtocolReference +import de.unibonn.simpleml.simpleML.SmlProtocolReferenceList +import de.unibonn.simpleml.simpleML.SmlProtocolSequence +import de.unibonn.simpleml.simpleML.SmlProtocolSubterm +import de.unibonn.simpleml.simpleML.SmlProtocolTokenClass +import de.unibonn.simpleml.simpleML.SmlReference +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlResultList +import de.unibonn.simpleml.simpleML.SmlStarProjection +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlString +import de.unibonn.simpleml.simpleML.SmlTemplateString +import de.unibonn.simpleml.simpleML.SmlTypeArgument +import de.unibonn.simpleml.simpleML.SmlTypeArgumentList +import de.unibonn.simpleml.simpleML.SmlTypeParameter +import de.unibonn.simpleml.simpleML.SmlTypeParameterConstraint +import de.unibonn.simpleml.simpleML.SmlTypeParameterList +import de.unibonn.simpleml.simpleML.SmlTypeProjection +import de.unibonn.simpleml.simpleML.SmlUnionType +import de.unibonn.simpleml.simpleML.SmlWildcard +import de.unibonn.simpleml.simpleML.SmlWorkflow +import de.unibonn.simpleml.simpleML.SmlYield +import de.unibonn.simpleml.utils.nullIfEmptyElse +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.xtext.resource.XtextResource +import kotlin.math.absoluteValue + +private val factory = SimpleMLFactory.eINSTANCE + +/** + * Returns a new [Resource]. + * + * This can be useful to serialize EObjects that were initialized with the creators in this file rather than generated + * by the parser, since serialization requires EObjects to be contained in a resource. + */ +fun createSmlDummyResource( + fileName: String, + fileExtension: SmlFileExtension, + compilationUnit: SmlCompilationUnit +): Resource { + val uri = URI.createURI("dummy:/$fileName.${fileExtension.extension}") + return XtextResource(uri).apply { + this.contents += compilationUnit + } +} + +/** + * Returns a new [Resource]. + * + * This can be useful to serialize EObjects that were initialized with the creators in this file rather than generated + * by the parser, since serialization requires EObjects to be contained in a resource. + */ +fun createSmlDummyResource( + fileName: String, + fileExtension: SmlFileExtension, + packageName: String, + init: SmlCompilationUnit.() -> Unit = {} +): Resource { + val uri = URI.createURI("dummy:/$fileName.${fileExtension.extension}") + return XtextResource(uri).apply { + this.contents += createSmlCompilationUnit( + packageName = packageName, + init = init + ) + } +} + +/** + * Returns a new object of class [SmlAnnotation]. + */ +fun createSmlAnnotation( + name: String, + annotationCalls: List = emptyList(), + parameters: List = emptyList() +): SmlAnnotation { + return factory.createSmlAnnotation().apply { + this.name = name + this.annotationCallList = createSmlAnnotationCallList(annotationCalls) + this.parameterList = parameters.nullIfEmptyElse(::createSmlParameterList) + } +} + +/** + * Adds a new object of class [SmlAnnotation] to the receiver. + */ +fun SmlCompilationUnit.smlAnnotation( + name: String, + annotationCalls: List = emptyList(), + parameters: List = emptyList() +) { + this.addMember(createSmlAnnotation(name, annotationCalls, parameters)) +} + +/** + * Returns a new object of class [SmlAnnotationCall]. + */ +fun createSmlAnnotationCall( + annotation: SmlAnnotation, + arguments: List = emptyList() +): SmlAnnotationCall { + return factory.createSmlAnnotationCall().apply { + this.annotation = annotation + this.argumentList = arguments.nullIfEmptyElse(::createSmlArgumentList) + } +} + +/** + * Returns a new object of class [SmlAnnotationCall] that points to an annotation with the given name. + */ +fun createSmlAnnotationCall( + annotationName: String, + arguments: List = emptyList() +): SmlAnnotationCall { + return createSmlAnnotationCall( + createSmlAnnotation(annotationName), + arguments + ) +} + +/** + * Returns a new object of class [SmlAnnotationCallList]. + */ +private fun createSmlAnnotationCallList(annotationCalls: List): SmlAnnotationCallList { + return factory.createSmlAnnotationCallList().apply { + this.annotationCalls += annotationCalls + } +} + +/** + * Returns a new object of class [SmlArgument]. + */ +fun createSmlArgument(value: SmlAbstractExpression, parameter: SmlParameter? = null): SmlArgument { + return factory.createSmlArgument().apply { + this.value = value + this.parameter = parameter + } +} + +/** + * Returns a new object of class [SmlArgument] that points to a parameter with the given name. + */ +fun createSmlArgument(value: SmlAbstractExpression, parameterName: String): SmlArgument { + return createSmlArgument( + value, + createSmlParameter(parameterName) + ) +} + +/** + * Returns a new object of class [SmlArgumentList]. + */ +fun createSmlArgumentList(arguments: List): SmlArgumentList { + return factory.createSmlArgumentList().apply { + this.arguments += arguments + } +} + +/** + * Returns a new object of class [SmlAssigneeList]. + */ +fun createSmlAssigneeList(assignees: List): SmlAssigneeList { + return factory.createSmlAssigneeList().apply { + this.assignees += assignees + } +} + +/** + * Returns a new object of class [SmlAssignment]. + * + * @throws IllegalArgumentException If no assignees are passed. + */ +fun createSmlAssignment(assignees: List, expression: SmlAbstractExpression): SmlAssignment { + if (assignees.isEmpty()) { + throw IllegalArgumentException("Must have at least one assignee.") + } + + return factory.createSmlAssignment().apply { + this.assigneeList = createSmlAssigneeList(assignees) + this.expression = expression + } +} + +/** + * Adds a new object of class [SmlAssignment] to the receiver. + */ +fun SmlBlockLambda.smlAssignment(assignees: List, expression: SmlAbstractExpression) { + this.addStatement(createSmlAssignment(assignees, expression)) +} + +/** + * Adds a new object of class [SmlAssignment] to the receiver. + */ +fun SmlWorkflow.smlAssignment(assignees: List, expression: SmlAbstractExpression) { + this.addStatement(createSmlAssignment(assignees, expression)) +} + +/** + * Adds a new object of class [SmlAssignment] to the receiver. + */ +fun SmlStep.smlAssignment(assignees: List, expression: SmlAbstractExpression) { + this.addStatement(createSmlAssignment(assignees, expression)) +} + +/** + * Returns a new object of class [SmlAttribute]. + */ +fun createSmlAttribute( + name: String, + annotationCalls: List = emptyList(), + isStatic: Boolean = false, + type: SmlAbstractType? = null +): SmlAttribute { + return factory.createSmlAttribute().apply { + this.name = name + this.annotationCallList = createSmlAnnotationCallList(annotationCalls) + this.isStatic = isStatic + this.type = type + } +} + +/** + * Adds a new object of class [SmlAttribute] to the receiver. + */ +fun SmlClass.smlAttribute( + name: String, + annotationCalls: List = emptyList(), + isStatic: Boolean = false, + type: SmlAbstractType? = null +) { + this.addMember(createSmlAttribute(name, annotationCalls, isStatic, type)) +} + +/** + * Returns a new object of class [SmlBlock]. + */ +fun createSmlBlock( + statements: List = emptyList(), + init: SmlBlock.() -> Unit = {} +): SmlBlock { + return factory.createSmlBlock().apply { + this.statements += statements + this.init() + } +} + +/** + * Returns a new object of class [SmlBlockLambda]. + */ +fun createSmlBlockLambda( + parameters: List = emptyList(), + statements: List = emptyList(), + init: SmlBlockLambda.() -> Unit = {} +): SmlBlockLambda { + return factory.createSmlBlockLambda().apply { + this.parameterList = createSmlLambdaParameterList(parameters) + this.body = factory.createSmlBlock() + statements.forEach { addStatement(it) } + this.init() + } +} + +/** + * Adds a new statement to the receiver. + */ +private fun SmlBlockLambda.addStatement(statement: SmlAbstractStatement) { + if (this.body == null) { + this.body = factory.createSmlBlock() + } + + this.body.statements += statement +} + +/** + * Returns a new object of class [SmlBlockLambdaResult]. + */ +fun createSmlBlockLambdaResult(name: String): SmlBlockLambdaResult { + return factory.createSmlBlockLambdaResult().apply { + this.name = name + } +} + +/** + * Returns a new object of class [SmlBoolean]. + */ +fun createSmlBoolean(value: Boolean): SmlBoolean { + return factory.createSmlBoolean().apply { + this.isTrue = value + } +} + +/** + * Returns a new object of class [SmlCall]. + */ +fun createSmlCall( + receiver: SmlAbstractExpression, + typeArguments: List = emptyList(), + arguments: List = emptyList() +): SmlCall { + return factory.createSmlCall().apply { + this.receiver = receiver + this.typeArgumentList = typeArguments.nullIfEmptyElse(::createSmlTypeArgumentList) + this.argumentList = createSmlArgumentList(arguments) + } +} + +/** + * Returns a new object of class [SmlCallableType]. + */ +fun createSmlCallableType(parameters: List, results: List): SmlCallableType { + return factory.createSmlCallableType().apply { + this.parameterList = createSmlParameterList(parameters) + this.resultList = createSmlResultList(results) + } +} + +/** + * Returns a new object of class [SmlClass]. + */ +fun createSmlClass( + name: String, + annotationCalls: List = emptyList(), + typeParameters: List = emptyList(), + parameters: List? = null, // null and emptyList() are semantically different + parentTypes: List = emptyList(), + constraints: List = emptyList(), + protocol: SmlProtocol? = null, + members: List = emptyList(), + init: SmlClass.() -> Unit = {} +): SmlClass { + return factory.createSmlClass().apply { + this.name = name + this.annotationCallList = createSmlAnnotationCallList(annotationCalls) + this.typeParameterList = typeParameters.nullIfEmptyElse(::createSmlTypeParameterList) + this.parameterList = parameters?.let { createSmlParameterList(it) } + this.parentTypeList = parentTypes.nullIfEmptyElse(::createSmlParentTypeList) + this.constraintList = constraints.nullIfEmptyElse(::createSmlConstraintList) + protocol?.let { addMember(it) } + members.forEach { addMember(it) } + this.init() + } +} + +/** + * Adds a new object of class [SmlClass] to the receiver. + */ +fun SmlClass.smlClass( + name: String, + annotationCalls: List = emptyList(), + typeParameters: List = emptyList(), + parameters: List? = null, + parentTypes: List = emptyList(), + constraints: List = emptyList(), + protocol: SmlProtocol? = null, + members: List = emptyList(), + init: SmlClass.() -> Unit = {} +) { + this.addMember( + createSmlClass( + name, + annotationCalls, + typeParameters, + parameters, + parentTypes, + constraints, + protocol, + members, + init + ) + ) +} + +/** + * Adds a new object of class [SmlClass] to the receiver. + */ +fun SmlCompilationUnit.smlClass( + name: String, + annotationCalls: List = emptyList(), + typeParameters: List = emptyList(), + parameters: List? = null, + parentTypes: List = emptyList(), + constraints: List = emptyList(), + protocol: SmlProtocol? = null, + members: List = emptyList(), + init: SmlClass.() -> Unit = {} +) { + this.addMember( + createSmlClass( + name, + annotationCalls, + typeParameters, + parameters, + parentTypes, + constraints, + protocol, + members, + init + ) + ) +} + +/** + * Adds a new member to the receiver. + */ +private fun SmlClass.addMember(member: SmlAbstractObject) { + if (this.body == null) { + this.body = factory.createSmlClassBody() + } + + this.body.members += member +} + +/** + * Returns a new object of class [SmlCompilationUnit]. + */ +fun createSmlCompilationUnit( + packageName: String, + annotationCalls: List = emptyList(), + imports: List = emptyList(), + members: List = emptyList(), + init: SmlCompilationUnit.() -> Unit = {} +): SmlCompilationUnit { + return factory.createSmlCompilationUnit().apply { + this.name = packageName + this.annotationCalls += annotationCalls + this.imports += imports + members.forEach { addMember(it) } + init() + } +} + +/** + * Adds a new member to the receiver. + */ +private fun SmlCompilationUnit.addMember(member: SmlAbstractCompilationUnitMember) { + this.members += member +} + +/** + * Returns a new object of class [SmlConstraintList]. + */ +fun createSmlConstraintList(constraints: List): SmlConstraintList { + return factory.createSmlConstraintList().apply { + this.constraints += constraints + } +} + +/** + * Returns a new object of class [SmlEnum]. + */ +fun createSmlEnum( + name: String, + annotationCalls: List = emptyList(), + variants: List = emptyList(), + init: SmlEnum.() -> Unit = {} +): SmlEnum { + return factory.createSmlEnum().apply { + this.name = name + this.annotationCallList = createSmlAnnotationCallList(annotationCalls) + variants.forEach { addVariant(it) } + this.init() + } +} + +/** + * Adds a new object of class [SmlEnum] to the receiver. + */ +fun SmlClass.smlEnum( + name: String, + annotationCalls: List = emptyList(), + variants: List = emptyList(), + init: SmlEnum.() -> Unit = {} +) { + this.addMember(createSmlEnum(name, annotationCalls, variants, init)) +} + +/** + * Adds a new object of class [SmlEnum] to the receiver. + */ +fun SmlCompilationUnit.smlEnum( + name: String, + annotationCalls: List = emptyList(), + variants: List = emptyList(), + init: SmlEnum.() -> Unit = {} +) { + this.addMember(createSmlEnum(name, annotationCalls, variants, init)) +} + +/** + * Adds a new variant to the receiver. + */ +private fun SmlEnum.addVariant(variant: SmlEnumVariant) { + if (this.body == null) { + this.body = factory.createSmlEnumBody() + } + + this.body.variants += variant +} + +/** + * Returns a new object of class [SmlEnumVariant]. + */ +fun createSmlEnumVariant( + name: String, + annotationCalls: List = emptyList(), + typeParameters: List = emptyList(), + parameters: List = emptyList(), + constraints: List = emptyList() +): SmlEnumVariant { + return factory.createSmlEnumVariant().apply { + this.name = name + this.annotationCalls += annotationCalls + this.typeParameterList = typeParameters.nullIfEmptyElse(::createSmlTypeParameterList) + this.parameterList = parameters.nullIfEmptyElse(::createSmlParameterList) + this.constraintList = constraints.nullIfEmptyElse(::createSmlConstraintList) + } +} + +/** + * Adds a new object of class [SmlEnumVariant] to the receiver. + */ +fun SmlEnum.smlEnumVariant( + name: String, + annotationCalls: List = emptyList(), + typeParameters: List = emptyList(), + parameters: List = emptyList(), + constraints: List = emptyList() +) { + this.addVariant(createSmlEnumVariant(name, annotationCalls, typeParameters, parameters, constraints)) +} + +/** + * Returns a new object of class [SmlExpressionLambda]. + */ +fun createSmlExpressionLambda( + parameters: List = emptyList(), + result: SmlAbstractExpression +): SmlExpressionLambda { + return factory.createSmlExpressionLambda().apply { + this.parameterList = createSmlLambdaParameterList(parameters) + this.result = result + } +} + +/** + * Returns a new object of class [SmlExpressionStatement]. + */ +fun createSmlExpressionStatement(expression: SmlAbstractExpression): SmlExpressionStatement { + return factory.createSmlExpressionStatement().apply { + this.expression = expression + } +} + +/** + * Adds a new object of class [SmlExpressionStatement] to the receiver. + */ +fun SmlBlockLambda.smlExpressionStatement(expression: SmlAbstractExpression) { + this.addStatement(createSmlExpressionStatement(expression)) +} + +/** + * Adds a new object of class [SmlExpressionStatement] to the receiver. + */ +fun SmlWorkflow.smlExpressionStatement(expression: SmlAbstractExpression) { + this.addStatement(createSmlExpressionStatement(expression)) +} + +/** + * Adds a new object of class [SmlExpressionStatement] to the receiver. + */ +fun SmlStep.smlExpressionStatement(expression: SmlAbstractExpression) { + this.addStatement(createSmlExpressionStatement(expression)) +} + +/** + * Returns a new object of class [SmlFloat] if the value is non-negative. Otherwise, the absolute value will be wrapped + * in a [SmlPrefixOperation] to negate it. + */ +fun createSmlFloat(value: Double): SmlAbstractExpression { + val float = factory.createSmlFloat().apply { + this.value = value.absoluteValue + } + + return when { + value < 0 -> createSmlPrefixOperation(SmlPrefixOperationOperator.Minus, float) + else -> float + } +} + +/** + * Returns a new object of class [SmlFunction]. + */ +fun createSmlFunction( + name: String, + annotationCalls: List = emptyList(), + isStatic: Boolean = false, + typeParameters: List = emptyList(), + parameters: List = emptyList(), + results: List = emptyList(), + constraints: List = emptyList() +): SmlFunction { + return factory.createSmlFunction().apply { + this.name = name + this.annotationCallList = createSmlAnnotationCallList(annotationCalls) + this.isStatic = isStatic + this.typeParameterList = typeParameters.nullIfEmptyElse(::createSmlTypeParameterList) + this.parameterList = createSmlParameterList(parameters) + this.resultList = results.nullIfEmptyElse(::createSmlResultList) + this.constraintList = constraints.nullIfEmptyElse(::createSmlConstraintList) + } +} + +/** + * Adds a new object of class [SmlFunction] to the receiver. + */ +fun SmlClass.smlFunction( + name: String, + annotationCalls: List = emptyList(), + isStatic: Boolean = false, + typeParameters: List = emptyList(), + parameters: List = emptyList(), + results: List = emptyList(), + constraints: List = emptyList() +) { + this.addMember( + createSmlFunction( + name, + annotationCalls, + isStatic, + typeParameters, + parameters, + results, + constraints + ) + ) +} + +/** + * Adds a new object of class [SmlFunction] to the receiver. + */ +fun SmlCompilationUnit.smlFunction( + name: String, + annotationCalls: List = emptyList(), + isStatic: Boolean = false, + typeParameters: List = emptyList(), + parameters: List = emptyList(), + results: List = emptyList(), + constraints: List = emptyList() +) { + this.addMember( + createSmlFunction( + name, + annotationCalls, + isStatic, + typeParameters, + parameters, + results, + constraints + ) + ) +} + +/** + * Returns a new object of class [SmlImport]. + */ +fun createSmlImport(importedNamespace: String, alias: String? = null): SmlImport { + return factory.createSmlImport().apply { + this.importedNamespace = importedNamespace + this.alias = createSmlImportAlias(alias) + } +} + +/** + * Returns a new object of class [SmlImportAlias] or `null` if the parameter is `null`. + */ +private fun createSmlImportAlias(name: String?): SmlImportAlias? { + if (name == null) { + return null + } + + return factory.createSmlImportAlias().apply { + this.name = name + } +} + +/** + * Returns a new object of class [SmlIndexedAccess]. + */ +fun createSmlIndexedAccess( + index: SmlAbstractExpression +): SmlIndexedAccess { + return factory.createSmlIndexedAccess().apply { + this.index = index + } +} + +/** + * Returns a new object of class [SmlInfixOperation]. + */ +fun createSmlInfixOperation( + leftOperand: SmlAbstractExpression, + operator: SmlInfixOperationOperator, + rightOperand: SmlAbstractExpression +): SmlInfixOperation { + return factory.createSmlInfixOperation().apply { + this.leftOperand = leftOperand + this.operator = operator.operator + this.rightOperand = rightOperand + } +} + +/** + * Returns a new object of class [SmlInt] if the value is non-negative. Otherwise, the absolute value will be wrapped in + * a [SmlPrefixOperation] to negate it. + */ +fun createSmlInt(value: Int): SmlAbstractExpression { + val int = factory.createSmlInt().apply { + this.value = value.absoluteValue + } + + return when { + value < 0 -> createSmlPrefixOperation(SmlPrefixOperationOperator.Minus, int) + else -> int + } +} + +/** + * Returns a new object of class [SmlMemberAccess]. + */ +fun createSmlMemberAccess( + receiver: SmlAbstractExpression, + member: SmlReference, + isNullSafe: Boolean = false +): SmlMemberAccess { + return factory.createSmlMemberAccess().apply { + this.receiver = receiver + this.member = member + this.isNullSafe = isNullSafe + } +} + +/** + * Returns a new object of class [SmlMemberType]. + */ +fun createSmlMemberType(receiver: SmlAbstractType, member: SmlNamedType): SmlMemberType { + return factory.createSmlMemberType().apply { + this.receiver = receiver + this.member = member + } +} + +/** + * Returns a new object of class [SmlNamedType]. + */ +fun createSmlNamedType( + declaration: SmlAbstractNamedTypeDeclaration, + typeArguments: List = emptyList(), + isNullable: Boolean = false +): SmlNamedType { + return factory.createSmlNamedType().apply { + this.declaration = declaration + this.typeArgumentList = typeArguments.nullIfEmptyElse(::createSmlTypeArgumentList) + this.isNullable = isNullable + } +} + +/** + * Returns a new object of class [SmlNull]. + */ +fun createSmlNull(): SmlNull { + return factory.createSmlNull() +} + +/** + * Returns a new object of class [SmlParameter]. + */ +fun createSmlParameter( + name: String, + annotationCalls: List = emptyList(), + isVariadic: Boolean = false, + type: SmlAbstractType? = null, + defaultValue: SmlAbstractExpression? = null +): SmlParameter { + return factory.createSmlParameter().apply { + this.name = name + this.annotationCalls += annotationCalls + this.isVariadic = isVariadic + this.type = type + this.defaultValue = defaultValue + } +} + +/** + * Returns a new object of class [SmlParameterList]. + */ +fun createSmlParameterList(parameters: List): SmlParameterList { + return factory.createSmlParameterList().apply { + this.parameters += parameters + } +} + +/** + * Returns a new object of class [SmlLambdaParameterList]. These have to be used as parameter lists of an + * [SmlAbstractLambda] + */ +@Suppress("FunctionName") +fun createSmlLambdaParameterList(parameters: List): SmlLambdaParameterList { + return factory.createSmlLambdaParameterList().apply { + this.parameters += parameters + } +} + +/** + * Returns a new object of class [SmlParenthesizedExpression]. + */ +fun createSmlParenthesizedExpression(expression: SmlAbstractExpression): SmlParenthesizedExpression { + return factory.createSmlParenthesizedExpression().apply { + this.expression = expression + } +} + +/** + * Returns a new object of class [SmlParenthesizedType]. + */ +fun createSmlParenthesizedType(type: SmlAbstractType): SmlParenthesizedType { + return factory.createSmlParenthesizedType().apply { + this.type = type + } +} + +/** + * Returns a new object of class [SmlParentTypeList]. + */ +fun createSmlParentTypeList(parentTypes: List): SmlParentTypeList { + return factory.createSmlParentTypeList().apply { + this.parentTypes += parentTypes + } +} + +/** + * Returns a new object of class [SmlPlaceholder]. + */ +fun createSmlPlaceholder(name: String): SmlPlaceholder { + return factory.createSmlPlaceholder().apply { + this.name = name + } +} + +/** + * Returns a new object of class [SmlPrefixOperation]. + */ +fun createSmlPrefixOperation(operator: SmlPrefixOperationOperator, operand: SmlAbstractExpression): SmlPrefixOperation { + return factory.createSmlPrefixOperation().apply { + this.operator = operator.operator + this.operand = operand + } +} + +/** + * Returns a new object of class [SmlProtocol]. + */ +fun createSmlProtocol( + subterms: List = emptyList(), + term: SmlAbstractProtocolTerm? = null, + init: SmlProtocol.() -> Unit = {} +): SmlProtocol { + return factory.createSmlProtocol().apply { + this.body = factory.createSmlProtocolBody() + subterms.forEach { addSubterm(it) } + this.body.term = term + this.init() + } +} + +/** + * Adds a new object of class [SmlProtocol] to the receiver. + */ +fun SmlClass.smlProtocol( + subterms: List = emptyList(), + term: SmlAbstractProtocolTerm? = null, + init: SmlProtocol.() -> Unit = {} +) { + this.addMember(createSmlProtocol(subterms, term, init)) +} + +/** + * Adds a new subterm to the receiver. + */ +private fun SmlProtocol.addSubterm(subterm: SmlProtocolSubterm) { + if (this.body == null) { + this.body = factory.createSmlProtocolBody() + } + + if (this.body.subtermList == null) { + this.body.subtermList = factory.createSmlProtocolSubtermList() + } + + this.body.subtermList.subterms += subterm +} + +/** + * Returns a new object of class [SmlProtocolAlternative]. + */ +fun createSmlProtocolAlternative(terms: List): SmlProtocolAlternative { + if (terms.size < 2) { + throw IllegalArgumentException("Must have at least two terms.") + } + + return factory.createSmlProtocolAlternative().apply { + this.terms += terms + } +} + +/** + * Returns a new object of class [SmlProtocolComplement]. + */ +fun createSmlProtocolComplement( + universe: SmlProtocolTokenClass? = null, + references: List = emptyList() +): SmlProtocolComplement { + return factory.createSmlProtocolComplement().apply { + this.universe = universe + this.referenceList = references.nullIfEmptyElse(::createSmlProtocolReferenceList) + } +} + +/** + * Returns a new object of class [SmlProtocolParenthesizedTerm]. + */ +fun createSmlProtocolParenthesizedTerm(term: SmlAbstractProtocolTerm): SmlProtocolParenthesizedTerm { + return factory.createSmlProtocolParenthesizedTerm().apply { + this.term = term + } +} + +/** + * Returns a new object of class [SmlProtocolQuantifiedTerm]. + */ +fun createSmlProtocolQuantifiedTerm( + term: SmlAbstractProtocolTerm, + quantifier: SmlProtocolQuantifiedTermQuantifier +): SmlProtocolQuantifiedTerm { + return factory.createSmlProtocolQuantifiedTerm().apply { + this.term = term + this.quantifier = quantifier.quantifier + } +} + +/** + * Returns a new object of class [SmlProtocolReference]. + */ +fun createSmlProtocolReference(token: SmlAbstractProtocolToken): SmlProtocolReference { + return factory.createSmlProtocolReference().apply { + this.token = token + } +} + +/** + * Returns a new object of class [SmlProtocolReferenceList]. + */ +fun createSmlProtocolReferenceList(references: List): SmlProtocolReferenceList { + return factory.createSmlProtocolReferenceList().apply { + this.references += references + } +} + +/** + * Returns a new object of class [SmlProtocolSequence]. + * + * @throws IllegalArgumentException If `terms.size < 2`. + */ +fun createSmlProtocolSequence(terms: List): SmlProtocolSequence { + if (terms.size < 2) { + throw IllegalArgumentException("Must have at least two terms.") + } + + return factory.createSmlProtocolSequence().apply { + this.terms += terms + } +} + +/** + * Returns a new object of class [SmlProtocolSubterm]. + */ +fun createSmlProtocolSubterm(name: String, term: SmlAbstractProtocolTerm): SmlProtocolSubterm { + return factory.createSmlProtocolSubterm().apply { + this.name = name + this.term = term + } +} + +/** + * Returns a new object of class [SmlProtocolSubterm]. + */ +fun SmlProtocol.smlProtocolSubterm(name: String, term: SmlAbstractProtocolTerm) { + this.addSubterm(createSmlProtocolSubterm(name, term)) +} + +/** + * Returns a new object of class [SmlProtocolTokenClass]. + */ +fun createSmlProtocolTokenClass(value: SmlProtocolTokenClassValue): SmlProtocolTokenClass { + return factory.createSmlProtocolTokenClass().apply { + this.value = value.value + } +} + +/** + * Returns a new object of class [SmlReference]. + */ +fun createSmlReference(declaration: SmlAbstractDeclaration): SmlReference { + return factory.createSmlReference().apply { + this.declaration = declaration + } +} + +/** + * Returns a new object of class [SmlResult]. + */ +fun createSmlResult( + name: String, + annotationCalls: List = emptyList(), + type: SmlAbstractType? = null +): SmlResult { + return factory.createSmlResult().apply { + this.name = name + this.annotationCalls += annotationCalls + this.type = type + } +} + +/** + * Returns a new object of class [SmlResultList]. + */ +fun createSmlResultList(results: List): SmlResultList { + return factory.createSmlResultList().apply { + this.results += results + } +} + +/** + * Returns a new object of class [SmlStarProjection]. + */ +fun createSmlStarProjection(): SmlStarProjection { + return factory.createSmlStarProjection() +} + +/** + * Returns a new object of class [SmlStep]. + */ +fun createSmlStep( + name: String, + annotationCalls: List = emptyList(), + visibility: SmlVisibility = SmlVisibility.Public, + parameters: List = emptyList(), + results: List = emptyList(), + statements: List = emptyList(), + init: SmlStep.() -> Unit = {} +): SmlStep { + return factory.createSmlStep().apply { + this.name = name + this.annotationCallList = createSmlAnnotationCallList(annotationCalls) + this.visibility = visibility.visibility + this.parameterList = createSmlParameterList(parameters) + this.resultList = results.nullIfEmptyElse(::createSmlResultList) + this.body = factory.createSmlBlock() + statements.forEach { addStatement(it) } + this.init() + } +} + +/** + * Adds a new object of class [SmlStep] to the receiver. + */ +fun SmlCompilationUnit.smlStep( + name: String, + annotationCalls: List = emptyList(), + visibility: SmlVisibility = SmlVisibility.Public, + parameters: List = emptyList(), + results: List = emptyList(), + statements: List = emptyList(), + init: SmlStep.() -> Unit = {} +) { + this.addMember( + createSmlStep( + name, + annotationCalls, + visibility, + parameters, + results, + statements, + init + ) + ) +} + +/** + * Adds a new statement to the receiver. + */ +private fun SmlStep.addStatement(statement: SmlAbstractStatement) { + if (this.body == null) { + this.body = factory.createSmlBlock() + } + + this.body.statements += statement +} + +/** + * Returns a new object of class [SmlString]. + */ +fun createSmlString(value: String): SmlString { + return factory.createSmlString().apply { + this.value = value + } +} + +/** + * Returns a new object of class [SmlTemplateString]. String parts should not include delimiters (`"`, `{{`, `}}`). + * Template expressions are inserted between the string parts. + * + * @throws IllegalArgumentException If `stringParts.size < 2`. + * @throws IllegalArgumentException If `templateExpressions` is empty. + * @throws IllegalArgumentException If `stringsParts.size` != `templateExpressions.size + 1`. + */ +fun createSmlTemplateString( + stringParts: List, + templateExpressions: List +): SmlTemplateString { + + // One of the first two checks is sufficient but this allows better error messages. + if (stringParts.size < 2) { + throw IllegalArgumentException("Must have at least two string parts.") + } else if (templateExpressions.isEmpty()) { + throw IllegalArgumentException("Must have at least one template expression.") + } else if (stringParts.size != templateExpressions.size + 1) { + throw IllegalArgumentException("Must have exactly one more string part than there are template expressions.") + } + + return factory.createSmlTemplateString().apply { + stringParts.forEachIndexed { index, value -> + + // Next template string part + this.expressions += when (index) { + 0 -> { + factory.createSmlTemplateStringStart().apply { + this.value = value + } + } + stringParts.size - 1 -> { + factory.createSmlTemplateStringEnd().apply { + this.value = value + } + } + else -> { + factory.createSmlTemplateStringInner().apply { + this.value = value + } + } + } + + // Next template expression + if (index < templateExpressions.size) { + this.expressions += templateExpressions[index] + } + } + + this.expressions += expressions + } +} + +/** + * Returns a new object of class [SmlTypeArgument]. + */ +fun createSmlTypeArgument( + value: SmlAbstractTypeArgumentValue, + typeParameter: SmlTypeParameter? = null +): SmlTypeArgument { + return factory.createSmlTypeArgument().apply { + this.value = value + this.typeParameter = typeParameter + } +} + +/** + * Returns a new object of class [SmlTypeArgument] that points to a type parameter with the given name. + */ +fun createSmlTypeArgument( + value: SmlAbstractTypeArgumentValue, + typeParameterName: String +): SmlTypeArgument { + return createSmlTypeArgument( + value, + createSmlTypeParameter(typeParameterName) + ) +} + +/** + * Returns a new object of class [SmlTypeArgumentList]. + */ +fun createSmlTypeArgumentList(typeArguments: List): SmlTypeArgumentList { + return factory.createSmlTypeArgumentList().apply { + this.typeArguments += typeArguments + } +} + +/** + * Returns a new object of class [SmlTypeParameter]. + */ +fun createSmlTypeParameter( + name: String, + annotationCalls: List = emptyList(), + variance: SmlVariance = SmlVariance.Invariant +): SmlTypeParameter { + return factory.createSmlTypeParameter().apply { + this.name = name + this.annotationCalls += annotationCalls + this.variance = variance.variance + } +} + +/** + * Returns a new object of class [SmlTypeParameterList]. + */ +fun createSmlTypeParameterList(typeParameters: List): SmlTypeParameterList { + return factory.createSmlTypeParameterList().apply { + this.typeParameters += typeParameters + } +} + +/** + * Returns a new object of class [SmlTypeParameterConstraint]. + */ +fun createSmlTypeParameterConstraint( + leftOperand: SmlTypeParameter, + operator: SmlTypeParameterConstraintOperator, + rightOperand: SmlAbstractType +): SmlTypeParameterConstraint { + return factory.createSmlTypeParameterConstraint().apply { + this.leftOperand = leftOperand + this.operator = operator.operator + this.rightOperand = rightOperand + } +} + +/** + * Returns a new object of class [SmlTypeParameterConstraint] that points to a type parameter with the given name. + */ +fun createSmlTypeParameterConstraint( + leftOperandName: String, + operator: SmlTypeParameterConstraintOperator, + rightOperand: SmlAbstractType +): SmlTypeParameterConstraint { + return createSmlTypeParameterConstraint( + createSmlTypeParameter(leftOperandName), + operator, + rightOperand + ) +} + +/** + * Returns a new object of class [SmlTypeProjection]. + */ +fun createSmlTypeProjection(type: SmlAbstractType, variance: SmlVariance = SmlVariance.Invariant): SmlTypeProjection { + return factory.createSmlTypeProjection().apply { + this.type = type + this.variance = variance.variance + } +} + +/** + * Returns a new object of class [SmlUnionType]. + * + * @throws IllegalArgumentException If no type arguments are passed. + */ +fun createSmlUnionType(typeArguments: List): SmlUnionType { + if (typeArguments.isEmpty()) { + throw IllegalArgumentException("Must have at least one type argument.") + } + + return factory.createSmlUnionType().apply { + this.typeArgumentList = createSmlTypeArgumentList(typeArguments) + } +} + +/** + * Returns a new object of class [SmlWildcard]. + */ +fun createSmlWildcard(): SmlWildcard { + return factory.createSmlWildcard() +} + +/** + * Returns a new object of class [SmlWorkflow]. + */ +fun createSmlWorkflow( + name: String, + annotationCalls: List = emptyList(), + statements: List = emptyList(), + init: SmlWorkflow.() -> Unit = {} +): SmlWorkflow { + return factory.createSmlWorkflow().apply { + this.name = name + this.annotationCallList = createSmlAnnotationCallList(annotationCalls) + this.body = factory.createSmlBlock() + statements.forEach { addStatement(it) } + this.init() + } +} + +/** + * Adds a new object of class [SmlWorkflow] to the receiver. + */ +fun SmlCompilationUnit.smlWorkflow( + name: String, + annotationCalls: List = emptyList(), + statements: List = emptyList(), + init: SmlWorkflow.() -> Unit = {} +) { + this.addMember(createSmlWorkflow(name, annotationCalls, statements, init)) +} + +/** + * Adds a new statement to the receiver. + */ +private fun SmlWorkflow.addStatement(statement: SmlAbstractStatement) { + if (this.body == null) { + this.body = factory.createSmlBlock() + } + + this.body.statements += statement +} + +/** + * Returns a new object of class [SmlYield]. + */ +fun createSmlYield(result: SmlResult): SmlYield { + return factory.createSmlYield().apply { + this.result = result + } +} + +/** + * Returns a new object of class [SmlYield] that points to a result with the given name. + */ +fun createSmlYield(resultName: String): SmlYield { + return createSmlYield(createSmlResult(resultName)) +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/emf/OriginalFilePath.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/emf/OriginalFilePath.kt new file mode 100644 index 000000000..3c969eedc --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/emf/OriginalFilePath.kt @@ -0,0 +1,8 @@ +package de.unibonn.simpleml.emf + +import org.eclipse.emf.common.notify.impl.AdapterImpl + +/** + * Stores the original file path. This is necessary since synthetic resources always have the `smlflow` extension. + */ +class OriginalFilePath(val path: String) : AdapterImpl() diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/emf/SimpleShortcuts.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/emf/SimpleShortcuts.kt new file mode 100644 index 000000000..c8de7856e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/emf/SimpleShortcuts.kt @@ -0,0 +1,463 @@ +@file:Suppress("unused") + +/** + * Contains shortcuts that simplify working with the EMF model. Since most of these are very straightforward, unit tests + * are usually not required. + */ + +package de.unibonn.simpleml.emf + +import de.unibonn.simpleml.simpleML.SmlAbstractAssignee +import de.unibonn.simpleml.simpleML.SmlAbstractCallable +import de.unibonn.simpleml.simpleML.SmlAbstractClassMember +import de.unibonn.simpleml.simpleML.SmlAbstractCompilationUnitMember +import de.unibonn.simpleml.simpleML.SmlAbstractConstraint +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlAbstractLambda +import de.unibonn.simpleml.simpleML.SmlAbstractLocalVariable +import de.unibonn.simpleml.simpleML.SmlAbstractObject +import de.unibonn.simpleml.simpleML.SmlAbstractProtocolTerm +import de.unibonn.simpleml.simpleML.SmlAbstractStatement +import de.unibonn.simpleml.simpleML.SmlAbstractType +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.simpleML.SmlAnnotationCall +import de.unibonn.simpleml.simpleML.SmlAnnotationCallList +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.simpleML.SmlBlockLambda +import de.unibonn.simpleml.simpleML.SmlBlockLambdaResult +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlCallableType +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlConstraintList +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlExpressionLambda +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlImport +import de.unibonn.simpleml.simpleML.SmlNamedType +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlProtocol +import de.unibonn.simpleml.simpleML.SmlProtocolBody +import de.unibonn.simpleml.simpleML.SmlProtocolComplement +import de.unibonn.simpleml.simpleML.SmlProtocolReference +import de.unibonn.simpleml.simpleML.SmlProtocolSubterm +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlTypeArgument +import de.unibonn.simpleml.simpleML.SmlTypeParameter +import de.unibonn.simpleml.simpleML.SmlUnionType +import de.unibonn.simpleml.simpleML.SmlWorkflow +import de.unibonn.simpleml.simpleML.SmlYield +import de.unibonn.simpleml.utils.uniqueOrNull +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.emf.ecore.resource.ResourceSet +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + +/* ******************************************************************************************************************** + * Accessing descendants * + * ********************************************************************************************************************/ + +// EObject ----------------------------------------------------------------------------------------- + +fun EObject.resourceSetOrNull(): ResourceSet? { + return eResource()?.resourceSet +} + +// Resource ---------------------------------------------------------------------------------------- + +fun Resource.compilationUnitOrNull(): SmlCompilationUnit? { + return this.allContents + ?.asSequence() + ?.filterIsInstance() + ?.firstOrNull() +} + +// SmlAbstractCallable ----------------------------------------------------------------------------- + +fun SmlAbstractCallable?.parametersOrEmpty(): List { + return this?.parameterList?.parameters.orEmpty() +} + +/** + * Returns all calls that are actually executed immediately when this [SmlAbstractCallable] is called. + */ +fun SmlAbstractCallable.immediateCalls(): List { + return descendants { it is SmlAbstractLambda }.toList() +} + +// SmlAbstractDeclaration -------------------------------------------------------------------------- + +fun SmlAbstractDeclaration?.annotationCallsOrEmpty(): List { + return this?.annotationCallList?.annotationCalls ?: this?.annotationCalls.orEmpty() +} + +// SmlAnnotation ----------------------------------------------------------------------------------- + +fun SmlAnnotation?.constraintsOrEmpty(): List { + return this?.constraintList?.constraints.orEmpty() +} + +// SmlAnnotationCall ------------------------------------------------------------------------------- + +fun SmlAnnotationCall?.argumentsOrEmpty(): List { + return this?.argumentList?.arguments.orEmpty() +} + +// SmlAssignment ----------------------------------------------------------------------------------- + +fun SmlAssignment?.assigneesOrEmpty(): List { + return this?.assigneeList?.assignees + ?.filterIsInstance() + .orEmpty() +} + +fun SmlAssignment?.blockLambdaResultsOrEmpty(): List { + return this.assigneesOrEmpty().filterIsInstance() +} + +fun SmlAssignment?.placeholdersOrEmpty(): List { + return this.assigneesOrEmpty().filterIsInstance() +} + +fun SmlAssignment?.yieldsOrEmpty(): List { + return this.assigneesOrEmpty().filterIsInstance() +} + +// SmlBlockLambda ---------------------------------------------------------------------------------- + +fun SmlBlockLambda?.blockLambdaResultsOrEmpty(): List { + return this.statementsOrEmpty() + .filterIsInstance() + .flatMap { it.blockLambdaResultsOrEmpty() } +} + +fun SmlBlockLambda?.localVariablesOrEmpty(): List { + return this.parametersOrEmpty() + this.placeholdersOrEmpty() +} + +fun SmlBlockLambda?.placeholdersOrEmpty(): List { + return this.statementsOrEmpty() + .filterIsInstance() + .flatMap { it.placeholdersOrEmpty() } +} + +fun SmlBlockLambda?.statementsOrEmpty(): List { + return this?.body?.statements.orEmpty() +} + +// SmlCall ----------------------------------------------------------------------------------------- + +fun SmlCall?.argumentsOrEmpty(): List { + return this?.argumentList?.arguments.orEmpty() +} + +fun SmlCall?.typeArgumentsOrEmpty(): List { + return this?.typeArgumentList?.typeArguments.orEmpty() +} + +// SmlCallableType --------------------------------------------------------------------------------- + +fun SmlCallableType?.resultsOrEmpty(): List { + return this?.resultList?.results.orEmpty() +} + +// SmlClass ---------------------------------------------------------------------------------------- + +fun SmlClass?.typeParametersOrEmpty(): List { + return this?.typeParameterList?.typeParameters.orEmpty() +} + +fun SmlClass?.parentTypesOrEmpty(): List { + return this?.parentTypeList?.parentTypes.orEmpty() +} + +fun SmlClass?.constraintsOrEmpty(): List { + return this?.constraintList?.constraints.orEmpty() +} + +fun SmlClass?.objectsInBodyOrEmpty(): List { + return this?.body?.members.orEmpty() +} + +fun SmlClass?.classMembersOrEmpty(): List { + return this?.body?.members + ?.filterIsInstance() + .orEmpty() +} + +fun SmlClass?.protocolsOrEmpty(): List { + return this?.body?.members + ?.filterIsInstance() + .orEmpty() +} + +fun SmlClass.uniqueProtocolOrNull(): SmlProtocol? { + return this.protocolsOrEmpty().uniqueOrNull() +} + +// SmlCompilationUnit ------------------------------------------------------------------------------ + +fun SmlCompilationUnit?.compilationUnitMembersOrEmpty(): List { + return this?.members + ?.filterIsInstance() + .orEmpty() +} + +// SmlEnum ----------------------------------------------------------------------------------------- + +fun SmlEnum?.variantsOrEmpty(): List { + return this?.body?.variants.orEmpty() +} + +// SmlEnumVariant ---------------------------------------------------------------------------------- + +fun SmlEnumVariant?.typeParametersOrEmpty(): List { + return this?.typeParameterList?.typeParameters.orEmpty() +} + +fun SmlEnumVariant?.constraintsOrEmpty(): List { + return this?.constraintList?.constraints.orEmpty() +} + +// SmlFunction ------------------------------------------------------------------------------------- + +fun SmlFunction?.resultsOrEmpty(): List { + return this?.resultList?.results.orEmpty() +} + +fun SmlFunction?.typeParametersOrEmpty(): List { + return this?.typeParameterList?.typeParameters.orEmpty() +} + +fun SmlFunction?.constraintsOrEmpty(): List { + return this?.constraintList?.constraints.orEmpty() +} + +// SmlImport --------------------------------------------------------------------------------------- + +fun SmlImport.aliasNameOrNull(): String? { + return this.alias?.name +} + +fun SmlImport.importedNameOrNull(): String? { + return when (alias) { + null -> when { + isQualified() -> importedNamespace.split(".").last() + else -> null + } + else -> aliasNameOrNull() + } +} + +// SmlNamedType ------------------------------------------------------------------------------------ + +fun SmlNamedType?.typeArgumentsOrEmpty(): List { + return this?.typeArgumentList?.typeArguments.orEmpty() +} + +// SmlProtocol ------------------------------------------------------------------------------------- + +fun SmlProtocol?.subtermsOrEmpty(): List { + return this?.body.subtermsOrEmpty() +} + +fun SmlProtocol.termOrNull(): SmlAbstractProtocolTerm? { + return this.body?.term +} + +// SmlProtocolBody --------------------------------------------------------------------------------- + +fun SmlProtocolBody?.subtermsOrEmpty(): List { + return this?.subtermList?.subterms.orEmpty() +} + +// SmlProtocolComplement --------------------------------------------------------------------------- + +fun SmlProtocolComplement?.referencesOrEmpty(): List { + return this?.referenceList?.references.orEmpty() +} + +// SmlUnionType ------------------------------------------------------------------------------------ + +fun SmlUnionType?.typeArgumentsOrEmpty(): List { + return this?.typeArgumentList?.typeArguments.orEmpty() +} + +// SmlWorkflow ------------------------------------------------------------------------------------- + +fun SmlWorkflow?.placeholdersOrEmpty(): List { + return this.statementsOrEmpty() + .filterIsInstance() + .flatMap { it.placeholdersOrEmpty() } +} + +fun SmlWorkflow?.statementsOrEmpty(): List { + return this?.body?.statements.orEmpty() +} + +// SmlWorkflowStep --------------------------------------------------------------------------------- + +fun SmlStep?.localVariablesOrEmpty(): List { + return this.parametersOrEmpty() + this.placeholdersOrEmpty() +} + +fun SmlStep?.placeholdersOrEmpty(): List { + return this.statementsOrEmpty() + .filterIsInstance() + .flatMap { it.placeholdersOrEmpty() } +} + +fun SmlStep?.resultsOrEmpty(): List { + return this?.resultList?.results.orEmpty() +} + +fun SmlStep?.statementsOrEmpty(): List { + return this?.body?.statements.orEmpty() +} + +fun SmlStep?.yieldsOrEmpty(): List { + return this.statementsOrEmpty() + .filterIsInstance() + .flatMap { it.yieldsOrEmpty() } +} + +/* ******************************************************************************************************************** + * Accessing ancestors * + * ********************************************************************************************************************/ + +fun EObject.containingBlockLambdaOrNull() = this.closestAncestorOrNull() +fun EObject.containingCallableOrNull() = this.closestAncestorOrNull() +fun EObject.containingClassOrNull() = this.closestAncestorOrNull() +fun EObject.containingCompilationUnitOrNull() = this.closestAncestorOrNull() +fun EObject.containingDeclarationOrNull() = this.closestAncestorOrNull() +fun EObject.containingEnumOrNull() = this.closestAncestorOrNull() +fun EObject.containingExpressionLambdaOrNull() = this.closestAncestorOrNull() +fun EObject.containingFunctionOrNull() = this.closestAncestorOrNull() +fun EObject.containingProtocolOrNull() = this.closestAncestorOrNull() +fun EObject.containingStepOrNull() = this.closestAncestorOrNull() +fun EObject.containingWorkflowOrNull() = this.closestAncestorOrNull() + +fun SmlAnnotationCall.targetOrNull(): SmlAbstractDeclaration? { + return when (val declaration = this.containingDeclarationOrNull() ?: return null) { + is SmlAnnotationCallList -> declaration.containingDeclarationOrNull() + else -> declaration + } +} + +/* ******************************************************************************************************************** + * Accessing siblings * + * ********************************************************************************************************************/ + +fun SmlConstraintList.typeParametersOrNull(): List? { + return when (val parent = this.eContainer()) { + is SmlClass -> parent.typeParametersOrEmpty() + is SmlEnumVariant -> return parent.typeParametersOrEmpty() + is SmlFunction -> parent.typeParametersOrEmpty() + else -> null + } +} + +/* ******************************************************************************************************************** + * Checks * + * ********************************************************************************************************************/ + +// SmlAbstractClassMember -------------------------------------------------------------------------- + +/** + * Returns whether this [SmlAbstractClassMember] is truly contained in a class and static. + */ +fun SmlAbstractClassMember.isStatic(): Boolean { + return when { + !this.isClassMember() -> false + this is SmlClass || this is SmlEnum -> true + this is SmlAttribute && this.isStatic -> true + this is SmlFunction && this.isStatic -> true + else -> false + } +} + +// SmlAbstractDeclaration -------------------------------------------------------------------------- + +/** + * Returns whether this [SmlAbstractDeclaration] is contained in a class. + */ +fun SmlAbstractDeclaration.isClassMember(): Boolean { + return this is SmlAbstractClassMember && containingClassOrNull() != null +} + +/** + * Returns whether this [SmlAbstractDeclaration] is a global declaration. + */ +fun SmlAbstractDeclaration.isGlobal(): Boolean { + return !isClassMember() && this is SmlAbstractCompilationUnitMember +} + +/** + * Returns whether this [SmlAbstractDeclaration] is resolved, i.e. not a proxy. + */ +@OptIn(ExperimentalContracts::class) +fun SmlAbstractDeclaration?.isResolved(): Boolean { + contract { + returns(true) implies (this@isResolved != null) + } + + return (this != null) && !this.eIsProxy() +} + +// SmlArgument ------------------------------------------------------------------------------------- + +fun SmlArgument.isNamed() = parameter != null +fun SmlArgument.isPositional() = parameter == null + +// SmlEnum ----------------------------------------------------------------------------------------- + +/** + * Returns whether no [SmlEnumVariant]s of this [SmlEnum] have non-empty parameter list. Only those enums can be + * processed by the compiler, so non-constant [SmlEnum]s cannot be used as the type of parameters of annotations. + */ +fun SmlEnum.isConstant(): Boolean { + return variantsOrEmpty().all { it.parametersOrEmpty().isEmpty() } +} + +// SmlFunction ----------------------------------------------------------------------------------- + +fun SmlFunction.isMethod() = containingClassOrNull() != null + +// SmlImport --------------------------------------------------------------------------------------- + +fun SmlImport.isQualified() = !importedNamespace.endsWith(".*") +fun SmlImport.isWildcard() = importedNamespace.endsWith(".*") + +// SmlParameter ------------------------------------------------------------------------------------ + +fun SmlParameter.isRequired() = defaultValue == null && !isVariadic +fun SmlParameter.isOptional() = defaultValue != null + +// SmlTypeArgument --------------------------------------------------------------------------------- + +fun SmlTypeArgument.isNamed() = typeParameter != null +fun SmlTypeArgument.isPositional() = typeParameter == null + +/* ******************************************************************************************************************** + * Conversions * + * ********************************************************************************************************************/ + +// SmlAbstractDeclaration -------------------------------------------------------------------------- + +/** + * Returns this [SmlAbstractDeclaration] if it is resolved, otherwise `null`. + * + * @see isResolved + */ +fun T.asResolvedOrNull(): T? { + return when { + isResolved() -> this + else -> null + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/emf/Traversal.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/emf/Traversal.kt new file mode 100644 index 000000000..a02158cd7 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/emf/Traversal.kt @@ -0,0 +1,43 @@ +package de.unibonn.simpleml.emf + +import org.eclipse.emf.ecore.EObject + +/** + * Returns all descendants of this [EObject] with the given type. + * + * @param prune Whether the subtree with the current object as root should be pruned. + */ +inline fun EObject.descendants(crossinline prune: (EObject) -> Boolean = { false }) = sequence { + val iterator = this@descendants.eAllContents() + for (obj in iterator) { + if (prune(obj)) { + iterator.prune() + } else if (obj is T) { + yield(obj) + } + } +} + +/** + * Returns the closest ancestor of this [EObject] with the given type or `null` if none exists. This cannot return the + * receiver. + */ +inline fun EObject.closestAncestorOrNull(): T? { + var current: EObject? = this.eContainer() + while (current != null && current !is T) { + current = current.eContainer() + } + return current as T? +} + +/** + * Returns the closest ancestor of this [EObject] that matches the given [predicate] or `null` if none + * exists. This cannot return the receiver. + */ +inline fun EObject.closestAncestorOrNull(crossinline predicate: (EObject) -> Boolean = { true }): EObject? { + var current: EObject? = this.eContainer() + while (current != null && !predicate(current)) { + current = current.eContainer() + } + return current +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/formatting2/SimpleMLFormatter.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/formatting2/SimpleMLFormatter.kt new file mode 100644 index 000000000..9d36b178f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/formatting2/SimpleMLFormatter.kt @@ -0,0 +1,1208 @@ +package de.unibonn.simpleml.formatting2 + +import de.unibonn.simpleml.emf.annotationCallsOrEmpty +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_ABSTRACT_DECLARATION__NAME +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_ANNOTATION_CALL__ANNOTATION +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_ARGUMENT__PARAMETER +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_IMPORT_ALIAS__NAME +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_IMPORT__IMPORTED_NAMESPACE +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_INFIX_OPERATION__OPERATOR +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_MEMBER_ACCESS__NULL_SAFE +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_NAMED_TYPE__DECLARATION +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_NAMED_TYPE__NULLABLE +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_PREFIX_OPERATION__OPERATOR +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_PROTOCOL_QUANTIFIED_TERM__QUANTIFIER +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_STEP__VISIBILITY +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_TYPE_ARGUMENT__TYPE_PARAMETER +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_TYPE_PARAMETER_CONSTRAINT__LEFT_OPERAND +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_TYPE_PARAMETER_CONSTRAINT__OPERATOR +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_TYPE_PARAMETER__VARIANCE +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_TYPE_PROJECTION__VARIANCE +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals.SML_YIELD__RESULT +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlAbstractTemplateStringPart +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.simpleML.SmlAnnotationCall +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlArgumentList +import de.unibonn.simpleml.simpleML.SmlAssigneeList +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.simpleML.SmlBlock +import de.unibonn.simpleml.simpleML.SmlBlockLambda +import de.unibonn.simpleml.simpleML.SmlBlockLambdaResult +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlCallableType +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlClassBody +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlConstraintList +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlEnumBody +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlExpressionLambda +import de.unibonn.simpleml.simpleML.SmlExpressionStatement +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlImport +import de.unibonn.simpleml.simpleML.SmlImportAlias +import de.unibonn.simpleml.simpleML.SmlIndexedAccess +import de.unibonn.simpleml.simpleML.SmlInfixOperation +import de.unibonn.simpleml.simpleML.SmlMemberAccess +import de.unibonn.simpleml.simpleML.SmlMemberType +import de.unibonn.simpleml.simpleML.SmlNamedType +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlParameterList +import de.unibonn.simpleml.simpleML.SmlParentTypeList +import de.unibonn.simpleml.simpleML.SmlParenthesizedExpression +import de.unibonn.simpleml.simpleML.SmlParenthesizedType +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlPrefixOperation +import de.unibonn.simpleml.simpleML.SmlProtocol +import de.unibonn.simpleml.simpleML.SmlProtocolAlternative +import de.unibonn.simpleml.simpleML.SmlProtocolBody +import de.unibonn.simpleml.simpleML.SmlProtocolComplement +import de.unibonn.simpleml.simpleML.SmlProtocolParenthesizedTerm +import de.unibonn.simpleml.simpleML.SmlProtocolQuantifiedTerm +import de.unibonn.simpleml.simpleML.SmlProtocolReferenceList +import de.unibonn.simpleml.simpleML.SmlProtocolSequence +import de.unibonn.simpleml.simpleML.SmlProtocolSubterm +import de.unibonn.simpleml.simpleML.SmlProtocolSubtermList +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlResultList +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlTemplateString +import de.unibonn.simpleml.simpleML.SmlTypeArgument +import de.unibonn.simpleml.simpleML.SmlTypeArgumentList +import de.unibonn.simpleml.simpleML.SmlTypeParameter +import de.unibonn.simpleml.simpleML.SmlTypeParameterConstraint +import de.unibonn.simpleml.simpleML.SmlTypeParameterList +import de.unibonn.simpleml.simpleML.SmlTypeProjection +import de.unibonn.simpleml.simpleML.SmlUnionType +import de.unibonn.simpleml.simpleML.SmlWorkflow +import de.unibonn.simpleml.simpleML.SmlYield +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.EStructuralFeature +import org.eclipse.xtext.TerminalRule +import org.eclipse.xtext.formatting2.AbstractFormatter2 +import org.eclipse.xtext.formatting2.FormatterPreferenceKeys.indentation +import org.eclipse.xtext.formatting2.IFormattableDocument +import org.eclipse.xtext.formatting2.ITextReplacer +import org.eclipse.xtext.formatting2.ITextReplacerContext +import org.eclipse.xtext.formatting2.internal.AbstractTextReplacer +import org.eclipse.xtext.formatting2.internal.CommentReplacer +import org.eclipse.xtext.formatting2.internal.WhitespaceReplacer +import org.eclipse.xtext.formatting2.regionaccess.IComment +import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion +import org.eclipse.xtext.formatting2.regionaccess.ITextReplacement +import org.eclipse.xtext.preferences.MapBasedPreferenceValues +import org.eclipse.xtext.resource.XtextResource +import org.eclipse.xtext.xbase.lib.Procedures +import kotlin.reflect.KFunction1 +import org.eclipse.xtext.formatting2.IHiddenRegionFormatter as Format + +class SimpleMLFormatter : AbstractFormatter2() { + + private val indent = Format::indent + private val noSpace = Format::noSpace + private val oneSpace = Format::oneSpace + private val newLine = Format::newLine + + @Suppress("SameParameterValue") + private fun newLines(n: Int): Procedures.Procedure1 { + return Procedures.Procedure1 { it.setNewLines(n) } + } + + /** + * We follow the rule here that an object never formats its preceding or following region. This is left to the + * parent. + */ + override fun format(obj: Any?, doc: IFormattableDocument) { + if (obj == null) { + return + } + + when (obj) { + is XtextResource -> { + useSpacesForIndentation() + _format(obj, doc) + } + + is SmlCompilationUnit -> { + + // Feature "annotations" + obj.annotationCallsOrEmpty().forEach { + doc.format(it) + + if (obj.annotationCallsOrEmpty().last() == it) { + doc.append(it, newLines(2)) + } else { + doc.append(it, newLine) + } + } + + // Keyword "package" + doc.formatKeyword(obj, "package", noSpace, oneSpace) + + // Feature "name" + val name = obj.regionForFeature(SML_ABSTRACT_DECLARATION__NAME) + if (name != null) { + doc.addReplacer(WhitespaceCollapser(doc, name)) + + if (obj.imports.isNotEmpty() || obj.members.isNotEmpty()) { + doc.append(name, newLines(2)) + } else { + doc.append(name, noSpace) + } + } + + // Feature "imports" + obj.imports.forEach { + doc.format(it) + + if (obj.imports.last() == it && obj.members.isNotEmpty()) { + doc.append(it, newLines(2)) + } else if (obj.imports.last() != it) { + doc.append(it, newLine) + } else { + doc.append(it, noSpace) + } + } + + // Feature "members" + obj.members.forEach { + doc.format(it) + + if (obj.members.last() != it) { + doc.append(it, newLines(2)) + } else { + doc.append(it, noSpace) + } + } + + doc.append(obj, newLine) + } + + /********************************************************************************************************** + * Declarations + **********************************************************************************************************/ + + is SmlImport -> { + + // Keyword "import" + doc.formatKeyword(obj, "import", noSpace, oneSpace) + + // Feature "importedNamespace" + val importedNamespace = obj.regionForFeature(SML_IMPORT__IMPORTED_NAMESPACE) + if (importedNamespace != null) { + doc.addReplacer(WhitespaceCollapser(doc, importedNamespace)) + } + + // EObject "aliasDeclaration" + doc.formatObject(obj.alias, oneSpace, noSpace) + } + is SmlImportAlias -> { + + // Keyword "as" + doc.formatKeyword(obj, "as", null, oneSpace) + + // Feature "alias" + doc.formatFeature(obj, SML_IMPORT_ALIAS__NAME, null, noSpace) + } + is SmlAnnotation -> { + + // Features "annotations" + doc.formatAnnotations(obj) + + // Keyword "annotation" + if (obj.annotationCallsOrEmpty().isEmpty()) { + doc.formatKeyword(obj, "annotation", null, oneSpace) + } else { + doc.formatKeyword(obj, "annotation", oneSpace, oneSpace) + } + + // Feature "name" + doc.formatFeature(obj, SML_ABSTRACT_DECLARATION__NAME, oneSpace, null) + + // EObject "parameterList" + doc.formatObject(obj.parameterList, noSpace, null) + + // EObject "constraintList" + doc.formatObject(obj.constraintList, oneSpace, null) + } + is SmlAnnotationCall -> { + + // Keyword "@" + doc.formatKeyword(obj, "@", null, noSpace) + + // Feature "annotation" + doc.formatFeature(obj, SML_ANNOTATION_CALL__ANNOTATION, noSpace, null) + + // EObject "argumentList" + doc.formatObject(obj.argumentList, noSpace, null) + } + is SmlAttribute -> { + + // Features "annotations" + doc.formatAnnotations(obj) + + // Keyword "static" + if (obj.annotationCallsOrEmpty().isNotEmpty()) { + doc.formatKeyword(obj, "static", oneSpace, null) + } + + // Keyword "attr" + if (obj.annotationCallsOrEmpty().isEmpty() && !obj.isStatic) { + doc.formatKeyword(obj, "attr", null, oneSpace) + } else { + doc.formatKeyword(obj, "attr", oneSpace, oneSpace) + } + + // Feature "name" + doc.formatFeature(obj, SML_ABSTRACT_DECLARATION__NAME, oneSpace, noSpace) + + // Keyword ":" + doc.formatKeyword(obj, ":", noSpace, oneSpace) + + // EObject "type" + doc.formatObject(obj.type, oneSpace, null) + } + is SmlClass -> { + + // Features "annotations" + doc.formatAnnotations(obj) + + // Keyword "class" + if (obj.annotationCallsOrEmpty().isEmpty()) { + doc.formatKeyword(obj, "class", null, oneSpace) + } else { + doc.formatKeyword(obj, "class", oneSpace, oneSpace) + } + + // Feature "name" + doc.formatFeature(obj, SML_ABSTRACT_DECLARATION__NAME, oneSpace, null) + + // EObject "typeParameterList" + doc.formatObject(obj.typeParameterList, noSpace, null) + + // EObject "constructor" + doc.formatObject(obj.parameterList, noSpace, null) + + // EObject "parentTypeList" + doc.formatObject(obj.parentTypeList, oneSpace, null) + + // EObject "constraintList" + doc.formatObject(obj.constraintList, oneSpace, null) + + // EObject "body" + doc.formatObject(obj.body, oneSpace, null) + } + is SmlParentTypeList -> { + + // Keyword "sub" + doc.formatKeyword(obj, "sub", null, oneSpace) + + // Feature "parentTypes" + obj.parentTypes.forEach { + doc.formatObject(it) + } + + // Keywords "," + doc.formatCommas(obj) + } + is SmlClassBody -> { + + // Keyword "{" + val openingBrace = obj.regionForKeyword("{") + if (obj.members.isEmpty()) { + doc.append(openingBrace, noSpace) + } else { + doc.append(openingBrace, newLine) + } + + // Feature "members" + obj.members.forEach { + doc.format(it) + if (obj.members.last() == it) { + doc.append(it, newLine) + } else { + doc.append(it, newLines(2)) + } + } + + // Keyword "}" + val closingBrace = obj.regionForKeyword("}") + doc.prepend(closingBrace, noSpace) + + doc.interior(openingBrace, closingBrace, indent) + } + is SmlEnum -> { + + // Features "annotations" + doc.formatAnnotations(obj) + + // Keyword "enum" + if (obj.annotationCallsOrEmpty().isEmpty()) { + doc.formatKeyword(obj, "enum", null, oneSpace) + } else { + doc.formatKeyword(obj, "enum", oneSpace, oneSpace) + } + + // Feature "name" + doc.formatFeature(obj, SML_ABSTRACT_DECLARATION__NAME, oneSpace, null) + + // EObject "body" + doc.formatObject(obj.body, oneSpace, null) + } + is SmlEnumBody -> { + // Keyword "{" + val openingBrace = obj.regionForKeyword("{") + if (obj.variants.isEmpty()) { + doc.append(openingBrace, noSpace) + } else { + doc.append(openingBrace, newLine) + } + + // Feature "variants" + obj.variants.forEach { + doc.format(it) + if (obj.variants.first() != it) { + doc.prepend(it, newLines(2)) + } + } + + // Keywords "," + val commas = textRegionExtensions.allRegionsFor(obj).keywords(",") + commas.forEach { + doc.prepend(it, noSpace) + } + + // Keyword "}" + val closingBrace = obj.regionForKeyword("}") + if (obj.variants.isEmpty()) { + doc.prepend(closingBrace, noSpace) + } else { + doc.prepend(closingBrace, newLine) + } + + doc.interior(openingBrace, closingBrace, indent) + } + is SmlEnumVariant -> { + + // Features "annotations" + doc.formatAnnotations(obj) + + // Feature "name" + if (obj.annotationCallsOrEmpty().isEmpty()) { + doc.formatFeature(obj, SML_ABSTRACT_DECLARATION__NAME) + } else { + doc.formatFeature(obj, SML_ABSTRACT_DECLARATION__NAME, oneSpace, null) + } + + // EObject "typeParameterList" + doc.formatObject(obj.typeParameterList, noSpace, null) + + // EObject "parameterList" + doc.formatObject(obj.parameterList, noSpace, null) + + // EObject "constraintList" + doc.formatObject(obj.constraintList, oneSpace, null) + } + is SmlFunction -> { + + // Features "annotations" + doc.formatAnnotations(obj) + + // Keyword "static" + if (obj.annotationCallsOrEmpty().isNotEmpty()) { + doc.formatKeyword(obj, "static", oneSpace, null) + } + + // Keyword "fun" + if (obj.annotationCallsOrEmpty().isEmpty() && !obj.isStatic) { + doc.formatKeyword(obj, "fun", null, oneSpace) + } else { + doc.formatKeyword(obj, "fun", oneSpace, oneSpace) + } + + // Feature "name" + doc.formatFeature(obj, SML_ABSTRACT_DECLARATION__NAME, oneSpace, null) + + // EObject "typeParameterList" + doc.formatObject(obj.typeParameterList, noSpace, null) + + // EObject "parameterList" + doc.formatObject(obj.parameterList, noSpace, null) + + // EObject "resultList" + doc.formatObject(obj.resultList, oneSpace, null) + + // EObject "constraintList" + doc.formatObject(obj.constraintList, oneSpace, null) + } + is SmlWorkflow -> { + + // Features "annotations" + doc.formatAnnotations(obj) + + // Keyword "workflow" + if (obj.annotationCallsOrEmpty().isEmpty()) { + doc.formatKeyword(obj, "workflow", noSpace, oneSpace) + } else { + doc.formatKeyword(obj, "workflow", oneSpace, oneSpace) + } + + // Feature "name" + doc.formatFeature(obj, SML_ABSTRACT_DECLARATION__NAME, null, oneSpace) + + // EObject "body" + doc.formatObject(obj.body) + } + is SmlStep -> { + + // Features "annotations" + doc.formatAnnotations(obj) + + // Feature "visibility" + if (obj.annotationCallsOrEmpty().isEmpty()) { + doc.formatFeature(obj, SML_STEP__VISIBILITY, noSpace, null) + } + + // Keyword "step" + if (obj.annotationCallsOrEmpty().isEmpty() && obj.visibility == null) { + doc.formatKeyword(obj, "step", noSpace, oneSpace) + } else { + doc.formatKeyword(obj, "step", oneSpace, oneSpace) + } + + // Feature "name" + doc.formatFeature(obj, SML_ABSTRACT_DECLARATION__NAME, null, noSpace) + + // EObject "parameterList" + doc.formatObject(obj.parameterList) + + // EObject "resultList" + doc.formatObject(obj.resultList) + + // EObject "body" + doc.formatObject(obj.body, oneSpace, null) + } + is SmlArgumentList -> { + + // Keyword "(" + doc.formatKeyword(obj, "(", null, noSpace) + + // Feature "parameters" + obj.arguments.forEach { + doc.formatObject(it) + } + + // Keywords "," + doc.formatCommas(obj) + + // Keyword ")" + doc.formatKeyword(obj, ")", noSpace, null) + } + is SmlArgument -> { + + // Feature "parameter" + doc.formatFeature(obj, SML_ARGUMENT__PARAMETER) + + // Keyword "=" + doc.formatKeyword(obj, "=", oneSpace, oneSpace) + + // EObject "value" + doc.formatObject(obj.value) + } + is SmlParameterList -> { + + // Keyword "(" + doc.formatKeyword(obj, "(", null, noSpace) + + // Feature "parameters" + obj.parameters.forEach { + doc.formatObject(it) + } + + // Keywords "," + doc.formatCommas(obj) + + // Keyword ")" + doc.formatKeyword(obj, ")", noSpace, null) + } + is SmlParameter -> { + + // Features "annotations" + doc.formatAnnotations(obj, inlineAnnotations = true) + + // Keyword "vararg" + if (obj.annotationCallsOrEmpty().isNotEmpty()) { + doc.formatKeyword(obj, "vararg", oneSpace, null) + } + + // Feature "name" + val name = obj.regionForFeature(SML_ABSTRACT_DECLARATION__NAME) + if (obj.annotationCallsOrEmpty().isNotEmpty() || obj.isVariadic) { + doc.prepend(name, oneSpace) + } + + // Keyword ":" + doc.formatKeyword(obj, ":", noSpace, oneSpace) + + // EObject "type" + doc.formatObject(obj.type) + + // Keyword "=" + doc.formatKeyword(obj, "=", oneSpace, oneSpace) + + // EObject "defaultValue" + doc.formatObject(obj.defaultValue) + } + is SmlResultList -> { + + // Keyword "->" + doc.formatKeyword(obj, "->", oneSpace, oneSpace) + + // Keyword "(" + doc.formatKeyword(obj, "(", null, noSpace) + + // Feature "results" + obj.results.forEach { + doc.formatObject(it) + } + + // Keywords "," + doc.formatCommas(obj) + + // Keyword ")" + doc.formatKeyword(obj, ")", noSpace, null) + } + is SmlResult -> { + + // Features "annotations" + doc.formatAnnotations(obj, inlineAnnotations = true) + + // Feature "name" + val name = obj.regionForFeature(SML_ABSTRACT_DECLARATION__NAME) + if (obj.annotationCallsOrEmpty().isNotEmpty()) { + doc.prepend(name, oneSpace) + } + + // Keyword ":" + doc.formatKeyword(obj, ":", noSpace, oneSpace) + + // EObject "type" + doc.formatObject(obj.type) + } + + /********************************************************************************************************** + * Protocols + **********************************************************************************************************/ + + is SmlProtocol -> { + + // Keyword "protocol" + doc.formatKeyword(obj, "protocol", null, oneSpace) + + // EObject "body" + doc.formatObject(obj.body) + } + is SmlProtocolBody -> { + + // Keyword "{" + val openingBrace = obj.regionForKeyword("{") + if (obj.subtermList == null && obj.term == null) { + doc.append(openingBrace, noSpace) + } else { + doc.append(openingBrace, newLine) + } + + // EObject "subtermList" + if (obj.term == null) { + doc.formatObject(obj.subtermList, null, newLine) + } else { + doc.format(obj.subtermList) + doc.append(obj.subtermList, newLines(2)) + } + + // EObject "term" + doc.formatObject(obj.term, null, newLine) + + // Keyword "}" + val closingBrace = obj.regionForKeyword("}") + doc.prepend(closingBrace, noSpace) + + doc.interior(openingBrace, closingBrace, indent) + } + is SmlProtocolSubtermList -> { + obj.subterms.forEach { + if (it === obj.subterms.last()) { + doc.formatObject(it, null, null) + } else { + doc.formatObject(it, null, newLine) + } + } + } + is SmlProtocolSubterm -> { + + // Keyword "subterm" + doc.formatKeyword(obj, "subterm", null, oneSpace) + + // Feature "name" + doc.formatFeature(obj, SML_ABSTRACT_DECLARATION__NAME, oneSpace, oneSpace) + + // Keyword "=" + doc.formatKeyword(obj, "=", oneSpace, oneSpace) + + // EObject "term" + doc.formatObject(obj.term, oneSpace, noSpace) + + // Keyword ";" + doc.formatKeyword(obj, ";", noSpace, null) + } + is SmlProtocolAlternative -> { + + // Keywords '|' + val pipes = textRegionExtensions.allRegionsFor(obj).keywords("|") + pipes.forEach { + doc.prepend(it, oneSpace) + doc.append(it, oneSpace) + } + + // EObject "terms" + obj.terms.forEach { + doc.formatObject(it) + } + } + is SmlProtocolComplement -> { + + // Keyword "[" + doc.formatKeyword(obj, "[", null, noSpace) + + // Keyword "^" + doc.formatKeyword(obj, "^", noSpace, null) + + // EObject "referenceList" + doc.formatObject(obj.referenceList, oneSpace, null) + + // Keyword "]" + doc.formatKeyword(obj, "]", noSpace, null) + } + is SmlProtocolReferenceList -> { + + // EObject "terms" + obj.references.forEach { + if (it == obj.references.last()) { + doc.formatObject(it) + } else { + doc.formatObject(it, null, oneSpace) + } + } + } + is SmlProtocolParenthesizedTerm -> { + + // Keyword "(" + doc.formatKeyword(obj, "(", null, noSpace) + + // EObject "term" + doc.formatObject(obj.term, noSpace, noSpace) + + // Keyword ")" + doc.formatKeyword(obj, ")", noSpace, null) + } + is SmlProtocolQuantifiedTerm -> { + + // EObject "term" + doc.formatObject(obj.term) + + // Feature "quantifier" + doc.formatFeature(obj, SML_PROTOCOL_QUANTIFIED_TERM__QUANTIFIER, noSpace, null) + } + is SmlProtocolSequence -> { + + // EObject "terms" + obj.terms.forEach { + if (it == obj.terms.last()) { + doc.formatObject(it) + } else { + doc.formatObject(it, null, oneSpace) + } + } + } + + /********************************************************************************************************** + * Statements + **********************************************************************************************************/ + + is SmlBlock -> { + + // Keyword "{" + val openingBrace = obj.regionForKeyword("{") + if (obj.statements.isEmpty()) { + doc.append(openingBrace, noSpace) + } else { + doc.append(openingBrace, newLine) + } + + // Feature "statements" + obj.statements.forEach { + doc.formatObject(it, null, newLine) + } + + // Keyword "}" + val closingBrace = obj.regionForKeyword("}") + doc.prepend(closingBrace, noSpace) + + doc.interior(openingBrace, closingBrace, indent) + } + is SmlAssignment -> { + + // EObject "assigneeList" + doc.formatObject(obj.assigneeList, null, oneSpace) + + // Keyword "=" + doc.formatKeyword(obj, "=", oneSpace, oneSpace) + + // EObject "expression" + doc.formatObject(obj.expression) + + // Keyword ";" + doc.formatKeyword(obj, ";", noSpace, null) + } + is SmlAssigneeList -> { + + // Feature "assignees" + obj.assignees.forEach { + doc.formatObject(it) + } + + // Keywords "," + doc.formatCommas(obj) + } + is SmlBlockLambdaResult -> { + + // Keyword "yield" + doc.formatKeyword(obj, "yield", null, oneSpace) + + // Feature "name" + doc.formatFeature(obj, SML_ABSTRACT_DECLARATION__NAME, oneSpace, null) + } + is SmlPlaceholder -> { + + // Keyword "val" + doc.formatKeyword(obj, "val", null, oneSpace) + + // Feature "name" + doc.formatFeature(obj, SML_ABSTRACT_DECLARATION__NAME, oneSpace, null) + } + is SmlYield -> { + + // Keyword "yield" + doc.formatKeyword(obj, "yield", null, oneSpace) + + // Feature "result" + doc.formatFeature(obj, SML_YIELD__RESULT) + } + is SmlExpressionStatement -> { + + // EObject "expression" + doc.formatObject(obj.expression) + + // Keyword ";" + doc.formatKeyword(obj, ";", noSpace, null) + } + + /********************************************************************************************************** + * Expressions + **********************************************************************************************************/ + + is SmlBlockLambda -> { + + // EObject "parameterList" + doc.formatObject(obj.parameterList, null, oneSpace) + + // EObject "body" + doc.formatObject(obj.body, oneSpace, null) + } + is SmlCall -> { + + // EObject "receiver" + doc.formatObject(obj.receiver, null, noSpace) + + // EObject "typeArgumentList" + doc.formatObject(obj.typeArgumentList, null, noSpace) + + // EObject "argumentList" + doc.formatObject(obj.argumentList) + } + is SmlExpressionLambda -> { + + // EObject "parameterList" + doc.formatObject(obj.parameterList, null, oneSpace) + + // Keyword "->" + doc.formatKeyword(obj, "->", oneSpace, oneSpace) + + // EObject "result" + doc.formatObject(obj.result, oneSpace, null) + } + is SmlIndexedAccess -> { + + // EObject "receiver" + doc.formatObject(obj.receiver, null, noSpace) + + // Keyword "[" + doc.formatKeyword(obj, "[", noSpace, noSpace) + + // EObject "index" + doc.formatObject(obj.index, noSpace, noSpace) + + // Keyword "]" + doc.formatKeyword(obj, "]", noSpace, null) + } + is SmlInfixOperation -> { + + // EObject "leftOperand" + doc.formatObject(obj.leftOperand, null, oneSpace) + + // Feature "operator" + doc.formatFeature(obj, SML_INFIX_OPERATION__OPERATOR, oneSpace, oneSpace) + + // EObject "rightOperand" + doc.formatObject(obj.rightOperand, oneSpace, null) + } + is SmlMemberAccess -> { + + // EObject "receiver" + doc.formatObject(obj.receiver, null, noSpace) + + // Feature "nullable" + doc.formatFeature(obj, SML_MEMBER_ACCESS__NULL_SAFE, noSpace, noSpace) + + // Keyword "." + doc.formatKeyword(obj, ".", noSpace, noSpace) + + // EObject "member" + doc.formatObject(obj.member, noSpace, null) + } + is SmlParenthesizedExpression -> { + + // Keyword "(" + doc.formatKeyword(obj, "(", null, noSpace) + + // EObject "expression" + doc.formatObject(obj.expression, noSpace, noSpace) + + // Keyword ")" + doc.formatKeyword(obj, ")", noSpace, null) + } + is SmlPrefixOperation -> { + + // Feature "operator" + doc.formatFeature( + obj, + SML_PREFIX_OPERATION__OPERATOR, + prepend = null, + append = if (obj.operator == "not") oneSpace else noSpace + ) + + // EObject "operand" + doc.formatObject(obj.operand) + } + is SmlTemplateString -> { + + // Feature expressions + obj.expressions.forEach { + if (it !is SmlAbstractTemplateStringPart) { + doc.formatObject(it, oneSpace, oneSpace) + } + } + } + + /********************************************************************************************************** + * Types + **********************************************************************************************************/ + + is SmlCallableType -> { + + // Keyword "callable" + doc.formatKeyword(obj, "callable", null, oneSpace) + + // EObject "parameterList" + doc.formatObject(obj.parameterList, oneSpace, oneSpace) + + // EObject "resultList" + doc.formatObject(obj.resultList, oneSpace, null) + } + is SmlMemberType -> { + + // EObject "receiver" + doc.formatObject(obj.receiver, null, noSpace) + + // Keyword "." + doc.formatKeyword(obj, ".", noSpace, noSpace) + + // EObject "member" + doc.formatObject(obj.member, noSpace, null) + } + is SmlNamedType -> { + + // Feature "declaration" + doc.formatFeature(obj, SML_NAMED_TYPE__DECLARATION) + + // EObject "typeArgumentList" + doc.formatObject(obj.typeArgumentList, noSpace, noSpace) + + // Feature "nullable" + doc.formatFeature(obj, SML_NAMED_TYPE__NULLABLE, noSpace, null) + } + is SmlParenthesizedType -> { + + // Keyword "(" + doc.formatKeyword(obj, "(", null, noSpace) + + // EObject "type" + doc.formatObject(obj.type, noSpace, noSpace) + + // Keyword ")" + doc.formatKeyword(obj, ")", noSpace, null) + } + is SmlUnionType -> { + + // Keyword "union" + doc.formatKeyword(obj, "union", null, noSpace) + + // EObject "typeArgumentList" + doc.formatObject(obj.typeArgumentList, noSpace, null) + } + is SmlTypeArgumentList -> { + + // Keyword "<" + doc.formatKeyword(obj, "<", null, noSpace) + + // Feature "typeArguments" + obj.typeArguments.forEach { + doc.formatObject(it) + } + + // Keywords "," + doc.formatCommas(obj) + + // Keyword ">" + doc.formatKeyword(obj, ">", noSpace, null) + } + is SmlTypeArgument -> { + + // Feature "typeParameter" + doc.formatFeature(obj, SML_TYPE_ARGUMENT__TYPE_PARAMETER) + + // Keyword "=" + doc.formatKeyword(obj, "=", oneSpace, oneSpace) + + // EObject "value" + doc.formatObject(obj.value) + } + is SmlTypeProjection -> { + + // Feature "variance" + doc.formatFeature(obj, SML_TYPE_PROJECTION__VARIANCE, null, oneSpace) + + // EObject "type" + doc.formatObject(obj.type) + } + is SmlTypeParameterList -> { + + // Keyword "<" + doc.formatKeyword(obj, "<", null, noSpace) + + // Feature "typeParameters" + obj.typeParameters.forEach { + doc.formatObject(it) + } + + // Keywords "," + doc.formatCommas(obj) + + // Keyword ">" + doc.formatKeyword(obj, ">", noSpace, null) + } + is SmlTypeParameter -> { + + // Features "annotations" + doc.formatAnnotations(obj, inlineAnnotations = true) + + // Feature "variance" + if (obj.annotationCallsOrEmpty().isEmpty()) { + doc.formatFeature(obj, SML_TYPE_PARAMETER__VARIANCE, null, oneSpace) + } else { + doc.formatFeature(obj, SML_TYPE_PARAMETER__VARIANCE, oneSpace, oneSpace) + } + + // Feature "name" + doc.formatFeature(obj, SML_ABSTRACT_DECLARATION__NAME) + } + is SmlConstraintList -> { + + // Keyword "where" + doc.formatKeyword(obj, "where", null, oneSpace) + + // Feature "constraints" + obj.constraints.forEach { + doc.formatObject(it) + } + + // Keywords "," + doc.formatCommas(obj) + } + is SmlTypeParameterConstraint -> { + + // Feature "leftOperand" + doc.formatFeature(obj, SML_TYPE_PARAMETER_CONSTRAINT__LEFT_OPERAND, null, oneSpace) + + // Feature "operator" + doc.formatFeature(obj, SML_TYPE_PARAMETER_CONSTRAINT__OPERATOR, oneSpace, oneSpace) + + // EObject "rightOperand" + doc.formatObject(obj.rightOperand, oneSpace, null) + } + } + } + + /** + * Formats comments, including test markers. Without this override formatting a file with test markers throws an + * exception in VS Code. + */ + override fun createCommentReplacer(comment: IComment): ITextReplacer? { + val grammarElement = comment.grammarElement + if (grammarElement is TerminalRule && grammarElement.name == "TEST_MARKER") { + return TestMarkerReplacer(comment) + } + + return super.createCommentReplacer(comment) + } + + /****************************************************************************************************************** + * Helpers + ******************************************************************************************************************/ + + private fun useSpacesForIndentation() { + val newPreferences = mutableMapOf() + newPreferences[indentation.id] = " " + request.preferences = MapBasedPreferenceValues(preferences, newPreferences) + } + + private fun EObject.regionForFeature(feature: EStructuralFeature): ISemanticRegion? { + return textRegionExtensions.regionFor(this).feature(feature) + } + + private fun EObject.regionForKeyword(keyword: String): ISemanticRegion? { + return textRegionExtensions.regionFor(this).keyword(keyword) + } + + private fun IFormattableDocument.formatObject( + obj: EObject?, + prepend: KFunction1? = null, + append: KFunction1? = null + ) { + if (obj != null) { + if (prepend != null) { + this.prepend(obj, prepend) + } + this.format(obj) + if (append != null) { + this.append(obj, append) + } + } + } + + private fun IFormattableDocument.formatFeature( + obj: EObject?, + feature: EStructuralFeature, + prepend: KFunction1? = null, + append: KFunction1? = null + ) { + if (obj == null) { + return + } + + val featureRegion = obj.regionForFeature(feature) + if (featureRegion != null) { + if (prepend != null) { + this.prepend(featureRegion, prepend) + } + if (append != null) { + this.append(featureRegion, append) + } + } + } + + private fun IFormattableDocument.formatKeyword( + obj: EObject?, + keyword: String, + prepend: KFunction1? = null, + append: KFunction1? = null + ) { + if (obj == null) { + return + } + + val keywordRegion = obj.regionForKeyword(keyword) + if (keywordRegion != null) { + if (prepend != null) { + this.prepend(keywordRegion, prepend) + } + if (append != null) { + this.append(keywordRegion, append) + } + } + } + + private fun IFormattableDocument.formatAnnotations( + obj: SmlAbstractDeclaration, + inlineAnnotations: Boolean = false + ) { + + // Feature "annotations" + obj.annotationCallsOrEmpty().forEach { + format(it) + + if (inlineAnnotations) { + append(it, oneSpace) + } else { + append(it, newLine) + } + } + } + + private fun IFormattableDocument.formatCommas(obj: EObject) { + val commas = textRegionExtensions.allRegionsFor(obj).keywords(",") + commas.forEach { + prepend(it, noSpace) + append(it, oneSpace) + } + } +} + +class TestMarkerReplacer(comment: IComment) : CommentReplacer(comment) { + override fun createReplacements(context: ITextReplacerContext): ITextReplacerContext { + return context + } + + override fun configureWhitespace(leading: WhitespaceReplacer, trailing: WhitespaceReplacer) { + if (comment.text == "»") { + trailing.formatting.space = "" + } else if (comment.text == "«") { + leading.formatting.space = "" + } + } +} + +class WhitespaceCollapser(doc: IFormattableDocument, name: ISemanticRegion?) : AbstractTextReplacer(doc, name) { + override fun createReplacements(context: ITextReplacerContext): ITextReplacerContext { + context.addReplacement(collapseWhitespace()) + return context + } + + private fun collapseWhitespace(): ITextReplacement { + return region.replaceWith(region.text.replace(Regex("\\s+"), "")) + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/generator/GeneratorUtils.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/generator/GeneratorUtils.kt new file mode 100644 index 000000000..a03a0cbb9 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/generator/GeneratorUtils.kt @@ -0,0 +1,43 @@ +package de.unibonn.simpleml.generator + +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.emf.compilationUnitOrNull +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.stdlibAccess.pythonModuleOrNull +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecore.resource.Resource + +/** + * Returns the base file name of the resource, i.e. the last segment of its [URI] with any Simple-ML extension removed, + * or `null` if the resource has no [URI]. + */ +fun Resource.baseFileNameOrNull(): String? { + return uri + ?.lastSegment() + ?.removeSuffix(".${SmlFileExtension.Stub}") + ?.removeSuffix(".${SmlFileExtension.Test}") + ?.removeSuffix(".${SmlFileExtension.Flow}") + ?.replace(Regex("%2520"), "_") // Twice URL encoded space + ?.replace(Regex("[ .-]"), "_") + ?.replace(Regex("\\W"), "") +} + +/** + * Returns the prefix of the path of all generated files, or `null` if this [Resource] does not provide enough + * information to deduce this prefix. This can be caused if either + * - the [Resource] contains no [SmlCompilationUnit], + * - the [SmlCompilationUnit] has no package, + * - the [Resource] has no [URI]. + */ +fun Resource.baseGeneratedFilePathOrNull(): String? { + val compilationUnit = compilationUnitOrNull() ?: return null + + val compilationUnitPythonName = compilationUnit.pythonModuleOrNull() ?: compilationUnit.name + val packagePart = compilationUnitPythonName + ?.replace(".", "/") + ?: return null + + val filePart = baseFileNameOrNull() ?: return null + + return "$packagePart/gen_$filePart" +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/generator/Main.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/generator/Main.kt new file mode 100644 index 000000000..486a382cb --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/generator/Main.kt @@ -0,0 +1,73 @@ +package de.unibonn.simpleml.generator + +import com.google.inject.Inject +import com.google.inject.Provider +import de.unibonn.simpleml.SimpleMLStandaloneSetup +import de.unibonn.simpleml.stdlibAccess.loadStdlib +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecore.resource.ResourceSet +import org.eclipse.xtext.diagnostics.Severity +import org.eclipse.xtext.generator.GeneratorContext +import org.eclipse.xtext.generator.GeneratorDelegate +import org.eclipse.xtext.generator.JavaIoFileSystemAccess +import org.eclipse.xtext.util.CancelIndicator +import org.eclipse.xtext.validation.CheckMode +import org.eclipse.xtext.validation.IResourceValidator +import kotlin.system.exitProcess + +@Suppress("unused") +class Main @Inject constructor( + private val fileAccess: JavaIoFileSystemAccess, + private val generator: GeneratorDelegate, + private val resourceSetProvider: Provider, + private val validator: IResourceValidator +) { + + fun runCodeGenerator(files: List) { + + // Load the resources + val resourceSet = resourceSetProvider.get() + files.forEach { + resourceSet.getResource(URI.createFileURI(it), true) + ?: throw IllegalArgumentException("Could not create resource for $it.") + } + + // Load the library + resourceSet.loadStdlib() + + // Configure the generator + fileAccess.setOutputPath("src-gen/") + val context = GeneratorContext().apply { + cancelIndicator = CancelIndicator.NullImpl + } + + // Generate all resources + resourceSet.resources.forEach { resource -> + + // Validate the resource + val issues = validator.validate(resource, CheckMode.ALL, CancelIndicator.NullImpl) + if (issues.any { it.severity == Severity.ERROR }) { + issues.forEach { println(it) } + + System.err.println("Aborting: A resource has errors.") + exitProcess(20) + } + + // Start the generator + generator.generate(resource, fileAccess, context) + } + + println("Code generation finished.") + } +} + +fun main(args: Array) { + if (args.isEmpty()) { + System.err.println("Aborting: No path to EMF resource provided.") + exitProcess(10) + } + + val injector = SimpleMLStandaloneSetup().createInjectorAndDoEMFRegistration() + val main = injector.getInstance(Main::class.java) + main.runCodeGenerator(args.toList()) +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/generator/SimpleMLGenerator.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/generator/SimpleMLGenerator.kt new file mode 100644 index 000000000..5f121db92 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/generator/SimpleMLGenerator.kt @@ -0,0 +1,714 @@ +package de.unibonn.simpleml.generator + +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.And +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.Elvis +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.IdenticalTo +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.NotIdenticalTo +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.Or +import de.unibonn.simpleml.constant.SmlPrefixOperationOperator +import de.unibonn.simpleml.constant.isFlowFile +import de.unibonn.simpleml.constant.isInFlowFile +import de.unibonn.simpleml.constant.isInTestFile +import de.unibonn.simpleml.constant.isTestFile +import de.unibonn.simpleml.constant.operator +import de.unibonn.simpleml.emf.assigneesOrEmpty +import de.unibonn.simpleml.emf.blockLambdaResultsOrEmpty +import de.unibonn.simpleml.emf.closestAncestorOrNull +import de.unibonn.simpleml.emf.compilationUnitOrNull +import de.unibonn.simpleml.emf.containingBlockLambdaOrNull +import de.unibonn.simpleml.emf.containingCompilationUnitOrNull +import de.unibonn.simpleml.emf.createSmlWildcard +import de.unibonn.simpleml.emf.descendants +import de.unibonn.simpleml.emf.isGlobal +import de.unibonn.simpleml.emf.isOptional +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.placeholdersOrEmpty +import de.unibonn.simpleml.emf.resultsOrEmpty +import de.unibonn.simpleml.emf.statementsOrEmpty +import de.unibonn.simpleml.naming.qualifiedNameOrNull +import de.unibonn.simpleml.simpleML.SmlAbstractAssignee +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlAbstractExpression +import de.unibonn.simpleml.simpleML.SmlAbstractStatement +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlArgumentList +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlBlockLambda +import de.unibonn.simpleml.simpleML.SmlBlockLambdaResult +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlExpressionLambda +import de.unibonn.simpleml.simpleML.SmlExpressionStatement +import de.unibonn.simpleml.simpleML.SmlIndexedAccess +import de.unibonn.simpleml.simpleML.SmlInfixOperation +import de.unibonn.simpleml.simpleML.SmlMemberAccess +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlParenthesizedExpression +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlPrefixOperation +import de.unibonn.simpleml.simpleML.SmlReference +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlResultList +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlTemplateString +import de.unibonn.simpleml.simpleML.SmlTemplateStringEnd +import de.unibonn.simpleml.simpleML.SmlTemplateStringInner +import de.unibonn.simpleml.simpleML.SmlTemplateStringStart +import de.unibonn.simpleml.simpleML.SmlWildcard +import de.unibonn.simpleml.simpleML.SmlWorkflow +import de.unibonn.simpleml.simpleML.SmlYield +import de.unibonn.simpleml.staticAnalysis.linking.parameterOrNull +import de.unibonn.simpleml.staticAnalysis.linking.parametersOrNull +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.SmlConstantBoolean +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.SmlConstantEnumVariant +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.SmlConstantFloat +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.SmlConstantInt +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.SmlConstantNull +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.SmlConstantString +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.toConstantExpressionOrNull +import de.unibonn.simpleml.staticAnalysis.resultsOrNull +import de.unibonn.simpleml.staticAnalysis.statementHasNoSideEffects +import de.unibonn.simpleml.stdlibAccess.pythonModuleOrNull +import de.unibonn.simpleml.stdlibAccess.pythonNameOrNull +import de.unibonn.simpleml.utils.IdManager +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.xtext.generator.AbstractGenerator +import org.eclipse.xtext.generator.IFileSystemAccess2 +import org.eclipse.xtext.generator.IGeneratorContext + +/** + * Generates code from your model files on save. + * + * See [Xtext Code Generation](https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation). + */ +class SimpleMLGenerator : AbstractGenerator() { + + private val codegenPackage = "simpleml.codegen" + private val runtimeBridgePackage = "runtimeBridge" + private val indent = " " + + /** + * Creates Python workflow and declaration files if the [resource] is either a Simple-ML flow or test file. + */ + override fun doGenerate(resource: Resource, fsa: IFileSystemAccess2, context: IGeneratorContext) { + if (resource.isFlowFile() || resource.isTestFile()) { + generateWorkflowFiles(resource, fsa, context) + generateDeclarationFile(resource, fsa, context) + } + } + + /** + * Creates one Python file for each workflow in the given resource that just contains a main block that calls the + * workflow. This way we can run the Python interpreter with the created file to run the workflow. + * + * **Example:** Given the following situation + * * Simple-ML package: "com.example" + * * Simple-ML file: "test.simpleml" + * * Workflow names: "workflow1", "workflow2" + * + * we create two files in the folder "com/example" (determined by the Simple-ML package). The file for "workflow1" + * is called "test_workflow1.py" and the file for "workflow2" is called "test_workflow2.py". The names are created + * by taking the Simple-ML file name, removing the file extension, appending an underscore, and then the workflow + * name. + */ + private fun generateWorkflowFiles(resource: Resource, fsa: IFileSystemAccess2, context: IGeneratorContext) { + resource.allContents.asSequence() + .filterIsInstance() + .forEach { + if (context.cancelIndicator.isCanceled) { + return + } + + val fileName = "${resource.baseGeneratedFilePathOrNull()}_${it.correspondingPythonName()}.py" + val content = """ + |from gen_${resource.baseFileNameOrNull()} import ${it.correspondingPythonName()} + | + |if __name__ == '__main__': + |$indent${it.correspondingPythonName()}() + | + """.trimMargin() + + fsa.generateFile(fileName, content) + } + } + + private fun generateDeclarationFile(resource: Resource, fsa: IFileSystemAccess2, context: IGeneratorContext) { + if (context.cancelIndicator.isCanceled) { + return + } + + val fileName = "${resource.baseGeneratedFilePathOrNull()}.py" + val compilationUnit = resource.compilationUnitOrNull() ?: return + val content = compile(compilationUnit) + + fsa.generateFile(fileName, content) + } + + private fun compile(compilationUnit: SmlCompilationUnit): String { + val imports = mutableSetOf() + + // Compile steps + val stepString = compilationUnit + .descendants() + .sortedBy { it.name } + .joinToString("\n") { + compileWorkflowSteps(it, imports) + } + + // Compile workflows + val workflowString = compilationUnit + .descendants() + .sortedBy { it.name } + .joinToString("\n") { + compileWorkflow(it, imports) + } + + return buildString { + + // Imports + val importsString = compileImports(imports) + if (importsString.isNotBlank()) { + appendLine("# Imports ----------------------------------------------------------------------\n") + appendLine(importsString) + } + + // Steps + if (stepString.isNotBlank()) { + appendLine("# Steps ------------------------------------------------------------------------\n") + append(stepString) + } + + // Workflows + if (workflowString.isNotBlank()) { + if (stepString.isNotBlank()) { + appendLine() + } + appendLine("# Workflows --------------------------------------------------------------------\n") + append(workflowString) + } + } + } + + private fun compileImports(imports: Set) = buildString { + + // Qualified imports + imports + .filter { it.declarationName == null } + .sortedBy { it.importPath } + .forEach { + appendLine(it.toString()) + } + + // From-imports + imports + .filter { it.declarationName != null } + .groupBy { it.importPath } + .entries + .sortedBy { it.key } + .forEach { (key, value) -> + val declarationNames = value + .sortedBy { it.declarationName } + .joinToString { + when (it.alias) { + null -> it.declarationName!! + else -> "${it.declarationName} as ${it.alias}" + } + } + appendLine("from $key import $declarationNames") + } + } + + @OptIn(ExperimentalStdlibApi::class) + private fun compileWorkflowSteps(step: SmlStep, imports: MutableSet) = buildString { + val blockLambdaIdManager = IdManager() + + append("def ${step.correspondingPythonName()}(") + append( + step.parametersOrEmpty().joinToString { + compileParameter(CompileParameterFrame(it, imports, blockLambdaIdManager)) + } + ) + appendLine("):") + + if (step.statementsOrEmpty().withEffect().isEmpty()) { + appendLine("${indent}pass") + } else { + step.statementsOrEmpty().withEffect().forEach { + val statement = compileStatement( + CompileStatementFrame( + it, + imports, + blockLambdaIdManager, + shouldSavePlaceholders = false + ) + ).prependIndent(indent) + appendLine(statement) + } + + if (step.resultsOrEmpty().isNotEmpty()) { + appendLine("${indent}return ${step.resultsOrEmpty().joinToString { it.name }}") + } + } + } + + @OptIn(ExperimentalStdlibApi::class) + private fun compileWorkflow(workflow: SmlWorkflow, imports: MutableSet) = buildString { + val blockLambdaIdManager = IdManager() + + appendLine("def ${workflow.correspondingPythonName()}():") + if (workflow.statementsOrEmpty().withEffect().isEmpty()) { + appendLine("${indent}pass") + } else { + workflow.statementsOrEmpty().withEffect().forEach { + appendLine( + compileStatement( + CompileStatementFrame(it, imports, blockLambdaIdManager, shouldSavePlaceholders = true) + ).prependIndent(indent) + ) + } + } + } + + private data class CompileStatementFrame( + val stmt: SmlAbstractStatement, + val imports: MutableSet, + val blockLambdaIdManager: IdManager, + val shouldSavePlaceholders: Boolean + ) + + @OptIn(ExperimentalStdlibApi::class) + private val compileStatement: DeepRecursiveFunction = + DeepRecursiveFunction { (stmt, imports, blockLambdaIdManager, shouldSavePlaceholders) -> + val stringBuilder = StringBuilder() + when (stmt) { + is SmlAssignment -> { + for (lambda in stmt.expression.descendants()) { + stringBuilder.append( + compileBlockLambda.callRecursive( + CompileBlockLambdaFrame( + lambda, + imports, + blockLambdaIdManager + ) + ) + ) + } + + if (stmt.assigneesOrEmpty().any { it !is SmlWildcard }) { + val assignees = stmt.paddedAssignees().joinToString { + when (it) { + is SmlBlockLambdaResult -> it.name + is SmlPlaceholder -> it.name + is SmlYield -> it.result.name + else -> "_" + } + } + stringBuilder.append("$assignees = ") + } + stringBuilder.append( + compileExpression.callRecursive( + CompileExpressionFrame(stmt.expression, imports, blockLambdaIdManager) + ) + ) + + if (shouldSavePlaceholders) { + stmt.placeholdersOrEmpty().forEach { + imports += ImportData(runtimeBridgePackage) + stringBuilder.append("\n$runtimeBridgePackage.save_placeholder('${it.name}', ${it.name})") + } + } + } + is SmlExpressionStatement -> { + for (lambda in stmt.expression.descendants()) { + stringBuilder.append( + compileBlockLambda.callRecursive( + CompileBlockLambdaFrame( + lambda, + imports, + blockLambdaIdManager + ) + ) + ) + } + + stringBuilder.append( + compileExpression.callRecursive( + CompileExpressionFrame(stmt.expression, imports, blockLambdaIdManager) + ) + ) + } + else -> throw java.lang.IllegalStateException("Missing case to handle statement $stmt.") + } + + stringBuilder.toString() + } + + private data class CompileBlockLambdaFrame( + val lambda: SmlBlockLambda, + val imports: MutableSet, + val blockLambdaIdManager: IdManager + ) + + @OptIn(ExperimentalStdlibApi::class) + private val compileBlockLambda: DeepRecursiveFunction = + DeepRecursiveFunction { (lambda, imports, blockLambdaIdManager) -> + val stringBuilder = StringBuilder() + + // Header + stringBuilder.append("def ${lambda.uniqueName(blockLambdaIdManager)}(") + val parameters = mutableListOf() + for (parameter in lambda.parametersOrEmpty()) { + parameters += compileParameter.callRecursive( + CompileParameterFrame( + parameter, + imports, + blockLambdaIdManager + ) + ) + } + stringBuilder.append(parameters.joinToString()) + stringBuilder.appendLine("):") + + // Statements + if (lambda.statementsOrEmpty().withEffect().isEmpty()) { + stringBuilder.appendLine("${indent}pass") + } else { + for (stmt in lambda.statementsOrEmpty().withEffect()) { + stringBuilder.appendLine( + compileStatement.callRecursive( + CompileStatementFrame( + stmt, + imports, + blockLambdaIdManager, + shouldSavePlaceholders = false + ) + ).prependIndent(indent) + ) + } + + if (lambda.blockLambdaResultsOrEmpty().isNotEmpty()) { + stringBuilder.appendLine( + "${indent}return ${ + lambda.blockLambdaResultsOrEmpty().joinToString { it.name } + }" + ) + } + } + + stringBuilder.toString() + } + + private data class CompileParameterFrame( + val parameter: SmlParameter, + val imports: MutableSet, + val blockLambdaIdManager: IdManager + ) + + @OptIn(ExperimentalStdlibApi::class) + private val compileParameter: DeepRecursiveFunction = + DeepRecursiveFunction { (parameter, imports, blockLambdaIdManager) -> + when { + parameter.isOptional() -> { + val defaultValue = compileExpression.callRecursive( + CompileExpressionFrame(parameter.defaultValue, imports, blockLambdaIdManager) + ) + "${parameter.correspondingPythonName()}=$defaultValue" + } + parameter.isVariadic -> "*${parameter.correspondingPythonName()}" + else -> parameter.correspondingPythonName() + } + } + + private data class CompileExpressionFrame( + val expression: SmlAbstractExpression, + val imports: MutableSet, + val blockLambdaIdManager: IdManager + ) + + @OptIn(ExperimentalStdlibApi::class) + private val compileExpression: DeepRecursiveFunction = + DeepRecursiveFunction { (expr, imports, blockLambdaIdManager) -> + + // Template string parts + when (expr) { + is SmlTemplateStringStart -> return@DeepRecursiveFunction "${expr.value.toSingleLine()}{ " + is SmlTemplateStringInner -> return@DeepRecursiveFunction " }${expr.value.toSingleLine()}{ " + is SmlTemplateStringEnd -> return@DeepRecursiveFunction " }${expr.value.toSingleLine()}" + } + + // Constant expressions + val constantExpr = expr.toConstantExpressionOrNull() + if (constantExpr != null) { + when (constantExpr) { + is SmlConstantBoolean -> return@DeepRecursiveFunction if (constantExpr.value) "True" else "False" + is SmlConstantEnumVariant -> { + /* let remaining code handle this */ + } + is SmlConstantFloat -> return@DeepRecursiveFunction constantExpr.value.toString() + is SmlConstantInt -> return@DeepRecursiveFunction constantExpr.value.toString() + is SmlConstantNull -> return@DeepRecursiveFunction "None" + is SmlConstantString -> return@DeepRecursiveFunction "'${constantExpr.value.toSingleLine()}'" + } + } + + // Other + return@DeepRecursiveFunction when (expr) { + is SmlBlockLambda -> { + expr.uniqueName(blockLambdaIdManager) + } + is SmlCall -> { + val receiver = callRecursive(CompileExpressionFrame(expr.receiver, imports, blockLambdaIdManager)) + val arguments = mutableListOf() + for (argument in expr.argumentList.sortedByParameter()) { + val value = callRecursive(CompileExpressionFrame(argument.value, imports, blockLambdaIdManager)) + arguments += if (argument.parameterOrNull()?.isOptional() == true) { + "${argument.parameterOrNull()?.correspondingPythonName()}=$value" + } else { + value + } + } + + "$receiver(${arguments.joinToString()})" + } + is SmlExpressionLambda -> { + val parameters = mutableListOf() + for (parameter in expr.parametersOrEmpty()) { + parameters += compileParameter.callRecursive( + CompileParameterFrame( + parameter, + imports, + blockLambdaIdManager + ) + ) + } + val result = callRecursive(CompileExpressionFrame(expr.result, imports, blockLambdaIdManager)) + + "lambda ${parameters.joinToString()}: $result" + } + is SmlInfixOperation -> { + val leftOperand = + callRecursive(CompileExpressionFrame(expr.leftOperand, imports, blockLambdaIdManager)) + val rightOperand = + callRecursive(CompileExpressionFrame(expr.rightOperand, imports, blockLambdaIdManager)) + when (expr.operator()) { + Or -> { + imports += ImportData(codegenPackage) + "$codegenPackage.eager_or($leftOperand, $rightOperand)" + } + And -> { + imports += ImportData(codegenPackage) + "$codegenPackage.eager_and($leftOperand, $rightOperand)" + } + IdenticalTo -> "($leftOperand) is ($rightOperand)" + NotIdenticalTo -> "($leftOperand) is not ($rightOperand)" + Elvis -> { + imports += ImportData(codegenPackage) + "$codegenPackage.eager_elvis($leftOperand, $rightOperand)" + } + else -> "($leftOperand) ${expr.operator} ($rightOperand)" + } + } + is SmlIndexedAccess -> { + val receiver = callRecursive(CompileExpressionFrame(expr.receiver, imports, blockLambdaIdManager)) + val index = callRecursive(CompileExpressionFrame(expr.index, imports, blockLambdaIdManager)) + "$receiver[$index]" + } + is SmlMemberAccess -> { + val receiver = callRecursive(CompileExpressionFrame(expr.receiver, imports, blockLambdaIdManager)) + when (val memberDeclaration = expr.member.declaration) { + is SmlBlockLambdaResult -> { + val allResults = memberDeclaration.containingBlockLambdaOrNull()!!.blockLambdaResultsOrEmpty() + if (allResults.size == 1) { + receiver + } else { + val thisIndex = allResults.indexOf(memberDeclaration) + "$receiver[$thisIndex]" + } + } + is SmlEnumVariant -> { + val member = + callRecursive(CompileExpressionFrame(expr.member, imports, blockLambdaIdManager)) + + val suffix = when (expr.eContainer()) { + is SmlCall -> "" + else -> "()" + } + + when { + expr.isNullSafe -> { + imports += ImportData(codegenPackage) + "$codegenPackage.safe_access($receiver, '$member')$suffix" + } + else -> "$receiver.$member$suffix" + } + } + is SmlResult -> { + val allResults = memberDeclaration.closestAncestorOrNull()!!.results + if (allResults.size == 1) { + receiver + } else { + val thisIndex = allResults.indexOf(memberDeclaration) + "$receiver[$thisIndex]" + } + } + else -> { + val member = + callRecursive(CompileExpressionFrame(expr.member, imports, blockLambdaIdManager)) + when { + expr.isNullSafe -> { + imports += ImportData(codegenPackage) + "$codegenPackage.safe_access($receiver, '$member')" + } + else -> "$receiver.$member" + } + } + } + } + is SmlParenthesizedExpression -> { + callRecursive(CompileExpressionFrame(expr.expression, imports, blockLambdaIdManager)) + } + is SmlPrefixOperation -> { + val operand = callRecursive(CompileExpressionFrame(expr.operand, imports, blockLambdaIdManager)) + when (expr.operator()) { + SmlPrefixOperationOperator.Not -> "not ($operand)" + SmlPrefixOperationOperator.Minus -> "-($operand)" + } + } + is SmlReference -> { + val importAlias = expr.containingCompilationUnitOrNull() + ?.imports + ?.firstOrNull { it.importedNamespace == expr.declaration.qualifiedNameOrNull().toString() } + ?.alias + ?.name + + // Add import as needed + val declaration = expr.declaration + if (declaration.isGlobal() && declaration.containingCompilationUnitOrNull() != expr.containingCompilationUnitOrNull()) { + val importPath = declaration + .containingCompilationUnitOrNull() + ?.correspondingPythonModule() + ?.split(".") + .orEmpty() + .toMutableList() + + if (importPath.isNotEmpty()) { + if (declaration.isInFlowFile() || declaration.isInTestFile()) { + val fileName = declaration.eResource().baseFileNameOrNull() + importPath += "gen_$fileName" + + if (fileName != null) { + imports += ImportData( + importPath.joinToString("."), + declaration.correspondingPythonName(), + importAlias + ) + } + } else { + imports += ImportData( + importPath.joinToString("."), + declaration.correspondingPythonName(), + importAlias + ) + } + } + } + + importAlias ?: declaration.correspondingPythonName() + } + is SmlTemplateString -> { + val substrings = mutableListOf() + for (expression in expr.expressions) { + substrings += callRecursive(CompileExpressionFrame(expression, imports, blockLambdaIdManager)) + } + "f'${substrings.joinToString("")}'" + } + else -> throw java.lang.IllegalStateException("Missing case to handle expression $expr.") + } + } +} + +/** + * Returns the name of the Python declaration that corresponds to this [SmlAbstractDeclaration]. + */ +private fun SmlAbstractDeclaration.correspondingPythonName(): String { + return pythonNameOrNull() ?: name +} + +/** + * Returns the name of the Python module that corresponds to this [SmlCompilationUnit]. + */ +private fun SmlCompilationUnit.correspondingPythonModule(): String { + return pythonModuleOrNull() ?: name +} + +/** + * Adds wildcards at the end of the assignee list until every value of the right-hand side is captured. + */ +private fun SmlAssignment.paddedAssignees(): List { + val desiredNumberOfAssignees = when (val expression = this.expression) { + is SmlCall -> expression.resultsOrNull()?.size ?: 0 + else -> 1 + } + + return buildList { + addAll(assigneesOrEmpty()) + while (size < desiredNumberOfAssignees) { + add(createSmlWildcard()) + } + } +} + +/** + * Returns a unique but consistent name for this lambda. + */ +private fun SmlBlockLambda.uniqueName(blockLambdaIdManager: IdManager): String { + val id = blockLambdaIdManager.assignIdIfAbsent(this).value + return "__block_lambda_$id" +} + +/** + * Returns a new list that only contains the [SmlAbstractStatement] that have some effect. + */ +private fun List.withEffect(): List { + return this.filter { !it.statementHasNoSideEffects() } +} + +/** + * Returns a new list with the arguments in the same order as the corresponding parameters. + */ +private fun SmlArgumentList?.sortedByParameter(): List { + val parameters = this?.parametersOrNull() ?: return emptyList() + val arguments = this.arguments + + return buildList { + parameters.forEach { parameter -> + addAll(arguments.filter { it.parameterOrNull() == parameter }) + } + } +} + +/** + * Escapes newlines. + */ +private fun String.toSingleLine(): String { + return replace("\n", "\\n") +} + +/** + * Stores information about the imports that should be generated + */ +private data class ImportData( + val importPath: String, + val declarationName: String? = null, + val alias: String? = null +) { + override fun toString(): String { + return when { + declarationName == null && alias == null -> "import $importPath" + declarationName == null && alias != null -> "import $importPath as $alias" + declarationName != null && alias == null -> "from $importPath import $declarationName" + else -> "from $importPath import $declarationName as $alias" + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/LspPosition.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/LspPosition.kt new file mode 100644 index 000000000..3aa4225e7 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/LspPosition.kt @@ -0,0 +1,85 @@ +package de.unibonn.simpleml.location + +/** + * A specific position in a program using the zero-based indexing of LSP. + */ +data class LspPosition(val line: LspLine, val column: LspColumn) : Comparable { + companion object { + + @JvmStatic + fun fromInts(line: Int, column: Int): LspPosition { + return LspPosition( + LspLine(line), + LspColumn(column) + ) + } + } + + fun toXtextPosition(): XtextPosition { + return XtextPosition( + line.toXtextLine(), + column.toXtextColumn() + ) + } + + override fun toString(): String { + return "[$line, $column]" + } + + override operator fun compareTo(other: LspPosition): Int { + val lineComparison = this.line.compareTo(other.line) + if (lineComparison != 0) { + return lineComparison + } + + return this.column.compareTo(other.column) + } +} + +/** + * A line in a program. Counting starts at 0. + * + * @throws IllegalArgumentException If value is negative. + */ +@JvmInline +value class LspLine(val value: Int) : Comparable { + init { + require(value >= 0) { "Line must be at least 0." } + } + + fun toXtextLine(): XtextLine { + return XtextLine(value + 1) + } + + override fun toString(): String { + return value.toString() + } + + override operator fun compareTo(other: LspLine): Int { + return this.value.compareTo(other.value) + } +} + +/** + * A column in a program. Counting starts at 0. + * + * @throws IllegalArgumentException If value is negative. + */ +@JvmInline +value class LspColumn(val value: Int) : Comparable { + init { + require(value >= 0) { "Column must be at least 0." } + } + + fun toXtextColumn(): XtextColumn { + return XtextColumn(value + 1) + } + + override fun toString(): String { + return value.toString() + } + + override operator fun compareTo(other: LspColumn): Int { + return this.value.compareTo(other.value) + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/LspRange.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/LspRange.kt new file mode 100644 index 000000000..9da205d6e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/LspRange.kt @@ -0,0 +1,34 @@ +package de.unibonn.simpleml.location + +/** + * A range in a program from a start to an end position with some length using the zero-based indexing of LSP. + * + * @see LspPosition + * @see ProgramRangeLength + */ +data class LspRange(val start: LspPosition, val end: LspPosition, val length: ProgramRangeLength) { + + companion object { + + @JvmStatic + fun fromInts(startLine: Int, startColumn: Int, endLine: Int, endColumn: Int, length: Int): LspRange { + return LspRange( + LspPosition.fromInts(startLine, startColumn), + LspPosition.fromInts(endLine, endColumn), + ProgramRangeLength(length) + ) + } + } + + fun toXtextRange(): XtextRange { + return XtextRange( + start.toXtextPosition(), + end.toXtextPosition(), + length + ) + } + + override fun toString(): String { + return "[$start .. $end]" + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/ProgramRangeLength.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/ProgramRangeLength.kt new file mode 100644 index 000000000..cf06d3c7e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/ProgramRangeLength.kt @@ -0,0 +1,21 @@ +package de.unibonn.simpleml.location + +/** + * The number of characters in a program range (Xtext/LSP). This value must be non-negative. + * + * @throws IllegalArgumentException If value is negative. + * + * @see XtextRange + * @see LspRange + */ +@JvmInline +value class ProgramRangeLength(val value: Int) { + init { + require(value >= 0) { "Length must be at least 0." } + } + + override fun toString(): String { + val chars = if (value == 1) "char" else "chars" + return "$value $chars" + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/XtextPosition.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/XtextPosition.kt new file mode 100644 index 000000000..aadad529f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/XtextPosition.kt @@ -0,0 +1,85 @@ +package de.unibonn.simpleml.location + +/** + * A specific position in a program using the one-based indexing of Xtext. + */ +data class XtextPosition(val line: XtextLine, val column: XtextColumn) : Comparable { + companion object { + + @JvmStatic + fun fromInts(line: Int, column: Int): XtextPosition { + return XtextPosition( + XtextLine(line), + XtextColumn(column) + ) + } + } + + fun toLspPosition(): LspPosition { + return LspPosition( + line.toLspLine(), + column.toLspColumn() + ) + } + + override fun toString(): String { + return "$line:$column" + } + + override operator fun compareTo(other: XtextPosition): Int { + val lineComparison = this.line.compareTo(other.line) + if (lineComparison != 0) { + return lineComparison + } + + return this.column.compareTo(other.column) + } +} + +/** + * A line in a program. Counting starts at 1. + * + * @throws IllegalArgumentException If value is less than 1. + */ +@JvmInline +value class XtextLine(val value: Int) : Comparable { + init { + require(value >= 1) { "Line must be at least 1." } + } + + fun toLspLine(): LspLine { + return LspLine(value - 1) + } + + override fun toString(): String { + return value.toString() + } + + override operator fun compareTo(other: XtextLine): Int { + return this.value.compareTo(other.value) + } +} + +/** + * A column in a program. Counting starts at 1. + * + * @throws IllegalArgumentException If value is less than 1. + */ +@JvmInline +value class XtextColumn(val value: Int) : Comparable { + init { + require(value >= 1) { "Column must be at least 1." } + } + + fun toLspColumn(): LspColumn { + return LspColumn(value - 1) + } + + override fun toString(): String { + return value.toString() + } + + override operator fun compareTo(other: XtextColumn): Int { + return this.value.compareTo(other.value) + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/XtextRange.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/XtextRange.kt new file mode 100644 index 000000000..b85e46ddc --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/location/XtextRange.kt @@ -0,0 +1,34 @@ +package de.unibonn.simpleml.location + +/** + * A range in a program from a start to an end position with some length using the one-based indexing of Xtext. + * + * @see XtextPosition + * @see ProgramRangeLength + */ +data class XtextRange(val start: XtextPosition, val end: XtextPosition, val length: ProgramRangeLength) { + + companion object { + + @JvmStatic + fun fromInts(startLine: Int, startColumn: Int, endLine: Int, endColumn: Int, length: Int): XtextRange { + return XtextRange( + XtextPosition.fromInts(startLine, startColumn), + XtextPosition.fromInts(endLine, endColumn), + ProgramRangeLength(length) + ) + } + } + + fun toLspRange(): LspRange { + return LspRange( + start.toLspPosition(), + end.toLspPosition(), + length + ) + } + + override fun toString(): String { + return "$start .. $end ($length)" + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/naming/QualifiedNameProvider.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/naming/QualifiedNameProvider.kt new file mode 100644 index 000000000..e32d94b09 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/naming/QualifiedNameProvider.kt @@ -0,0 +1,30 @@ +package de.unibonn.simpleml.naming + +import com.google.inject.Inject +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import org.eclipse.xtext.naming.IQualifiedNameConverter +import org.eclipse.xtext.naming.IQualifiedNameProvider +import org.eclipse.xtext.naming.QualifiedName + +internal object QualifiedNameProviderInjectionTarget { + + @Inject + lateinit var qualifiedNameConverter: IQualifiedNameConverter + + @Inject + lateinit var qualifiedNameProvider: IQualifiedNameProvider +} + +/** + * Returns the qualified name of the declaration. + */ +fun SmlAbstractDeclaration.qualifiedNameOrNull(): QualifiedName? { + return QualifiedNameProviderInjectionTarget.qualifiedNameProvider.getFullyQualifiedName(this) +} + +/** + * Converts a string to a qualified name. + */ +fun String.toQualifiedName(): QualifiedName { + return QualifiedNameProviderInjectionTarget.qualifiedNameConverter.toQualifiedName(this) +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/Main.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/Main.kt new file mode 100644 index 000000000..ddf194dff --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/Main.kt @@ -0,0 +1,81 @@ +package de.unibonn.simpleml.prologBridge + +import com.google.inject.Inject +import com.google.inject.Provider +import de.unibonn.simpleml.SimpleMLStandaloneSetup +import de.unibonn.simpleml.prologBridge.converters.AstToPrologFactbase +import de.unibonn.simpleml.prologBridge.model.facts.PlFactbase +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.stdlibAccess.loadStdlib +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecore.resource.ResourceSet +import org.eclipse.xtext.util.CancelIndicator +import org.eclipse.xtext.validation.CheckMode +import org.eclipse.xtext.validation.IResourceValidator +import org.eclipse.xtext.validation.Issue +import java.io.File +import java.nio.file.Files +import java.nio.file.Paths + +class Main @Inject constructor( + private val prologVisitor: AstToPrologFactbase, + private val resourceSetProvider: Provider, + private val validator: IResourceValidator +) { + + fun createFactbase(file: String): PlFactbase { + + // Load resource and library + val set = resourceSetProvider.get() + set.loadStdlib() + + val resource = set.getResource(URI.createFileURI(file), true) + require(resource != null) { "Could not create resource for $file." } + require(resource.contents.isNotEmpty()) { "Resource for $file is empty." } + + // Check for syntax errors + val issues = validator.validate(resource, CheckMode.ALL, CancelIndicator.NullImpl) + require(issues.none { it.isSyntaxError }) { syntaxErrorMessage(issues) } + + // Configure and start the visitor + val compilationUnit = resource.contents[0] as SmlCompilationUnit + return prologVisitor.createFactbase(listOf(compilationUnit)) + } + + private fun syntaxErrorMessage(issues: List) = buildString { + appendLine("Resource has syntax errors:") + issues.filter { it.isSyntaxError }.forEach { + appendLine(" * $it") + } + } +} + +fun main() { + val time0: Long = System.currentTimeMillis() + /*if (args.isEmpty()) { + System.err.println("Aborting: no path to EMF resource provided!") + return + } + + val injector = SimpleMLStandaloneSetup().createInjectorAndDoEMFRegistration() + val main = injector.getInstance(Main::class.java) + val factbase = main.createFactbase(args[0]) + + Files.write(Paths.get("prolog_facts.pl"), factbase.toString().toByteArray())*/ + + // TESTING ALL ELEMENTS + val injector = SimpleMLStandaloneSetup().createInjectorAndDoEMFRegistration() + val main = injector.getInstance(Main::class.java) + + File("testPrologVisitor").walkTopDown().forEach { + if (it.isFile) { + val factbase = main.createFactbase(it.absolutePath) + val path = ("PrologFacts/" + it.path).replace(it.name, "") + Files.createDirectories(Paths.get(path)) + Files.write(Paths.get(path + (it.name.replace(it.extension, "pl"))), factbase.toString().toByteArray()) + } + } + + val time1 = System.currentTimeMillis() + println((time1 - time0) / 1000.0) +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/converters/AstToPrologFactbase.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/converters/AstToPrologFactbase.kt new file mode 100644 index 000000000..98d486884 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/converters/AstToPrologFactbase.kt @@ -0,0 +1,704 @@ +package de.unibonn.simpleml.prologBridge.converters + +import de.unibonn.simpleml.emf.aliasNameOrNull +import de.unibonn.simpleml.emf.annotationCallsOrEmpty +import de.unibonn.simpleml.emf.argumentsOrEmpty +import de.unibonn.simpleml.emf.assigneesOrEmpty +import de.unibonn.simpleml.emf.compilationUnitMembersOrEmpty +import de.unibonn.simpleml.emf.constraintsOrEmpty +import de.unibonn.simpleml.emf.objectsInBodyOrEmpty +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.parentTypesOrEmpty +import de.unibonn.simpleml.emf.referencesOrEmpty +import de.unibonn.simpleml.emf.resultsOrEmpty +import de.unibonn.simpleml.emf.statementsOrEmpty +import de.unibonn.simpleml.emf.subtermsOrEmpty +import de.unibonn.simpleml.emf.termOrNull +import de.unibonn.simpleml.emf.typeArgumentsOrEmpty +import de.unibonn.simpleml.emf.typeParametersOrEmpty +import de.unibonn.simpleml.emf.variantsOrEmpty +import de.unibonn.simpleml.prologBridge.model.facts.AnnotationCallT +import de.unibonn.simpleml.prologBridge.model.facts.AnnotationT +import de.unibonn.simpleml.prologBridge.model.facts.ArgumentT +import de.unibonn.simpleml.prologBridge.model.facts.AssignmentT +import de.unibonn.simpleml.prologBridge.model.facts.AttributeT +import de.unibonn.simpleml.prologBridge.model.facts.BlockLambdaResultT +import de.unibonn.simpleml.prologBridge.model.facts.BlockLambdaT +import de.unibonn.simpleml.prologBridge.model.facts.BooleanT +import de.unibonn.simpleml.prologBridge.model.facts.CallT +import de.unibonn.simpleml.prologBridge.model.facts.CallableTypeT +import de.unibonn.simpleml.prologBridge.model.facts.ClassT +import de.unibonn.simpleml.prologBridge.model.facts.CompilationUnitT +import de.unibonn.simpleml.prologBridge.model.facts.EnumT +import de.unibonn.simpleml.prologBridge.model.facts.EnumVariantT +import de.unibonn.simpleml.prologBridge.model.facts.ExpressionLambdaT +import de.unibonn.simpleml.prologBridge.model.facts.ExpressionStatementT +import de.unibonn.simpleml.prologBridge.model.facts.FloatT +import de.unibonn.simpleml.prologBridge.model.facts.FunctionT +import de.unibonn.simpleml.prologBridge.model.facts.ImportT +import de.unibonn.simpleml.prologBridge.model.facts.IndexedAccessT +import de.unibonn.simpleml.prologBridge.model.facts.InfixOperationT +import de.unibonn.simpleml.prologBridge.model.facts.IntT +import de.unibonn.simpleml.prologBridge.model.facts.MemberAccessT +import de.unibonn.simpleml.prologBridge.model.facts.MemberTypeT +import de.unibonn.simpleml.prologBridge.model.facts.NamedTypeT +import de.unibonn.simpleml.prologBridge.model.facts.NullT +import de.unibonn.simpleml.prologBridge.model.facts.ParameterT +import de.unibonn.simpleml.prologBridge.model.facts.ParenthesizedExpressionT +import de.unibonn.simpleml.prologBridge.model.facts.ParenthesizedTypeT +import de.unibonn.simpleml.prologBridge.model.facts.PlFactbase +import de.unibonn.simpleml.prologBridge.model.facts.PlaceholderT +import de.unibonn.simpleml.prologBridge.model.facts.PrefixOperationT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolAlternativeT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolComplementT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolParenthesizedTermT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolQuantifiedTermT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolReferenceT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolSequenceT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolSubtermT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolTokenClassT +import de.unibonn.simpleml.prologBridge.model.facts.ReferenceT +import de.unibonn.simpleml.prologBridge.model.facts.ResourceS +import de.unibonn.simpleml.prologBridge.model.facts.ResultT +import de.unibonn.simpleml.prologBridge.model.facts.SourceLocationS +import de.unibonn.simpleml.prologBridge.model.facts.StarProjectionT +import de.unibonn.simpleml.prologBridge.model.facts.StepT +import de.unibonn.simpleml.prologBridge.model.facts.StringT +import de.unibonn.simpleml.prologBridge.model.facts.TemplateStringEndT +import de.unibonn.simpleml.prologBridge.model.facts.TemplateStringInnerT +import de.unibonn.simpleml.prologBridge.model.facts.TemplateStringStartT +import de.unibonn.simpleml.prologBridge.model.facts.TemplateStringT +import de.unibonn.simpleml.prologBridge.model.facts.TypeArgumentT +import de.unibonn.simpleml.prologBridge.model.facts.TypeParameterConstraintT +import de.unibonn.simpleml.prologBridge.model.facts.TypeParameterT +import de.unibonn.simpleml.prologBridge.model.facts.TypeProjectionT +import de.unibonn.simpleml.prologBridge.model.facts.UnionTypeT +import de.unibonn.simpleml.prologBridge.model.facts.UnresolvedT +import de.unibonn.simpleml.prologBridge.model.facts.WildcardT +import de.unibonn.simpleml.prologBridge.model.facts.WorkflowT +import de.unibonn.simpleml.prologBridge.model.facts.YieldT +import de.unibonn.simpleml.simpleML.SimpleMLPackage +import de.unibonn.simpleml.simpleML.SmlAbstractAssignee +import de.unibonn.simpleml.simpleML.SmlAbstractConstraint +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlAbstractExpression +import de.unibonn.simpleml.simpleML.SmlAbstractObject +import de.unibonn.simpleml.simpleML.SmlAbstractProtocolTerm +import de.unibonn.simpleml.simpleML.SmlAbstractStatement +import de.unibonn.simpleml.simpleML.SmlAbstractType +import de.unibonn.simpleml.simpleML.SmlAbstractTypeArgumentValue +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.simpleML.SmlAnnotationCall +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.simpleML.SmlBlockLambda +import de.unibonn.simpleml.simpleML.SmlBlockLambdaResult +import de.unibonn.simpleml.simpleML.SmlBoolean +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlCallableType +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlExpressionLambda +import de.unibonn.simpleml.simpleML.SmlExpressionStatement +import de.unibonn.simpleml.simpleML.SmlFloat +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlImport +import de.unibonn.simpleml.simpleML.SmlIndexedAccess +import de.unibonn.simpleml.simpleML.SmlInfixOperation +import de.unibonn.simpleml.simpleML.SmlInt +import de.unibonn.simpleml.simpleML.SmlMemberAccess +import de.unibonn.simpleml.simpleML.SmlMemberType +import de.unibonn.simpleml.simpleML.SmlNamedType +import de.unibonn.simpleml.simpleML.SmlNull +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlParenthesizedExpression +import de.unibonn.simpleml.simpleML.SmlParenthesizedType +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlPrefixOperation +import de.unibonn.simpleml.simpleML.SmlProtocol +import de.unibonn.simpleml.simpleML.SmlProtocolAlternative +import de.unibonn.simpleml.simpleML.SmlProtocolComplement +import de.unibonn.simpleml.simpleML.SmlProtocolParenthesizedTerm +import de.unibonn.simpleml.simpleML.SmlProtocolQuantifiedTerm +import de.unibonn.simpleml.simpleML.SmlProtocolReference +import de.unibonn.simpleml.simpleML.SmlProtocolSequence +import de.unibonn.simpleml.simpleML.SmlProtocolSubterm +import de.unibonn.simpleml.simpleML.SmlProtocolTokenClass +import de.unibonn.simpleml.simpleML.SmlReference +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlStarProjection +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlString +import de.unibonn.simpleml.simpleML.SmlTemplateString +import de.unibonn.simpleml.simpleML.SmlTemplateStringEnd +import de.unibonn.simpleml.simpleML.SmlTemplateStringInner +import de.unibonn.simpleml.simpleML.SmlTemplateStringStart +import de.unibonn.simpleml.simpleML.SmlTypeArgument +import de.unibonn.simpleml.simpleML.SmlTypeParameter +import de.unibonn.simpleml.simpleML.SmlTypeParameterConstraint +import de.unibonn.simpleml.simpleML.SmlTypeProjection +import de.unibonn.simpleml.simpleML.SmlUnionType +import de.unibonn.simpleml.simpleML.SmlWildcard +import de.unibonn.simpleml.simpleML.SmlWorkflow +import de.unibonn.simpleml.simpleML.SmlYield +import de.unibonn.simpleml.utils.Id +import de.unibonn.simpleml.utils.IdManager +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.EReference +import org.eclipse.xtext.EcoreUtil2 +import org.eclipse.xtext.nodemodel.util.NodeModelUtils + +class AstToPrologFactbase { + private var idManager = IdManager() + + fun createFactbase(compilationUnits: List): PlFactbase { + reset() + + return PlFactbase().apply { + + // Enforce order of IDs + compilationUnits.forEach { compilationUnit -> + compilationUnit.id + compilationUnit.eAllContents().asSequence().forEach { obj -> + obj.id + } + } + + // Create facts + compilationUnits.forEach { + visitCompilationUnit(it) + } + } + } + + // ***************************************************************************************************************** + // Declarations + // ****************************************************************************************************************/ + + private fun PlFactbase.visitCompilationUnit(obj: SmlCompilationUnit) { + obj.imports.forEach { this.visitImport(it, obj.id) } + obj.compilationUnitMembersOrEmpty().forEach { this.visitDeclaration(it, obj.id) } + + +CompilationUnitT( + obj.id, + obj.name, + obj.imports.map { it.id }, + obj.compilationUnitMembersOrEmpty().map { it.id } + ) + +ResourceS(obj.id, obj.eResource().uri.toString()) + visitSourceLocation(obj) + } + + private fun PlFactbase.visitDeclaration(obj: SmlAbstractDeclaration, parentId: Id) { + obj.annotationCallsOrEmpty().forEach { visitAnnotationCall(it, obj.id) } + + when (obj) { + is SmlAnnotation -> { + obj.parametersOrEmpty().forEach { visitDeclaration(it, obj.id) } + obj.constraintsOrEmpty().forEach { visitConstraint(it, obj.id) } + + +AnnotationT( + obj.id, + parentId, + obj.name, + obj.parameterList?.parameters?.map { it.id }, + obj.constraintList?.constraints?.map { it.id } + ) + } + is SmlAttribute -> { + obj.type?.let { visitType(it, obj.id) } + + +AttributeT(obj.id, parentId, obj.name, obj.isStatic, obj.type?.id) + } + is SmlClass -> { + obj.typeParametersOrEmpty().forEach { visitDeclaration(it, obj.id) } + obj.parametersOrEmpty().forEach { visitDeclaration(it, obj.id) } + obj.parentTypesOrEmpty().forEach { visitType(it, obj.id) } + obj.constraintsOrEmpty().forEach { visitConstraint(it, obj.id) } + obj.objectsInBodyOrEmpty().forEach { visitClassMember(it, obj.id) } + + +ClassT( + obj.id, + parentId, + obj.name, + obj.typeParameterList?.typeParameters?.map { it.id }, + obj.parameterList?.parameters?.map { it.id }, + obj.parentTypeList?.parentTypes?.map { it.id }, + obj.constraintList?.constraints?.map { it.id }, + obj.body?.members?.map { it.id } + ) + } + is SmlEnum -> { + obj.variantsOrEmpty().forEach { visitDeclaration(it, obj.id) } + + +EnumT(obj.id, parentId, obj.name, obj.body?.variants?.map { it.id }) + } + is SmlEnumVariant -> { + obj.typeParametersOrEmpty().forEach { visitDeclaration(it, obj.id) } + obj.parametersOrEmpty().forEach { visitDeclaration(it, obj.id) } + obj.constraintsOrEmpty().forEach { visitConstraint(it, obj.id) } + + +EnumVariantT( + obj.id, + parentId, + obj.name, + obj.typeParameterList?.typeParameters?.map { it.id }, + obj.parameterList?.parameters?.map { it.id }, + obj.constraintList?.constraints?.map { it.id }, + ) + } + is SmlFunction -> { + obj.typeParametersOrEmpty().forEach { visitDeclaration(it, obj.id) } + obj.parametersOrEmpty().forEach { visitDeclaration(it, obj.id) } + obj.resultsOrEmpty().forEach { visitDeclaration(it, obj.id) } + obj.constraintsOrEmpty().forEach { visitConstraint(it, obj.id) } + + +FunctionT( + obj.id, + parentId, + obj.name, + obj.isStatic, + obj.typeParameterList?.typeParameters?.map { it.id }, + obj.parametersOrEmpty().map { it.id }, + obj.resultList?.results?.map { it.id }, + obj.constraintList?.constraints?.map { it.id }, + ) + } + is SmlParameter -> { + obj.type?.let { visitType(it, obj.id) } + obj.defaultValue?.let { visitExpression(it, obj.id, obj.id) } + + +ParameterT(obj.id, parentId, obj.name, obj.isVariadic, obj.type?.id, obj.defaultValue?.id) + } + is SmlProtocolSubterm -> { + visitProtocolTerm(obj.term, obj.id, obj.id) + + +ProtocolSubtermT(obj.id, parentId, obj.name, obj.term.id) + } + is SmlResult -> { + obj.type?.let { visitType(it, obj.id) } + + +ResultT(obj.id, parentId, obj.name, obj.type?.id) + } + is SmlTypeParameter -> { + +TypeParameterT(obj.id, parentId, obj.name, obj.variance) + } + is SmlWorkflow -> { + obj.statementsOrEmpty().forEach { visitStatement(it, obj.id) } + + +WorkflowT(obj.id, parentId, obj.name, obj.statementsOrEmpty().map { it.id }) + } + is SmlStep -> { + obj.parametersOrEmpty().forEach { visitDeclaration(it, obj.id) } + obj.resultsOrEmpty().forEach { visitDeclaration(it, obj.id) } + obj.statementsOrEmpty().forEach { visitStatement(it, obj.id) } + + +StepT( + obj.id, + parentId, + obj.name, + obj.visibility, + obj.parametersOrEmpty().map { it.id }, + obj.resultList?.results?.map { it.id }, + obj.statementsOrEmpty().map { it.id } + ) + } + } + + visitSourceLocation(obj) + } + + private fun PlFactbase.visitAnnotationCall(obj: SmlAnnotationCall, parentId: Id) { + visitCrossReference(obj, SimpleMLPackage.Literals.SML_ANNOTATION_CALL__ANNOTATION, obj.annotation) + obj.argumentsOrEmpty().forEach { visitExpression(it, obj.id, obj.id) } + + +AnnotationCallT(obj.id, parentId, obj.annotation.id, obj.argumentList?.arguments?.map { it.id }) + visitSourceLocation(obj) + } + + private fun PlFactbase.visitImport(obj: SmlImport, parentId: Id) { + +ImportT(obj.id, parentId, obj.importedNamespace, obj.aliasNameOrNull()) + visitSourceLocation(obj) + } + + private fun PlFactbase.visitClassMember(obj: SmlAbstractObject, parentId: Id) { + when (obj) { + is SmlAbstractDeclaration -> visitDeclaration(obj, parentId) + is SmlProtocol -> visitProtocol(obj, parentId) + } + } + + private fun PlFactbase.visitProtocol(obj: SmlProtocol, parentId: Id) { + obj.subtermsOrEmpty().forEach { visitDeclaration(it, obj.id) } + obj.termOrNull()?.let { visitProtocolTerm(it, obj.id, obj.id) } + + +ProtocolT( + obj.id, + parentId, + obj.body?.subtermList?.subterms?.map { it.id }, + obj.body?.term?.id + ) + visitSourceLocation(obj) + } + + private fun PlFactbase.visitProtocolTerm( + obj: SmlAbstractProtocolTerm, + parentId: Id, + enclosingId: Id + ) { + when (obj) { + is SmlProtocolAlternative -> { + obj.terms.forEach { visitProtocolTerm(it, obj.id, enclosingId) } + + +ProtocolAlternativeT(obj.id, parentId, enclosingId, obj.terms.map { it.id }) + } + is SmlProtocolComplement -> { + obj.universe?.let { visitProtocolTerm(obj.universe, obj.id, enclosingId) } + obj.referencesOrEmpty().forEach { visitProtocolTerm(it, obj.id, enclosingId) } + + +ProtocolComplementT( + obj.id, + parentId, + enclosingId, + obj.universe?.id, + obj.referenceList?.references?.map { it.id } + ) + } + is SmlProtocolParenthesizedTerm -> { + visitProtocolTerm(obj.term, obj.id, enclosingId) + + +ProtocolParenthesizedTermT(obj.id, parentId, enclosingId, obj.term.id) + } + is SmlProtocolQuantifiedTerm -> { + visitProtocolTerm(obj.term, obj.id, enclosingId) + + +ProtocolQuantifiedTermT(obj.id, parentId, enclosingId, obj.term.id, obj.quantifier) + } + is SmlProtocolReference -> { + visitCrossReference(obj, SimpleMLPackage.Literals.SML_PROTOCOL_REFERENCE__TOKEN, obj.token) + + +ProtocolReferenceT(obj.id, parentId, enclosingId, obj.token.id) + } + is SmlProtocolSequence -> { + obj.terms.forEach { visitProtocolTerm(it, obj.id, enclosingId) } + + +ProtocolSequenceT(obj.id, parentId, enclosingId, obj.terms.map { it.id }) + } + is SmlProtocolTokenClass -> { + +ProtocolTokenClassT(obj.id, parentId, enclosingId, obj.value) + } + } + + visitSourceLocation(obj) + } + + // ***************************************************************************************************************** + // Statements + // ****************************************************************************************************************/ + + private fun PlFactbase.visitStatement(obj: SmlAbstractStatement, parentId: Id) { + when (obj) { + is SmlAssignment -> { + obj.assigneesOrEmpty().forEach { this.visitAssignee(it, obj.id) } + this.visitExpression(obj.expression, obj.id, obj.id) + + +AssignmentT(obj.id, parentId, obj.assigneesOrEmpty().map { it.id }, obj.expression.id) + } + is SmlExpressionStatement -> { + this.visitExpression(obj.expression, obj.id, obj.id) + + +ExpressionStatementT(obj.id, parentId, obj.expression.id) + } + } + + visitSourceLocation(obj) + } + + private fun PlFactbase.visitAssignee(obj: SmlAbstractAssignee, parentId: Id) { + when (obj) { + is SmlBlockLambdaResult -> { + +BlockLambdaResultT(obj.id, parentId, obj.name) + } + is SmlPlaceholder -> { + +PlaceholderT(obj.id, parentId, obj.name) + } + is SmlWildcard -> { + +WildcardT(obj.id, parentId) + } + is SmlYield -> { + visitCrossReference(obj, SimpleMLPackage.Literals.SML_YIELD__RESULT, obj.result) + + +YieldT(obj.id, parentId, obj.result.id) + } + } + + visitSourceLocation(obj) + } + + // ***************************************************************************************************************** + // Expressions + // ****************************************************************************************************************/ + + private fun PlFactbase.visitExpression( + obj: SmlAbstractExpression, + parentId: Id, + enclosingId: Id + ) { + when (obj) { + is SmlArgument -> { + obj.parameter?.let { visitCrossReference(obj, SimpleMLPackage.Literals.SML_ARGUMENT__PARAMETER, it) } + visitExpression(obj.value, obj.id, enclosingId) + + +ArgumentT(obj.id, parentId, enclosingId, obj.parameter?.id, obj.value.id) + } + is SmlBoolean -> { + +BooleanT(obj.id, parentId, enclosingId, obj.isTrue) + } + is SmlBlockLambda -> { + obj.parametersOrEmpty().forEach { visitDeclaration(it, obj.id) } + obj.statementsOrEmpty().forEach { visitStatement(it, obj.id) } + + +BlockLambdaT( + obj.id, + parentId, + enclosingId, + obj.parametersOrEmpty().map { it.id }, + obj.statementsOrEmpty().map { it.id } + ) + } + is SmlCall -> { + visitExpression(obj.receiver, obj.id, enclosingId) + obj.typeArgumentsOrEmpty().forEach { visitTypeArgument(it, obj.id) } + obj.argumentsOrEmpty().forEach { visitExpression(it, obj.id, enclosingId) } + + +CallT( + obj.id, + parentId, + enclosingId, + obj.receiver.id, + obj.typeArgumentList?.typeArguments?.map { it.id }, + obj.argumentsOrEmpty().map { it.id } + ) + } + is SmlExpressionLambda -> { + obj.parametersOrEmpty().forEach { visitDeclaration(it, obj.id) } + visitExpression(obj.result, obj.id, enclosingId) + + +ExpressionLambdaT( + obj.id, + parentId, + enclosingId, + obj.parametersOrEmpty().map { it.id }, + obj.result.id + ) + } + is SmlFloat -> { + +FloatT(obj.id, parentId, enclosingId, obj.value) + } + is SmlIndexedAccess -> { + visitExpression(obj.receiver, obj.id, enclosingId) + visitExpression(obj.index, obj.id, enclosingId) + + +IndexedAccessT(obj.id, parentId, enclosingId, obj.receiver.id, obj.index.id) + } + is SmlInfixOperation -> { + visitExpression(obj.leftOperand, obj.id, enclosingId) + visitExpression(obj.rightOperand, obj.id, enclosingId) + + +InfixOperationT(obj.id, parentId, enclosingId, obj.leftOperand.id, obj.operator, obj.rightOperand.id) + } + is SmlInt -> { + +IntT(obj.id, parentId, enclosingId, obj.value) + } + is SmlMemberAccess -> { + visitExpression(obj.receiver, obj.id, enclosingId) + visitExpression(obj.member, obj.id, enclosingId) + + +MemberAccessT(obj.id, parentId, enclosingId, obj.receiver.id, obj.isNullSafe, obj.member.id) + } + is SmlNull -> { + +NullT(obj.id, parentId, enclosingId) + } + is SmlParenthesizedExpression -> { + visitExpression(obj.expression, obj.id, enclosingId) + + +ParenthesizedExpressionT(obj.id, parentId, enclosingId, obj.expression.id) + } + is SmlPrefixOperation -> { + visitExpression(obj.operand, obj.id, enclosingId) + + +PrefixOperationT(obj.id, parentId, enclosingId, obj.operator, obj.operand.id) + } + is SmlReference -> { + visitCrossReference(obj, SimpleMLPackage.Literals.SML_REFERENCE__DECLARATION, obj.declaration) + + +ReferenceT(obj.id, parentId, enclosingId, obj.declaration.id) + } + is SmlString -> { + +StringT(obj.id, parentId, enclosingId, obj.value) + } + is SmlTemplateString -> { + obj.expressions.forEach { visitExpression(it, obj.id, enclosingId) } + + +TemplateStringT(obj.id, parentId, enclosingId, obj.expressions.map { it.id }) + } + is SmlTemplateStringStart -> { + +TemplateStringStartT(obj.id, parentId, enclosingId, obj.value) + } + is SmlTemplateStringInner -> { + +TemplateStringInnerT(obj.id, parentId, enclosingId, obj.value) + } + is SmlTemplateStringEnd -> { + +TemplateStringEndT(obj.id, parentId, enclosingId, obj.value) + } + } + + visitSourceLocation(obj) + } + + // ***************************************************************************************************************** + // Types + // ****************************************************************************************************************/ + + private fun PlFactbase.visitType(obj: SmlAbstractType, parentId: Id) { + when (obj) { + is SmlCallableType -> { + obj.parametersOrEmpty().forEach { visitDeclaration(it, obj.id) } + obj.resultsOrEmpty().forEach { visitDeclaration(it, obj.id) } + + +CallableTypeT( + obj.id, + parentId, + obj.parametersOrEmpty().map { it.id }, + obj.resultsOrEmpty().map { it.id } + ) + } + is SmlMemberType -> { + visitType(obj.receiver, obj.id) + visitType(obj.member, obj.id) + + +MemberTypeT(obj.id, parentId, obj.receiver.id, obj.member.id) + } + is SmlNamedType -> { + visitCrossReference(obj, SimpleMLPackage.Literals.SML_NAMED_TYPE__DECLARATION, obj.declaration) + obj.typeArgumentsOrEmpty().forEach { visitTypeArgument(it, obj.id) } + + +NamedTypeT( + obj.id, + parentId, + obj.declaration.id, + obj.typeArgumentList?.typeArguments?.map { it.id }, + obj.isNullable + ) + } + is SmlParenthesizedType -> { + visitType(obj.type, obj.id) + + +ParenthesizedTypeT(obj.id, parentId, obj.type.id) + } + is SmlUnionType -> { + obj.typeArgumentsOrEmpty().forEach { visitTypeArgument(it, obj.id) } + + +UnionTypeT(obj.id, parentId, obj.typeArgumentsOrEmpty().map { it.id }) + } + } + + visitSourceLocation(obj) + } + + private fun PlFactbase.visitTypeArgument(obj: SmlTypeArgument, parentId: Id) { + obj.typeParameter?.let { + visitCrossReference( + obj, + SimpleMLPackage.Literals.SML_TYPE_ARGUMENT__TYPE_PARAMETER, + obj.typeParameter + ) + } + visitTypeArgumentValue(obj.value, obj.id) + + +TypeArgumentT(obj.id, parentId, obj.typeParameter?.id, obj.value.id) + visitSourceLocation(obj) + } + + private fun PlFactbase.visitTypeArgumentValue(obj: SmlAbstractTypeArgumentValue, parentId: Id) { + when (obj) { + is SmlStarProjection -> { + +StarProjectionT(obj.id, parentId) + } + is SmlTypeProjection -> { + visitType(obj.type, obj.id) + + +TypeProjectionT(obj.id, parentId, obj.variance, obj.type.id) + } + } + + visitSourceLocation(obj) + } + + private fun PlFactbase.visitConstraint( + obj: SmlAbstractConstraint, + parentId: Id + ) { + when (obj) { + is SmlTypeParameterConstraint -> { + visitCrossReference( + obj, + SimpleMLPackage.Literals.SML_TYPE_PARAMETER_CONSTRAINT__LEFT_OPERAND, + obj.leftOperand + ) + visitType(obj.rightOperand, obj.id) + + +TypeParameterConstraintT(obj.id, parentId, obj.leftOperand.id, obj.operator, obj.rightOperand.id) + } + } + + visitSourceLocation(obj) + } + + // ***************************************************************************************************************** + // Other + // ****************************************************************************************************************/ + + private fun PlFactbase.visitSourceLocation(obj: SmlAbstractObject) { + val uriHash = EcoreUtil2.getURI(obj).toString().split("#").last() + val node = NodeModelUtils.getNode(obj) + val location = NodeModelUtils.getLineAndColumn(node, node.offset) + + +SourceLocationS( + obj.id, + uriHash, + node.offset, + location.line, + location.column, + node.length + ) + } + + private fun PlFactbase.visitCrossReference(source: SmlAbstractObject, edge: EReference, target: SmlAbstractObject) { + if (!idManager.knowsObject(target)) { + val name = getReferencedName(source, edge) + +UnresolvedT(target.id, name) + } + } + + private fun getReferencedName(obj: SmlAbstractObject, eReference: EReference): String { + return NodeModelUtils + .findNodesForFeature(obj, eReference) + .joinToString("") { it.text } + } + + // ***************************************************************************************************************** + // Helpers + // ****************************************************************************************************************/ + + private val T.id: Id + get() = idManager.assignIdIfAbsent(this) + + private fun reset() { + idManager = IdManager() + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/model/facts/Factbase.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/model/facts/Factbase.kt new file mode 100644 index 000000000..b78de00f9 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/model/facts/Factbase.kt @@ -0,0 +1,84 @@ +package de.unibonn.simpleml.prologBridge.model.facts + +import de.unibonn.simpleml.utils.Id + +private const val factPrefix = "simpleml" + +class PlFactbase { + val facts = mutableListOf() + + inline fun Id<*>.resolve(): T? { + return findUniqueFact { it.id == this } + } + + inline fun findUniqueFact(filter: (T) -> Boolean = { true }): T? { + val candidates = findFacts(filter) + return when (candidates.size) { + 1 -> candidates.first() + else -> null + } + } + + inline fun findFacts(filter: (T) -> Boolean = { true }): List { + return facts.filterIsInstance().filter(filter) + } + + fun isContainedIn(descendant: Node, ancestor: Node): Boolean { + return isContainedIn(descendant, ancestor, mutableSetOf()) + } + + private tailrec fun isContainedIn(descendant: Node, ancestor: Node, visitedNodes: MutableSet): Boolean { + return when (descendant) { + ancestor -> true + !is NodeWithParent -> false + in visitedNodes -> false + else -> { + val parent = descendant.parent.resolve() + if (parent == null) { + false + } else { + visitedNodes += descendant + isContainedIn(parent, ancestor, visitedNodes) + } + } + } + } + + /** + * Add this fact to the factbase. + */ + operator fun PlFact.unaryPlus() { + facts += this + } + + override fun toString() = buildString { + appendDiscontiguousDirectives() + appendNodes() + appendOtherFacts() + appendPefCounts() + } + + private fun StringBuilder.appendDiscontiguousDirectives() { + facts.map { it.functor } + .toSortedSet() + .forEach { appendLine(":- discontiguous($factPrefix:$it).") } + } + + private fun StringBuilder.appendNodes() { + facts.filterIsInstance() + .sortedBy { it.id.value } + .forEach { appendLine("$factPrefix:$it") } + } + + private fun StringBuilder.appendOtherFacts() { + facts.filter { it !is Node } + .sortedBy { it.functor } + .forEach { appendLine("$factPrefix:$it") } + } + + private fun StringBuilder.appendPefCounts() { + facts.groupBy { it.functor } + .toSortedMap() + .forEach { appendLine("$factPrefix:pefCount(${it.key}, ${it.value.size}).") } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/model/facts/Facts.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/model/facts/Facts.kt new file mode 100644 index 000000000..bccb1b494 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/model/facts/Facts.kt @@ -0,0 +1,1883 @@ +package de.unibonn.simpleml.prologBridge.model.facts + +import de.unibonn.simpleml.prologBridge.model.facts.PlTerm.Companion.fromJavaRepresentation +import de.unibonn.simpleml.simpleML.SmlAbstractAssignee +import de.unibonn.simpleml.simpleML.SmlAbstractConstraint +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlAbstractExpression +import de.unibonn.simpleml.simpleML.SmlAbstractObject +import de.unibonn.simpleml.simpleML.SmlAbstractProtocolTerm +import de.unibonn.simpleml.simpleML.SmlAbstractProtocolToken +import de.unibonn.simpleml.simpleML.SmlAbstractStatement +import de.unibonn.simpleml.simpleML.SmlAbstractType +import de.unibonn.simpleml.simpleML.SmlAbstractTypeArgumentValue +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.simpleML.SmlAnnotationCall +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.simpleML.SmlBlockLambda +import de.unibonn.simpleml.simpleML.SmlBlockLambdaResult +import de.unibonn.simpleml.simpleML.SmlBoolean +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlCallableType +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlExpressionLambda +import de.unibonn.simpleml.simpleML.SmlExpressionStatement +import de.unibonn.simpleml.simpleML.SmlFloat +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlImport +import de.unibonn.simpleml.simpleML.SmlIndexedAccess +import de.unibonn.simpleml.simpleML.SmlInfixOperation +import de.unibonn.simpleml.simpleML.SmlInt +import de.unibonn.simpleml.simpleML.SmlMemberAccess +import de.unibonn.simpleml.simpleML.SmlMemberType +import de.unibonn.simpleml.simpleML.SmlNamedType +import de.unibonn.simpleml.simpleML.SmlNull +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlPrefixOperation +import de.unibonn.simpleml.simpleML.SmlProtocol +import de.unibonn.simpleml.simpleML.SmlProtocolAlternative +import de.unibonn.simpleml.simpleML.SmlProtocolComplement +import de.unibonn.simpleml.simpleML.SmlProtocolParenthesizedTerm +import de.unibonn.simpleml.simpleML.SmlProtocolQuantifiedTerm +import de.unibonn.simpleml.simpleML.SmlProtocolReference +import de.unibonn.simpleml.simpleML.SmlProtocolSequence +import de.unibonn.simpleml.simpleML.SmlProtocolSubterm +import de.unibonn.simpleml.simpleML.SmlProtocolTokenClass +import de.unibonn.simpleml.simpleML.SmlReference +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlStarProjection +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlString +import de.unibonn.simpleml.simpleML.SmlTemplateString +import de.unibonn.simpleml.simpleML.SmlTemplateStringEnd +import de.unibonn.simpleml.simpleML.SmlTemplateStringInner +import de.unibonn.simpleml.simpleML.SmlTemplateStringStart +import de.unibonn.simpleml.simpleML.SmlTypeArgument +import de.unibonn.simpleml.simpleML.SmlTypeParameter +import de.unibonn.simpleml.simpleML.SmlTypeParameterConstraint +import de.unibonn.simpleml.simpleML.SmlTypeProjection +import de.unibonn.simpleml.simpleML.SmlUnionType +import de.unibonn.simpleml.simpleML.SmlWildcard +import de.unibonn.simpleml.simpleML.SmlWorkflow +import de.unibonn.simpleml.simpleML.SmlYield +import de.unibonn.simpleml.utils.Id + +/** + * Represents generic Prolog facts. + * + * @param factName + * The name of this fact. + * + * @param arguments + * The arguments of this fact. Arguments can either be `null`, booleans, IDs, number, strings or lists. + */ +sealed class PlFact(factName: String, vararg arguments: Any?) { + + /** + * The name of this fact as a Prolog atom. + */ + private val factName: PlAtom = PlAtom(factName) + + /** + * The arguments of this fact as a list of Prolog terms. + */ + val plArguments: List = fromJavaRepresentation(listOf(*arguments)) + + /** + * The number of arguments of this fact. + */ + private val arity = arguments.size + + /** + * The functor of this fact, i.e. factName/arity. + * + * **Example:** If the name is "example" and the number of arguments (= arity) is 3, the functor is "example/3". + */ + val functor: String = "$factName/$arity" + + override fun toString() = plArguments.joinToString(prefix = "$factName(", postfix = ").") { it.toString() } +} + +/** + * Prolog facts that have their own ID and a reference to the fact for their logical parent. + * + * @param factName + * The name of this fact. + * + * @param id + * The ID of this fact. + * + * @param otherArguments + * Arguments of this fact beyond the ID. Arguments can either be `null`, booleans, IDs, number, strings or lists. + */ +sealed class Node(factName: String, id: Id, vararg otherArguments: Any?) : + PlFact(factName, id, *otherArguments) { + + /** + * The ID of this fact. + */ + abstract val id: Id +} + +/** + * Prolog facts that have their own ID and a reference to the fact for their logical parent. + * + * @param factName + * The name of this fact. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent. + * + * @param otherArguments + * Arguments of this fact beyond ID and parent. Arguments can either be `null`, booleans, IDs, number, strings or lists. + */ +sealed class NodeWithParent( + factName: String, + id: Id, + parent: Id, + vararg otherArguments: Any? +) : + Node(factName, id, parent, *otherArguments) { + + /** + * The ID of the fact for the logical parent. + */ + abstract val parent: Id +} + +/********************************************************************************************************************** + * Compilation Unit + **********************************************************************************************************************/ + +/** + * This Prolog fact represents compilations units. + * + * @param id + * The ID of this fact. + * + * @param packageName + * The name of the package that this compilation unit belongs to or `null` if no package is declared. + * + * @param + * IDs of [ImportT] facts. + * + * @param members + * The IDs of the facts for the members. + */ +data class CompilationUnitT( + override val id: Id, + val packageName: String?, + val imports: List>, + val members: List> +) : Node("compilationUnitT", id, packageName, imports, members) { + override fun toString() = super.toString() +} + +/********************************************************************************************************************** + * Declarations + **********************************************************************************************************************/ + +/** + * Prolog facts that have their own ID, a reference to the fact for their logical parent, and a name (which is the name + * of the declaration and unrelated to the factName). + * + * @param factName + * The name of this fact. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent. + * + * @param name + * The name of the declaration. + * + * @param otherArguments + * Arguments of this fact beyond ID, parent, and name. Arguments can either be `null`, booleans, IDs, number, strings or + * lists. + */ +sealed class DeclarationT( + factName: String, + id: Id, + parent: Id, // SmlClass | SmlCompilationUnit + name: String, + vararg otherArguments: Any? +) : + NodeWithParent(factName, id, parent, name, *otherArguments) { + + /** + * The name of the declaration. + */ + abstract val name: String +} + +/** + * This Prolog fact represents annotations. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the compilationUnitT fact for the containing compilation unit. + * + * @param name + * The name of the annotation. + * + * @param parameters + * The list of parameters or null. Each element in the list is the ID of a parameterT fact for the respective parameter. + * Note that an empty list is used for an annotation with an empty parameter list, e.g. `annotation A()`, while null is + * used for an annotation with no parameter list at all, like `annotation B`. + * + * @param constraints + * The IDs of the facts for the constraints of this annotation or null if the annotation has no type parameter + * constraints. Note that the grammar forbids the use of the keyword `where` without any type parameter constraints + * afterwards, so this will never be set to an empty list. + */ +data class AnnotationT( + override val id: Id, + override val parent: Id, // Actually just SmlCompilationUnit but this allows a handleDeclaration function + override val name: String, + val parameters: List>?, + val constraints: List>?, +) : DeclarationT("annotationT", id, parent, name, parameters) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents attributes of a class or interface. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the classT/interfaceT fact for the containing class or interface. + * + * @param name + * The name of the attribute. + * + * @param isStatic + * Whether this attribute is static. + * + * @param type + * The ID of the fact for the type of the attribute or null if no type was specified. + */ +data class AttributeT( + override val id: Id, + override val parent: Id, // Actually just SmlClass but this allows a handleDeclaration function + override val name: String, + val isStatic: Boolean, + val type: Id? +) : + DeclarationT("attributeT", id, parent, name, isStatic, type) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents classes. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the containing compilation unit, class or interface. + * + * @param name + * The name of the class. + * + * @param typeParameters + * The list of type parameters or null. Each element in the list is the ID of a typeParameterT fact for the respective + * type parameter. Note that an empty list is used for a class with an empty type parameter list, e.g. `class A<>`, + * while null is used for a class with no type parameter list at all, like `class B`. + * + * @param parameters + * The list of parameters or null. Each element in the list is the ID of a parameterT fact for the respective parameter. + * Note that an empty list is used for a class with a constructor with an empty parameter list, e.g. `class A()`, while + * null is used for a class with no constructor at all, like `class B`. + * + * @param parentTypes + * The IDs of the facts for the parent types of this class or null if the class has no parent types. Note that the + * grammar forbids the use of the keyword `sub` without any parent types afterwards, so this will never be set to an + * empty list. + * + * @param constraints + * The IDs of the facts for the constraints of this class or null if the class has no type parameter constraints. Note + * that the grammar forbids the use of the keyword `where` without any type parameter constraints afterwards, so this + * will never be set to an empty list. + * + * @param members + * The list of class members or null. Each element in the list is the ID of the fact for the respective member. Note + * that an empty list is used for a class with an empty body, e.g. `class A {}`, while null is used for a class without + * a body, like `class B`. + */ +data class ClassT( + override val id: Id, + override val parent: Id, // SmlClass | SmlCompilationUnit + override val name: String, + val typeParameters: List>?, + val parameters: List>?, + val parentTypes: List>?, + val constraints: List>?, + val members: List>? // SmlClassMember | SmlProtocol +) : DeclarationT( + "classT", + id, + parent, + name, + typeParameters, + parameters, + parentTypes, + constraints, + members +) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents enums. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the containing compilation unit, class or interface. + * + * @param name + * The name of the enum. + * + * @param variants + * The list of instances or null. Each element in the list is the ID of the enumVariantT fact for the respective + * instance. Note that an empty list is used for an enum with an empty body, e.g. `enum A {}`, while null is used for + * an enum without a body, like `enum B`. + */ +data class EnumT( + override val id: Id, + override val parent: Id, // SmlClass | SmlCompilationUnit + override val name: String, + val variants: List>? +) : + DeclarationT("enumT", id, parent, name, variants) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents enum instances. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the enumT fact for the containing enum. + * + * @param name + * The name of the enum instance. + * + * @param typeParameters + * The list of type parameters or null. Each element in the list is the ID of a typeParameterT fact for the respective + * type parameter. Note that an empty list is used for a variant with an empty type parameter list, e.g. `A<>`, + * while null is used for a variant with no type parameter list at all, like `B`. + * + * @param parameters + * The list of parameters or null. Each element in the list is the ID of a parameterT fact for the respective parameter. + * Note that an empty list is used for a variant with a constructor with an empty parameter list, e.g. `A()`, while + * null is used for a variant with no constructor at all, like `B`. + * + * @param constraints + * The IDs of the facts for the constraints of this variant or null if the variant has no type parameter constraints. + * Note that the grammar forbids the use of the keyword `where` without any type parameter constraints afterwards, so + * this will never be set to an empty list. + */ +data class EnumVariantT( + override val id: Id, + override val parent: Id, // Actually just SmlEnum but this allows a handleDeclaration function + override val name: String, + val typeParameters: List>?, + val parameters: List>?, + val constraints: List>? +) : + DeclarationT("enumVariantT", id, parent, name, typeParameters, parameters, constraints) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents functions. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the containing compilation unit, class or interface. + * + * @param name + * The name of the function. + * + * @param isStatic + * Whether this function is static. + * + * @param typeParameters + * The list of type parameters or null. Each element in the list is the ID of a typeParameterT fact for the respective + * type parameter. Note that an empty list is used for a function with an empty type parameter list, e.g. `fun a<>()`, + * while null is used for a function with no type parameter list at all, like `fun b()`. + * + * @param parameters + * The IDs of the parameterT facts for the parameters of the function. The grammar requires the list to be there so this + * is never null. + * + * @param results + * The list of result or null. Each element in the list is the ID of a resultT fact for the respective result. Note that + * an empty list is used for a function with an empty result list, e.g. `fun a() -> ()`, while null is used for a + * function with no result list at all, like `fun b()`. + * + * @param constraints + * The IDs of the facts for the constraints of this function or null if the function has no type parameter constraints. + * Note that the grammar forbids the use of the keyword `where` without any type parameter constraints afterwards, so + * this will never be set to an empty list. + */ +data class FunctionT( + override val id: Id, + override val parent: Id, // SmlClass | SmlCompilationUnit + override val name: String, + val isStatic: Boolean, + val typeParameters: List>?, + val parameters: List>, + val results: List>?, + val constraints: List>? +) : DeclarationT( + "functionT", + id, + parent, + name, + isStatic, + typeParameters, + parameters, + results, + constraints +) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents imports. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the packageT fact for the containing package. + * + * @param importedNamespace + * The qualified name of the imported namespace. + * + * @param alias + * The alias the namespace should be imported under or null if no alias is specified. + */ +data class ImportT( + override val id: Id, + override val parent: Id, + val importedNamespace: String, + val alias: String? +) : + NodeWithParent("importT", id, parent, importedNamespace, alias) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents parameters. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. an annotation. + * + * @param name + * The name of the parameter. + * + * @param isVariadic + * Whether this parameter is variadic. + * + * @param type + * The ID of the fact for the type or null if no type is specified. + * + * @param defaultValue + * The ID of the fact for the default value or null if the parameter is required and has no default value. + */ +data class ParameterT( + override val id: Id, + override val parent: Id, // Actually just SmlDeclaration but this allows a handleDeclaration function + override val name: String, + val isVariadic: Boolean, + val type: Id?, + val defaultValue: Id? +) : DeclarationT( + "parameterT", + id, + parent, + name, + isVariadic, + type, + defaultValue +) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents results. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a function. + * + * @param name + * The name of the result. + * + * @param type + * The ID of the fact for the type or null if no type is specified. + */ +data class ResultT( + override val id: Id, + override val parent: Id, + override val name: String, + val type: Id? +) : + DeclarationT("resultT", id, parent, name, type) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents type parameters. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a class. + * + * @param name + * The name of the type parameter. + * + * @param variance + * The variance of this type parameter ("in" for contravariance, "out" for covariance, or `null` for invariance). + */ +data class TypeParameterT( + override val id: Id, + override val parent: Id, + override val name: String, + val variance: String? +) : DeclarationT("typeParameterT", id, parent, name, variance) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents workflows. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the compilationUnitT fact for the containing compilation unit. + * + * @param name + * The name of the workflow. + * + * @param statements + * The IDs of the facts for the statements in the workflow body. The grammar requires the body to be there so this is + * never null. + */ +data class WorkflowT( + override val id: Id, + override val parent: Id, // Actually just SmlCompilationUnit but this allows a handleDeclaration function + override val name: String, + val statements: List> +) : DeclarationT("workflowT", id, parent, name, statements) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents steps. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the compilationUnitT fact for the containing compilation unit. + * + * @param name + * The name of the step. + * + * @param parameters + * The IDs of the parameterT facts for the parameters of the step. The grammar requires the list to be there so this is + * never null. + * + * @param results + * The list of result or null. Each element in the list is the ID of a resultT fact for the respective result. Note that + * an empty list is used for a step with an empty result list, e.g. `step a() -> () {}`, while null is used for a step + * with no result list at all, like `step b() {}`. + * + * @param statements + * The IDs of the facts for the statements in the body of the step. The grammar requires the body to be there so this is + * never null. + */ +data class StepT( + override val id: Id, + override val parent: Id, // Actually just SmlCompilationUnit but this allows a handleDeclaration function + override val name: String, + val visibility: String?, + val parameters: List>, + val results: List>?, + val statements: List> +) : DeclarationT( + "stepT", + id, + parent, + name, + visibility, + parameters, + results, + statements +) { + override fun toString() = super.toString() +} + +/********************************************************************************************************************** + * Statements + **********************************************************************************************************************/ + +/** + * Prolog facts for statements. + * + * @param factName + * The name of this fact. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent. + * + * @param otherArguments + * Arguments of this fact beyond ID and parent. Arguments can either be `null`, booleans, IDs, number, strings or lists. + */ +sealed class StatementT( + factName: String, + id: Id, + parent: Id, // SmlBlockLambda | SmlWorkflow | SmlWorkflowStep + vararg otherArguments: Any? +) : + NodeWithParent(factName, id, parent, *otherArguments) + +/** + * This Prolog fact represents assignments. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, such as a workflow. + * + * @param assignees + * The assignees of this assignment (has at least one). + * + * @param expression + * The ID of the fact for the expression on the right-hand side of this assignment. + */ +data class AssignmentT( + override val id: Id, + override val parent: Id, + val assignees: List>, + val expression: Id +) : + StatementT("assignmentT", id, parent, assignees, expression) { + override fun toString() = super.toString() +} + +/** + * Facts that can be uses as assignees in an assignmentT fact. + */ +interface AssigneeT + +/** + * This Prolog fact represents results of a block lambda. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the assignmentT fact for the containing assignment. + * + * @param name + * The name of the result. + */ +data class BlockLambdaResultT( + override val id: Id, + override val parent: Id, + override val name: String +) : + DeclarationT("blockLambdaResultT", id, parent, name), AssigneeT { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents placeholder declarations. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the assignmentT fact for the containing assignment. + * + * @param name + * The name of the placeholder. + */ +data class PlaceholderT( + override val id: Id, + override val parent: Id, + override val name: String +) : + DeclarationT("placeholderT", id, parent, name), AssigneeT { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents wildcards in an assignment, which discard the assigned value. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the assignmentT fact for the containing assignment. + */ +data class WildcardT(override val id: Id, override val parent: Id) : + NodeWithParent("wildcardT", id, parent), AssigneeT { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents yields. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the assignmentT fact for the containing assignment. + * + * @param result + * The ID of the resultT fact for the referenced result or an unresolvedT fact if the result could not be + * resolved. + */ +data class YieldT( + override val id: Id, + override val parent: Id, + val result: Id +) : + NodeWithParent("yieldT", id, parent, result), AssigneeT { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents expression statements. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, such as a workflow. + * + * @param expression + * The ID of the fact for the expression that is evaluated. + */ +data class ExpressionStatementT( + override val id: Id, + override val parent: Id, + val expression: Id +) : + StatementT("expressionStatementT", id, parent, expression) { + override fun toString() = super.toString() +} + +/********************************************************************************************************************** + * Expressions + **********************************************************************************************************************/ + +/** + * Prolog facts for expressions. + * + * @param factName + * The name of this fact. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param otherArguments + * Arguments of this fact beyond ID, parent, and enclosing. Arguments can either be `null`, booleans, IDs, number, + * strings or lists. + */ +sealed class ExpressionT( + factName: String, + id: Id, + parent: Id, + enclosing: Id, + vararg otherArguments: Any? +) : + NodeWithParent(factName, id, parent, enclosing, *otherArguments) { + + /** + * The ID of the fact for closest ancestor that is not an expression. This is usually a statement but can also be a + * parameter if the expression is its default value. + */ + abstract val enclosing: Id +} + +/** + * This Prolog fact represents arguments for annotation calls or calls. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. This is either a statement or an annotation call. + * + * @param parameter + * If the argument is named, this is the ID of the parameterT fact for the referenced parameter or an unresolvedT + * fact if the parameter could not be resolved. If the argument is purely positional this is null. + * + * @param value + * The ID of the fact for the expression that represents the passed value. + */ +data class ArgumentT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val parameter: Id?, + val value: Id +) : ExpressionT("argumentT", id, parent, enclosing, parameter, value) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents block lambdas. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param parameters + * The list of parameters. Each element in the list is the ID of a parameterT fact for the respective parameter.The + * grammar requires the body to be there so this is never null. + * + * @param statements + * The IDs of the facts for the statements in the body of the lambda. The grammar requires the body to be there + * so this is never null. + */ +data class BlockLambdaT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val parameters: List>, + val statements: List> +) : ExpressionT( + "blockLambdaT", + id, + parent, + enclosing, + parameters, + statements +) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents boolean literals. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param value + * The value of the literal. + */ +data class BooleanT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val value: Boolean +) : + ExpressionT("booleanT", id, parent, enclosing, value) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents calls. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. another call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param receiver + * The ID of the fact for the callable that is called. + * + * @param typeArguments + * The list of type arguments or null. Each element in the list is the ID of a typeArgumentT fact for the respective + * type argument. Note that an empty list is used for a call with an empty type argument list, e.g. `a<>()`, while null + * is used for a call with no type argument list at all, like `b()`. + * + * @param arguments + * The IDs of the argumentT facts for the arguments of the call. The grammar requires the list to be there so this + * is never null. + */ +data class CallT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val receiver: Id, + val typeArguments: List>?, + val arguments: List> +) : ExpressionT( + "callT", + id, + parent, + enclosing, + receiver, + typeArguments, + arguments +) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents expression lambdas. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param parameters + * The list of parameters. Each element in the list is the ID of a parameterT fact for the respective parameter.The + * grammar requires the body to be there so this is never null. + * + * @param result + * The ID of the fact for the result of the lambda. + */ +data class ExpressionLambdaT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val parameters: List>, + val result: Id +) : ExpressionT( + "expressionLambdaT", + id, + parent, + enclosing, + parameters, + result +) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents float literals. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param value + * The value of the literal. + */ +data class FloatT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val value: Double +) : + ExpressionT("floatT", id, parent, enclosing, value) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents member accesses. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param receiver + * The ID of the fact for the receiver of the indexed access. + * + * @param index + * The accessed index. + */ +data class IndexedAccessT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val receiver: Id, + val index: Id +) : ExpressionT( + "indexedAccessT", + id, + parent, + enclosing, + receiver, + index +) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents infix operations. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param leftOperand + * The ID of the fact for the expression that is used as the left operand. + * + * @param operator + * The operator of this operation. + * + * @param rightOperand + * The ID of the fact for the expression that is used as the right operand. + */ +data class InfixOperationT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val leftOperand: Id, + val operator: String, + val rightOperand: Id +) : ExpressionT( + "infixOperationT", + id, + parent, + enclosing, + leftOperand, + operator, + rightOperand +) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents integer literals. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param value + * The value of the literal. + */ +data class IntT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val value: Int +) : + ExpressionT("intT", id, parent, enclosing, value) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents member accesses. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param receiver + * The ID of the fact for the receiver of the member access. + * + * @param isNullSafe + * Whether this member access is null safe. + * + * @param member + * The ID of the referenceT fact for the accessed member. + */ +data class MemberAccessT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val receiver: Id, + val isNullSafe: Boolean, + val member: Id +) : ExpressionT( + "memberAccessT", + id, + parent, + enclosing, + receiver, + isNullSafe, + member +) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represent the `null` literal. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + */ +data class NullT( + override val id: Id, + override val parent: Id, + override val enclosing: Id +) : + ExpressionT("nullT", id, parent, enclosing) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents parenthesized expressions. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param expression + * The ID of the fact for the expression inside the parentheses. + */ +data class ParenthesizedExpressionT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val expression: Id +) : ExpressionT("parenthesizedExpressionT", id, parent, enclosing, expression) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents prefix operations. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param operator + * The operator of this operation. + * + * @param operand + * The ID of the fact for the expression that is used as the operand. + */ +data class PrefixOperationT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val operator: String, + val operand: Id +) : ExpressionT( + "prefixOperationT", + id, + parent, + enclosing, + operator, + operand +) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents references. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param declaration + * The ID of the fact for the referenced declaration or an unresolvedT fact if the reference could not be resolved. + */ +data class ReferenceT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val declaration: Id +) : ExpressionT("referenceT", id, parent, enclosing, declaration) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents string literals. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param value + * The value of the literal. + */ +data class StringT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val value: String +) : ExpressionT("stringT", id, parent, enclosing, value) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents string literals. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param expressions + * Template string parts and template expressions. + */ +data class TemplateStringT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val expressions: List> +) : ExpressionT("templateStringT", id, parent, enclosing, expressions) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents starts of template string. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param value + * The value of the template string part. + */ +data class TemplateStringStartT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val value: String +) : ExpressionT("templateStringStartT", id, parent, enclosing, value) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents inner parts of template string. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param value + * The value of the template string part. + */ +data class TemplateStringInnerT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val value: String +) : ExpressionT("templateStringInnerT", id, parent, enclosing, value) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents the end of template strings. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param enclosing + * The ID of the fact for closest ancestor that is not an expression. + * + * @param value + * The value of the template string part. + */ +data class TemplateStringEndT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val value: String +) : ExpressionT("templateStringEndT", id, parent, enclosing, value) { + override fun toString() = super.toString() +} + +/********************************************************************************************************************** + * Types + **********************************************************************************************************************/ + +/** + * Prolog facts for types. + * + * @param factName + * The name of this fact. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent. + * + * @param otherArguments + * Arguments of this fact beyond ID and parent. Arguments can either be `null`, booleans, IDs, number, strings or lists. + */ +sealed class TypeT( + factName: String, + id: Id, + parent: Id, + vararg otherArguments: Any? +) : + NodeWithParent(factName, id, parent, *otherArguments) + +/** + * This Prolog fact represents callable types. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a parameter. + * + * @param parameters + * The IDs of the parameterT facts for the parameters of the callable type. The grammar requires the list to be there so + * this is never null. + * + * @param results + * The IDs of the resultT facts for the results of the callable type. The grammar requires the list to be there so this + * is never null. + */ +data class CallableTypeT( + override val id: Id, + override val parent: Id, + val parameters: List>, + val results: List> +) : TypeT("callableTypeT", id, parent, parameters, results) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents member types. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a parameter. + * + * @param receiver + * The ID of the fact for the receiver of the member type. + * + * @param member + * The ID of the namedTypeT fact for the accessed member type. + */ +data class MemberTypeT( + override val id: Id, + override val parent: Id, + val receiver: Id, + val member: Id +) : + TypeT("memberTypeT", id, parent, receiver, member) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents named types like classes. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a parameter. + * + * @param declaration + * The ID of the fact for the declaration that is used as the type or an unresolvedT fact if the declaration could not + * be resolved. + * + * @param typeArguments + * The list of type arguments or null. Each element in the list is the ID of a typeArgumentT fact for the respective + * type argument. Note that an empty list is used for a named type with an empty type argument list, e.g. `A<>`, while + * null is used for a named type with no type argument list at all, like `B`. + * + * @param isNullable + * Whether `null` is a valid instance of the type. + */ +data class NamedTypeT( + override val id: Id, + override val parent: Id, + val declaration: Id, + val typeArguments: List>?, + val isNullable: Boolean +) : TypeT( + "namedTypeT", + id, + parent, + declaration, + typeArguments, + isNullable +) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents parenthesized types. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a parameter. + * + * @param type + * The ID of the fact for the type inside the parentheses. + */ +data class ParenthesizedTypeT( + override val id: Id, + override val parent: Id, + val type: Id +) : + TypeT("parenthesizedTypeT", id, parent, type) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents union types. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a parameter. + * + * @param typeArguments + * The IDs of the typeArgumentT facts for the type arguments of this union type. Note that the grammar requires the list + * to be there (can be empty) so this will never be `null`. + */ +data class UnionTypeT( + override val id: Id, + override val parent: Id, + val typeArguments: List> +) : + TypeT("unionTypeT", id, parent, typeArguments) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents type arguments. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a call. + * + * @param typeParameter + * If the type argument is named, this is the ID of the typeParameterT fact for the referenced type parameter or an + * unresolvedT fact if the type parameter could not be resolved. If the type argument is purely positional this is null. + * + * @param value + * The ID of the fact for the type that represents the passed value. + */ +data class TypeArgumentT( + override val id: Id, + override val parent: Id, + val typeParameter: Id?, + val value: Id +) : + NodeWithParent("typeArgumentT", id, parent, typeParameter, value) { + override fun toString() = super.toString() +} + +/** + * A Prolog fact that can be used as the value of a type argument. + */ +interface TypeArgumentValueT + +/** + * This Prolog fact represents star projections `*`. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the containing typeArgumentT fact. + */ +data class StarProjectionT(override val id: Id, override val parent: Id) : + NodeWithParent("starProjectionT", id, parent), TypeArgumentValueT { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents type projections. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the containing typeArgumentT fact. + * + * @param variance + * The variance of this type projection ("in" for contravariance, "out" for covariance, or `null` for invariance). + * + * @param type + * The ID of the fact for the type to use for projection. + */ +data class TypeProjectionT( + override val id: Id, + override val parent: Id, + val variance: String?, + val type: Id +) : + NodeWithParent("typeProjectionT", id, parent, variance, type), TypeArgumentValueT { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents type parameter constraints. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the logical parent, e.g. a class. + * + * @param leftOperand + * The ID of the typeParameterT fact for the type parameter that is used as the left operand or an unresolvedT fact if + * the type parameter could not be resolved. + * + * @param operator + * The operator of this operation. + * + * @param rightOperand + * The ID of the fact for the type that is used as the right operand. + */ +data class TypeParameterConstraintT( + override val id: Id, + override val parent: Id, + val leftOperand: Id, + val operator: String, + val rightOperand: Id +) : NodeWithParent( + "typeParameterConstraintT", + id, + parent, + leftOperand, + operator, + rightOperand +) { + override fun toString() = super.toString() +} + +/********************************************************************************************************************** + * Other + **********************************************************************************************************************/ + +/** + * This Prolog fact represents annotation calls. + * + * @param id + * The ID of this fact. + * + * @param parent + * The ID of the fact for the annotated declaration. + * + * @param annotation + * The ID of the annotationT fact for the referenced annotation or an unresolvedT fact if the annotation could + * not be resolved. + * + * @param arguments + * The list of arguments or null. Each element in the list is the ID of an argumentT fact for the respective argument. + * Note that an empty list is used for an annotation call with an empty argument list, e.g. `@A()`, while null is used + * for an annotation call without an argument list, like `@B`. + */ +data class AnnotationCallT( + override val id: Id, + override val parent: Id, + val annotation: Id, + val arguments: List>? +) : + NodeWithParent("annotationCallT", id, parent, annotation, arguments) { + override fun toString() = super.toString() +} + +data class ProtocolT( + override val id: Id, + override val parent: Id, + val subterms: List>?, + val term: Id? +) : NodeWithParent("protocolT", id, parent, subterms, term) { + override fun toString() = super.toString() +} + +sealed class ProtocolTermT( + factName: String, + id: Id, + parent: Id, + enclosing: Id, + vararg otherArguments: Any? +) : NodeWithParent(factName, id, parent, enclosing, *otherArguments) { + + /** + * The ID of the fact for closest ancestor that is not an expression. This is usually a statement but can also be a + * parameter if the expression is its default value. + */ + abstract val enclosing: Id +} + +data class ProtocolAlternativeT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val terms: List> +) : ProtocolTermT("protocolAlternativeT", id, parent, enclosing, terms) { + override fun toString() = super.toString() +} + +data class ProtocolComplementT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val universe: Id?, + val references: List>? +) : ProtocolTermT("protocolComplementT", id, parent, enclosing, references) { + override fun toString() = super.toString() +} + +data class ProtocolParenthesizedTermT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val term: Id +) : ProtocolTermT("protocolParenthesizedTermT", id, parent, enclosing, term) { + override fun toString() = super.toString() +} + +data class ProtocolQuantifiedTermT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val term: Id, + val quantifier: String +) : ProtocolTermT("protocolQuantifiedTermT", id, parent, term, enclosing, quantifier) { + override fun toString() = super.toString() +} + +data class ProtocolReferenceT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val token: Id, +) : ProtocolTermT("protocolReferenceT", id, parent, enclosing, token) { + override fun toString() = super.toString() +} + +data class ProtocolSequenceT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val terms: List>, +) : ProtocolTermT("protocolSequenceT", id, parent, enclosing, terms) { + override fun toString() = super.toString() +} + +data class ProtocolSubtermT( + override val id: Id, + override val parent: Id, // Actually just SmlProtocol but this allows a handleDeclaration function + override val name: String, + val term: Id, +) : DeclarationT("protocolSubtermT", id, parent, name, term) { + override fun toString() = super.toString() +} + +data class ProtocolTokenClassT( + override val id: Id, + override val parent: Id, + override val enclosing: Id, + val value: String, +) : ProtocolTermT("protocolTokenClassT", id, parent, enclosing, value) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact represents cross-references that could not be resolved. It is used so the name of the referenced + * declaration can be retrieved. + * + * @param id + * The ID of this fact. + * + * @param name + * The name of the referenced declaration. + */ +data class UnresolvedT(override val id: Id, val name: String) : Node("unresolvedT", id, name) { + override fun toString() = super.toString() +} + +/********************************************************************************************************************** + * Relations + **********************************************************************************************************************/ + +/** + * Prolog facts that add additional information to nodes and do not have their own ID. + * + * @param factName + * The name of this fact. + * + * @param target + * The ID of the node that should be enhanced. + * + * @param otherArguments + * The arguments of this fact. Arguments can either be `null`, booleans, IDs, number, strings or lists. + */ +sealed class Relation(factName: String, target: Id, vararg otherArguments: Any?) : + PlFact(factName, target, *otherArguments) { + + /** + * The ID of the node that should be enhanced. + */ + abstract val target: Id +} + +/** + * This Prolog fact stores the resource URI of a compilation unit. + * + * @param target + * The ID of the fact for the respective compilation unit. + * + * @param uri + * The resource URI of the compilation unit. + */ +data class ResourceS(override val target: Id, val uri: String) : + Relation("resourceS", target, uri) { + override fun toString() = super.toString() +} + +/** + * This Prolog fact stores the location of the source code for some node in a source file. + * + * @param target + * The ID of the fact for the respective node. + * + * @param offset + * The total offset of start of the source code for the node from the start of the file. + * + * @param line + * The line where the start of the source code for the node is located. + * + * @param column + * The column where the start of the source code for the node is located. + * + * @param length + * The length the source code for the node. + */ +data class SourceLocationS( + override val target: Id, + val uriHash: String, + val offset: Int, + val line: Int, + val column: Int, + val length: Int +) : + Relation("sourceLocationS", target, uriHash, offset, line, column, length) { + override fun toString() = super.toString() +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/model/facts/Terms.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/model/facts/Terms.kt new file mode 100644 index 000000000..a348a40f1 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/model/facts/Terms.kt @@ -0,0 +1,108 @@ +package de.unibonn.simpleml.prologBridge.model.facts + +import de.unibonn.simpleml.utils.Id +import java.text.NumberFormat +import java.text.ParseException + +sealed class PlTerm { + companion object { + + @JvmStatic + fun fromJavaRepresentation(javaRepresentation: List): List { + return javaRepresentation.map { + when (it) { + null -> PlNull + is Boolean -> PlBooleanAtom(it) + is Id<*> -> PlNumber(it.value) + is Number -> PlNumber(it) + is String -> PlString(it) + is List<*> -> PlList(fromJavaRepresentation(it)) + else -> throw IllegalArgumentException("Cannot handle type " + it.javaClass.typeName + ".") + } + } + } + + @JvmStatic + fun fromString(s: String): List { + return s.split(", (?![^\\[]*[\\]])".toRegex()).map { + if (it == "null") { + PlNull + } else if (it == "true" || it == "false") { + PlBooleanAtom(it) + } else if (it.matches(Regex("[0-9\\.]+"))) { + PlNumber(it) + } else if (it.matches(Regex("\\'(.*?)\\'"))) { + PlString(it.substring(1, it.length - 1)) + } else if (it.matches(Regex("\\[(.*?)\\]"))) { + PlList(fromString(it.substring(1, it.length - 1))) + } else { + throw IllegalArgumentException("Cannot handle string format $it. ") + } + } + } + } +} + +open class PlAtom(val value: String) : PlTerm() { + override fun toString() = value +} + +object PlNull : PlAtom("null") { + override fun toString() = "null" +} + +class PlBooleanAtom : PlTerm { + val value: Boolean + + constructor(b: Boolean) { + value = b + } + + constructor(s: String) { + value = (s == "true") + } + + override fun toString() = value.toString() +} + +class PlNumber : PlTerm { + var value: Number = 0 + + constructor(x: Number) { + value = x + } + + constructor(s: String?) { + try { + value = NumberFormat.getInstance().parse(s) + } catch (e: ParseException) { + // TODO Auto-generated catch block + e.printStackTrace() + } + } + + override fun toString(): String { + return value.toString() + } +} + +class PlString(private val value: String) : PlTerm() { + fun toRawString() = value + override fun toString() = "'$value'" // TODO: this is an atom and not a string! a string has "" +} + +/** + * A data structure that represents a Prolog list. + * + * @param arguments The arguments of the list. + */ +data class PlList(val arguments: List) : PlTerm() { + override fun toString() = arguments.joinToString(prefix = "[", postfix = "]") { it.toString() } +} + +// TODO: +// there are three different ways to create a PlList or PlFact: +// * parsed from string +// * create from basic java types like list/string +// * created with a list of PlArguments +// we should have common default case and always offer the same set of options diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/scoping/IndexExtensions.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/scoping/IndexExtensions.kt new file mode 100644 index 000000000..37a5a22ba --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/scoping/IndexExtensions.kt @@ -0,0 +1,101 @@ +package de.unibonn.simpleml.scoping + +import com.google.inject.Inject +import de.unibonn.simpleml.scoping.IndexExtensionsInjectionTarget.containerManager +import de.unibonn.simpleml.scoping.IndexExtensionsInjectionTarget.resourceDescriptionsProvider +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.emf.ecore.resource.ResourceSet +import org.eclipse.xtext.resource.IContainer +import org.eclipse.xtext.resource.IEObjectDescription +import org.eclipse.xtext.resource.IResourceDescription +import org.eclipse.xtext.resource.IResourceDescriptions +import org.eclipse.xtext.resource.IResourceDescriptionsProvider + +internal object IndexExtensionsInjectionTarget { + + @Inject + lateinit var containerManager: IContainer.Manager + + @Inject + lateinit var resourceDescriptionsProvider: IResourceDescriptionsProvider +} + +/** + * Returns all global declarations that are visible from this context. If this [EObject] is not in a [Resource] or the + * [Resource] not in a [ResourceSet] an empty list is returned. + */ +fun EObject.allGlobalDeclarations(): List { + return eResource() + ?.visibleContainers() + ?.asSequence() + ?.map { it.exportedObjects } + ?.flatten() + ?.filter { it.isGlobalDeclaration() } + ?.toList() + .orEmpty() +} + +/** + * Returns all global declarations that are visible from this context and in the same [Resource]. If this [EObject] is + * not in a [Resource] or the [Resource] not in a [ResourceSet] an empty list is returned. + */ +fun EObject.ownGlobalDeclarations(): List { + return eResource() + ?.resourceDescriptionOrNull() + ?.exportedObjects + ?.asSequence() + ?.filter { it.isGlobalDeclaration() } + ?.toList() + .orEmpty() +} + +/** + * Returns all global declarations that are visible from this context but in another [Resource]. If this [EObject] is + * not in a [Resource] or the [Resource] not in a [ResourceSet] and empty list is returned. + */ +fun EObject.externalGlobalDeclarations(): List { + return allGlobalDeclarations() - ownGlobalDeclarations().toSet() +} + +/** + * Returns a list of [IContainer]s that are visible from this [Resource], including this [Resource]. If this [Resource] + * is not in a [ResourceSet], an empty list is returned. An [IContainer] describes [Resource]s that should be treated + * as visible on the same level during the scoping stage. + */ +private fun Resource.visibleContainers(): List { + val resourceSet = this.resourceSet ?: return emptyList() + + return containerManager.getVisibleContainers( + resourceDescriptionOrNull(), + resourceSet.resourceDescriptions() + ) +} + +/** + * Returns information about this [Resource] or `null` if the [Resource] is not in a [ResourceSet]. + */ +private fun Resource.resourceDescriptionOrNull(): IResourceDescription? { + return resourceSet?.resourceDescriptions()?.getResourceDescription(uri) +} + +/** + * Returns the information about the [Resource]s in this [ResourceSet]. + */ +private fun ResourceSet.resourceDescriptions(): IResourceDescriptions { + return resourceDescriptionsProvider.getResourceDescriptions(this) +} + +/** + * Returns whether this [IEObjectDescription] should be available in other [Resource]s. + */ +private fun IEObjectDescription.isGlobalDeclaration(): Boolean { + return this.eClass in setOf( + Literals.SML_ANNOTATION, + Literals.SML_CLASS, + Literals.SML_ENUM, + Literals.SML_FUNCTION, + Literals.SML_STEP + ) +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/scoping/SimpleMLImportedNamespaceAwareLocalScopeProvider.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/scoping/SimpleMLImportedNamespaceAwareLocalScopeProvider.kt new file mode 100644 index 000000000..2ff2a4f76 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/scoping/SimpleMLImportedNamespaceAwareLocalScopeProvider.kt @@ -0,0 +1,130 @@ +package de.unibonn.simpleml.scoping + +import de.unibonn.simpleml.emf.aliasNameOrNull +import de.unibonn.simpleml.naming.qualifiedNameOrNull +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtext.naming.QualifiedName +import org.eclipse.xtext.scoping.impl.ImportNormalizer +import org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider + +class SimpleMLImportedNamespaceAwareLocalScopeProvider : ImportedNamespaceAwareLocalScopeProvider() { + + /** + * Import all declarations from the listed packages implicitly, such as "simpleml.lang". + */ + override fun getImplicitImports(ignoreCase: Boolean): List { + return listOf( + ImportNormalizer(QualifiedName.create("simpleml", "lang"), true, ignoreCase) + ) + } + + /** + * Import all declarations in the same package implicitly. + * + * See Xtext book page 278 for more information. + */ + override fun internalGetImportedNamespaceResolvers( + context: EObject, + ignoreCase: Boolean + ): List { + + if (context !is SmlCompilationUnit) { + return emptyList() + } + + // Resolve imports - including aliases + val resolvers = context.imports.mapNotNull { + createImportedNamespaceResolver(it.importedNamespace, it.aliasNameOrNull(), ignoreCase) + }.toMutableList() + + // Implicitly import declarations in same package + context.qualifiedNameOrNull()?.let { + resolvers += ImportNormalizer( + it, + true, + ignoreCase + ) + } + + return resolvers + } + + private fun createImportedNamespaceResolver( + namespace: String, + alias: String?, + ignoreCase: Boolean + ): ImportNormalizer? { + + if (namespace.isEmpty()) { + return null + } else if (alias == null) { + return createImportedNamespaceResolver(namespace, ignoreCase) + } + + val importedNamespace = qualifiedNameConverter.toQualifiedName(namespace) + if (importedNamespace == null || importedNamespace.isEmpty) { + return null + } + + return when { + hasWildCard(importedNamespace, ignoreCase) -> null + else -> ImportWithAliasNormalizer(importedNamespace, QualifiedName.create(alias), ignoreCase) + } + } + + private fun hasWildCard(importedNamespace: QualifiedName, ignoreCase: Boolean): Boolean { + return when { + ignoreCase -> importedNamespace.lastSegment.equals(wildCard, ignoreCase = true) + else -> importedNamespace.lastSegment == wildCard + } + } +} + +data class ImportWithAliasNormalizer( + val importedNamespace: QualifiedName, + val alias: QualifiedName, + val ignoreCase: Boolean +) : ImportNormalizer(importedNamespace, false, ignoreCase) { + + init { + require(!(importedNamespace.isEmpty)) { "Imported namespace must not be empty." } + require(alias.segmentCount == 1) { "Alias must have exactly one segment." } + } + + /** + * Converts a fully qualified name to the simple alias that can be used to refer to a declaration. If this + * normalizer is not responsible for the given fully qualified name, null is returned instead. + */ + override fun deresolve(fullyQualifiedName: QualifiedName): QualifiedName? { + return when { + ignoreCase && fullyQualifiedName.equalsIgnoreCase(importedNamespacePrefix) -> alias + !ignoreCase && fullyQualifiedName == importedNamespacePrefix -> alias + else -> null + } + } + + /** + * Converts a simple alias to the fully qualified name of the declaration. If this normalizer is not responsible for + * the given alias, null is returned instead. + */ + override fun resolve(relativeName: QualifiedName): QualifiedName? { + if (relativeName.segmentCount != 1) { + return null + } + + return when { + ignoreCase && relativeName.lastSegment.equals(alias.lastSegment, ignoreCase = true) -> { + importedNamespacePrefix.skipLast(1).append(relativeName.lastSegment) + } + !ignoreCase && relativeName.lastSegment == alias.lastSegment -> { + return importedNamespace + } + else -> null + } + } + + override fun toString(): String { + return "import $importedNamespace as $alias" + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/scoping/SimpleMLResourceDescriptionStrategy.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/scoping/SimpleMLResourceDescriptionStrategy.kt new file mode 100644 index 000000000..4c8a15c5c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/scoping/SimpleMLResourceDescriptionStrategy.kt @@ -0,0 +1,68 @@ +package de.unibonn.simpleml.scoping + +import de.unibonn.simpleml.constant.SmlVisibility +import de.unibonn.simpleml.constant.visibility +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlTypeParameter +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtext.resource.IEObjectDescription +import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy +import org.eclipse.xtext.util.IAcceptor + +/** + * Describes which objects are exported to other resources. + */ +class SimpleMLResourceDescriptionStrategy : DefaultResourceDescriptionStrategy() { + override fun createEObjectDescriptions(eObject: EObject, acceptor: IAcceptor): Boolean { + return when (eObject) { + is SmlCompilationUnit -> { + super.createEObjectDescriptions(eObject, acceptor) + } + is SmlAnnotation -> { + super.createEObjectDescriptions(eObject, acceptor) + } + is SmlAttribute -> { + super.createEObjectDescriptions(eObject, acceptor) + false + } + is SmlClass -> { + super.createEObjectDescriptions(eObject, acceptor) + } + is SmlEnum -> { + super.createEObjectDescriptions(eObject, acceptor) + } + is SmlEnumVariant -> { + super.createEObjectDescriptions(eObject, acceptor) + } + is SmlFunction -> { + super.createEObjectDescriptions(eObject, acceptor) + } + is SmlParameter -> { + super.createEObjectDescriptions(eObject, acceptor) + false + } + is SmlTypeParameter -> { + super.createEObjectDescriptions(eObject, acceptor) + false + } + is SmlStep -> { + if (eObject.visibility() != SmlVisibility.Private) { + super.createEObjectDescriptions(eObject, acceptor) + } else { + false + } + } + else -> { + false + } + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/scoping/SimpleMLScopeProvider.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/scoping/SimpleMLScopeProvider.kt new file mode 100644 index 000000000..27bcd4ab3 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/scoping/SimpleMLScopeProvider.kt @@ -0,0 +1,361 @@ +package de.unibonn.simpleml.scoping + +import de.unibonn.simpleml.constant.SmlVisibility +import de.unibonn.simpleml.constant.visibility +import de.unibonn.simpleml.emf.classMembersOrEmpty +import de.unibonn.simpleml.emf.closestAncestorOrNull +import de.unibonn.simpleml.emf.compilationUnitOrNull +import de.unibonn.simpleml.emf.containingCallableOrNull +import de.unibonn.simpleml.emf.containingClassOrNull +import de.unibonn.simpleml.emf.containingCompilationUnitOrNull +import de.unibonn.simpleml.emf.containingProtocolOrNull +import de.unibonn.simpleml.emf.isStatic +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.placeholdersOrEmpty +import de.unibonn.simpleml.emf.subtermsOrEmpty +import de.unibonn.simpleml.emf.typeParametersOrNull +import de.unibonn.simpleml.emf.variantsOrEmpty +import de.unibonn.simpleml.naming.qualifiedNameOrNull +import de.unibonn.simpleml.simpleML.SimpleMLPackage +import de.unibonn.simpleml.simpleML.SmlAbstractLambda +import de.unibonn.simpleml.simpleML.SmlAbstractNamedTypeDeclaration +import de.unibonn.simpleml.simpleML.SmlAbstractProtocolToken +import de.unibonn.simpleml.simpleML.SmlAbstractStatement +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.simpleML.SmlAnnotationCall +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlArgumentList +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlBlock +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlConstraintList +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlMemberAccess +import de.unibonn.simpleml.simpleML.SmlMemberType +import de.unibonn.simpleml.simpleML.SmlNamedType +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlProtocol +import de.unibonn.simpleml.simpleML.SmlProtocolReference +import de.unibonn.simpleml.simpleML.SmlProtocolSubterm +import de.unibonn.simpleml.simpleML.SmlReference +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlTypeArgument +import de.unibonn.simpleml.simpleML.SmlTypeArgumentList +import de.unibonn.simpleml.simpleML.SmlTypeParameterConstraint +import de.unibonn.simpleml.simpleML.SmlWorkflow +import de.unibonn.simpleml.simpleML.SmlYield +import de.unibonn.simpleml.staticAnalysis.classHierarchy.superClassMembers +import de.unibonn.simpleml.staticAnalysis.linking.parametersOrNull +import de.unibonn.simpleml.staticAnalysis.linking.typeParametersOrNull +import de.unibonn.simpleml.staticAnalysis.resultsOrNull +import de.unibonn.simpleml.staticAnalysis.typing.ClassType +import de.unibonn.simpleml.staticAnalysis.typing.EnumType +import de.unibonn.simpleml.staticAnalysis.typing.EnumVariantType +import de.unibonn.simpleml.staticAnalysis.typing.NamedType +import de.unibonn.simpleml.staticAnalysis.typing.type +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.EReference +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.xtext.naming.QualifiedName +import org.eclipse.xtext.resource.IEObjectDescription +import org.eclipse.xtext.scoping.IScope +import org.eclipse.xtext.scoping.Scopes +import org.eclipse.xtext.scoping.impl.FilteringScope + +/** + * This class contains custom scoping description. + * + * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping + * on how and when to use it. + */ +class SimpleMLScopeProvider : AbstractSimpleMLScopeProvider() { + + override fun getScope(context: EObject, reference: EReference): IScope { + return when (context) { + is SmlArgument -> scopeForArgumentParameter(context) + is SmlNamedType -> scopeForNamedTypeDeclaration(context) + is SmlProtocolReference -> scopeForProtocolReferenceToken(context) + is SmlReference -> scopeForReferenceDeclaration(context) + is SmlTypeArgument -> scopeForTypeArgumentTypeParameter(context) + is SmlTypeParameterConstraint -> scopeForTypeParameterConstraintLeftOperand(context) + is SmlAnnotationCall, is SmlYield -> { + super.getScope(context, reference) + } + else -> IScope.NULLSCOPE + } + } + + private fun scopeForArgumentParameter(smlArgument: SmlArgument): IScope { + val parameters = smlArgument + .closestAncestorOrNull() + ?.parametersOrNull() + ?: emptyList() + return Scopes.scopeFor(parameters) + } + + private fun scopeForReferenceDeclaration(context: SmlReference): IScope { + val container = context.eContainer() + return when { + container is SmlMemberAccess && container.member == context -> scopeForMemberAccessDeclaration(container) + else -> { + val resource = context.eResource() + val packageName = context.containingCompilationUnitOrNull()?.qualifiedNameOrNull() + + // Declarations in other files + var result: IScope = FilteringScope( + super.delegateGetScope(context, SimpleMLPackage.Literals.SML_REFERENCE__DECLARATION) + ) { + it.isReferencableExternalDeclaration(resource, packageName) + } + + // Declarations in this file + result = declarationsInSameFile(resource, result) + + // Declarations in this package + result = declarationsInSamePackageDeclaration(resource, result) + + // Declarations in containing classes + context.containingClassOrNull()?.let { + result = classMembers(it, result) + } + + // Declarations in containing blocks + localDeclarations(context, result) + } + } + } + + /** + * Removes declarations in this [Resource], [SmlAnnotation]s, and internal [SmlStep]s located in other + * [SmlCompilationUnit]s. + */ + private fun IEObjectDescription?.isReferencableExternalDeclaration( + fromResource: Resource, + fromPackageWithQualifiedName: QualifiedName? + ): Boolean { + + // Resolution failed in delegate scope + if (this == null) return false + + val obj = this.eObjectOrProxy + + // Local declarations are added later using custom scoping rules + if (obj.eResource() == fromResource) return false + + // Annotations cannot be referenced + if (obj is SmlAnnotation) return false + + // Internal steps in another package cannot be referenced + return !( + obj is SmlStep && + obj.visibility() == SmlVisibility.Internal && + obj.containingCompilationUnitOrNull()?.qualifiedNameOrNull() != fromPackageWithQualifiedName + ) + } + + private fun scopeForMemberAccessDeclaration(context: SmlMemberAccess): IScope { + val receiver = context.receiver + + // Static access + val receiverDeclaration = when (receiver) { + is SmlReference -> receiver.declaration + is SmlMemberAccess -> receiver.member.declaration + else -> null + } + if (receiverDeclaration != null) { + when (receiverDeclaration) { + is SmlClass -> { + val members = receiverDeclaration.classMembersOrEmpty().filter { it.isStatic() } + val superTypeMembers = receiverDeclaration.superClassMembers() + .filter { it.isStatic() } + .toList() + + return Scopes.scopeFor(members, Scopes.scopeFor(superTypeMembers)) + } + is SmlEnum -> { + return Scopes.scopeFor(receiverDeclaration.variantsOrEmpty()) + } + } + } + + // Call results + var resultScope = IScope.NULLSCOPE + if (receiver is SmlCall) { + val results = receiver.resultsOrNull() + when { + results == null -> return IScope.NULLSCOPE + results.size > 1 -> return Scopes.scopeFor(results) + results.size == 1 -> resultScope = Scopes.scopeFor(results) + } + } + + // Members + val type = (receiver.type() as? NamedType) ?: return resultScope + + return when { + type.isNullable && !context.isNullSafe -> resultScope + type is ClassType -> { + val members = type.smlClass.classMembersOrEmpty().filter { !it.isStatic() } + val superTypeMembers = type.smlClass.superClassMembers() + .filter { !it.isStatic() } + .toList() + + Scopes.scopeFor(members, Scopes.scopeFor(superTypeMembers, resultScope)) + } + type is EnumVariantType -> Scopes.scopeFor(type.smlEnumVariant.parametersOrEmpty()) + else -> resultScope + } + } + + private fun declarationsInSameFile(resource: Resource, parentScope: IScope): IScope { + if (resource.compilationUnitOrNull() != null) { + return Scopes.scopeFor( + emptyList(), + parentScope + ) + } + + val members = resource.compilationUnitOrNull() + ?.members + ?.filter { it !is SmlAnnotation && it !is SmlWorkflow } + ?: emptyList() + + return Scopes.scopeFor( + members, + parentScope + ) + } + + private fun declarationsInSamePackageDeclaration(resource: Resource, parentScope: IScope): IScope { + val members = resource.compilationUnitOrNull() + ?.members + ?.filter { it !is SmlAnnotation && it !is SmlWorkflow } + ?: emptyList() + + return Scopes.scopeFor( + members, + parentScope + ) + } + + private fun classMembers(context: SmlClass, parentScope: IScope): IScope { + return when (val containingClassOrNull = context.containingClassOrNull()) { + is SmlClass -> Scopes.scopeFor( + context.classMembersOrEmpty(), + classMembers(containingClassOrNull, parentScope) + ) + else -> Scopes.scopeFor(context.classMembersOrEmpty(), parentScope) + } + } + + private fun localDeclarations(context: EObject, parentScope: IScope): IScope { + + // Placeholders + val placeholders = when (val containingStatement = context.closestAncestorOrNull()) { + null -> emptyList() + else -> + containingStatement + .closestAncestorOrNull() + ?.placeholdersUpTo(containingStatement) + .orEmpty() + } + + // Parameters + val containingCallable = context.containingCallableOrNull() + val parameters = containingCallable.parametersOrEmpty() + + // Local declarations + val localDeclarations = placeholders + parameters + + return when (containingCallable) { + + // Lambdas can be nested + is SmlAbstractLambda -> Scopes.scopeFor( + localDeclarations, + localDeclarations(containingCallable, parentScope) + ) + else -> Scopes.scopeFor(localDeclarations, parentScope) + } + } + + private fun SmlBlock.placeholdersUpTo(containingStatement: SmlAbstractStatement): List { + return this.statements + .takeWhile { it !== containingStatement } + .filterIsInstance() + .flatMap { it.placeholdersOrEmpty() } + } + + private fun scopeForNamedTypeDeclaration(context: SmlNamedType): IScope { + val container = context.eContainer() + return when { + container is SmlMemberType && container.member == context -> scopeForMemberTypeDeclaration(container) + else -> { + super.getScope(context, SimpleMLPackage.Literals.SML_NAMED_TYPE__DECLARATION) + } + } + } + + private fun scopeForMemberTypeDeclaration(context: SmlMemberType): IScope { + val type = (context.receiver.type() as? NamedType) ?: return IScope.NULLSCOPE + + return when { + type.isNullable -> IScope.NULLSCOPE + type is ClassType -> { + val members = + type.smlClass.classMembersOrEmpty().filterIsInstance() + val superTypeMembers = type.smlClass.superClassMembers() + .filterIsInstance() + .toList() + + Scopes.scopeFor(members, Scopes.scopeFor(superTypeMembers)) + } + type is EnumType -> Scopes.scopeFor(type.smlEnum.variantsOrEmpty()) + else -> IScope.NULLSCOPE + } + } + + private fun scopeForProtocolReferenceToken(context: SmlProtocolReference): IScope { + val containingClass = context.containingClassOrNull() ?: return IScope.NULLSCOPE + val containingProtocol = context.containingProtocolOrNull() ?: return IScope.NULLSCOPE + val containingSubtermOrNull = context.closestAncestorOrNull() + + // Own & inherited class members + val members = containingClass.classMembersOrEmpty().filterIsInstance() + val superTypeMembers = containingClass.superClassMembers() + .filterIsInstance() + .toList() + + val resultScope = Scopes.scopeFor(members, Scopes.scopeFor(superTypeMembers)) + + // Subterms + return Scopes.scopeFor(containingProtocol.subtermsUpTo(containingSubtermOrNull), resultScope) + } + + private fun SmlProtocol.subtermsUpTo(containingSubtermOrNull: SmlProtocolSubterm?): List { + if (containingSubtermOrNull == null) { + return this.subtermsOrEmpty() + } + + return this.subtermsOrEmpty().takeWhile { it !== containingSubtermOrNull } + } + + private fun scopeForTypeArgumentTypeParameter(smlTypeArgument: SmlTypeArgument): IScope { + val typeParameters = smlTypeArgument + .closestAncestorOrNull() + ?.typeParametersOrNull() + ?: emptyList() + + return Scopes.scopeFor(typeParameters) + } + + private fun scopeForTypeParameterConstraintLeftOperand(smlTypeParameterConstraint: SmlTypeParameterConstraint): IScope { + val typeParameters = smlTypeParameterConstraint + .closestAncestorOrNull() + ?.typeParametersOrNull() + ?: emptyList() + + return Scopes.scopeFor(typeParameters) + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/CommentAdapters.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/CommentAdapters.kt new file mode 100644 index 000000000..821aea393 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/CommentAdapters.kt @@ -0,0 +1,37 @@ +package de.unibonn.simpleml.serializer + +import org.eclipse.emf.common.notify.impl.AdapterImpl +import org.eclipse.emf.ecore.EObject + +/** + * Stores a comment that is attached to an [EObject]. + */ +sealed class CommentAdapter(val text: String) : AdapterImpl() + +/** + * Stores a single-line comment that is attached to an [EObject]. + */ +class SingleLineCommentAdapter(text: String) : CommentAdapter(text) { + override fun toString(): String { + return "// $text" + } +} + +/** + * Stores a multi-line comment that is attached to an [EObject]. + */ +class MultiLineCommentAdapter(text: String) : CommentAdapter(text) { + override fun toString(): String { + return if (text.lines().size == 1) { + "/* $text */" + } else { + buildString { + appendLine("/*") + text.lineSequence().forEach { + appendLine(" * $it") + } + appendLine(" */") + } + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SerializerExtensions.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SerializerExtensions.kt new file mode 100644 index 000000000..424875030 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SerializerExtensions.kt @@ -0,0 +1,76 @@ +package de.unibonn.simpleml.serializer + +import com.google.inject.Inject +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.xtext.resource.SaveOptions +import org.eclipse.xtext.serializer.impl.Serializer + +internal object SerializerExtensionsInjectionTarget { + + @Inject + lateinit var serializer: Serializer +} + +private val WithFormatting = SaveOptions.newBuilder().format().options + +/** + * Serializes a subtree of the EMF model and applies the formatter to it. This only works if the [EObject] is part of a + * [Resource]. + * + * @receiver The root of the subtree. + * @return A result object indicating success or failure. + */ +fun EObject.serializeToFormattedString(): SerializationResult { + if (this.eResource() == null) { + return SerializationResult.NotInResourceFailure + } + + return try { + val code = SerializerExtensionsInjectionTarget.serializer + .serialize(this, WithFormatting) + .trim() + .replace(System.lineSeparator(), "\n") + + SerializationResult.Success(code) + } catch (e: RuntimeException) { + SerializationResult.WrongEmfModelStructureFailure(e.message ?: "") + } +} + +/** + * Result of calling [serializeToFormattedString]. + */ +sealed interface SerializationResult { + + /** + * Serialization was successful. + * + * @param code The created DSL code. + */ + class Success(val code: String) : SerializationResult + + /** + * Something went wrong while serializing the [EObject]. + */ + sealed interface Failure : SerializationResult { + + /** + * A message that describes the failure. + */ + val message: String + } + + /** + * The [EObject] is not part of a [Resource] and cannot be serialized. + */ + object NotInResourceFailure : Failure { + override val message: String + get() = "The EObject is not part of a Resource and cannot be serialized." + } + + /** + * The EMF model is not configured correctly. + */ + class WrongEmfModelStructureFailure(override val message: String) : Failure +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SimpleMLCrossReferenceSerializer.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SimpleMLCrossReferenceSerializer.kt new file mode 100644 index 000000000..5176a55fc --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SimpleMLCrossReferenceSerializer.kt @@ -0,0 +1,35 @@ +package de.unibonn.simpleml.serializer + +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtext.CrossReference +import org.eclipse.xtext.nodemodel.INode +import org.eclipse.xtext.scoping.IScope +import org.eclipse.xtext.serializer.diagnostic.ISerializationDiagnostic +import org.eclipse.xtext.serializer.tokens.CrossReferenceSerializer + +class SimpleMLCrossReferenceSerializer : CrossReferenceSerializer() { + + override fun getCrossReferenceNameFromScope( + semanticObject: EObject, + crossref: CrossReference, + target: EObject, + scope: IScope, + errors: ISerializationDiagnostic.Acceptor? + ): String { + return when (target) { + is SmlAbstractDeclaration -> target.name + else -> super.getCrossReferenceNameFromScope(semanticObject, crossref, target, scope, errors) + } + } + + override fun isValid( + semanticObject: EObject, + crossref: CrossReference, + target: EObject, + node: INode, + errors: ISerializationDiagnostic.Acceptor? + ): Boolean { + return target is SmlAbstractDeclaration + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SimpleMLHiddenTokenSequencer.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SimpleMLHiddenTokenSequencer.kt new file mode 100644 index 000000000..435adfa4f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SimpleMLHiddenTokenSequencer.kt @@ -0,0 +1,27 @@ +package de.unibonn.simpleml.serializer + +import com.google.inject.Inject +import de.unibonn.simpleml.services.SimpleMLGrammarAccess +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtext.RuleCall +import org.eclipse.xtext.nodemodel.ICompositeNode +import org.eclipse.xtext.serializer.sequencer.HiddenTokenSequencer + +@Suppress("unused") +class SimpleMLHiddenTokenSequencer @Inject constructor( + private val grammarAccess: SimpleMLGrammarAccess +) : HiddenTokenSequencer() { + + override fun enterAssignedParserRuleCall(rc: RuleCall, semanticChild: EObject, node: ICompositeNode?): Boolean { + semanticChild.eAdapters() + .filterIsInstance() + .forEach { + val rule = when (it) { + is SingleLineCommentAdapter -> grammarAccess.sL_COMMENTRule + is MultiLineCommentAdapter -> grammarAccess.mL_COMMENTRule + } + delegate.acceptComment(rule, it.toString(), null) + } + return super.enterAssignedParserRuleCall(rc, semanticChild, node) + } +} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SimpleMLSemanticSequencer.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SimpleMLSemanticSequencer.kt new file mode 100644 index 000000000..b028d20db --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SimpleMLSemanticSequencer.kt @@ -0,0 +1,3 @@ +package de.unibonn.simpleml.serializer + +internal class SimpleMLSemanticSequencer : AbstractSimpleMLSemanticSequencer() \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SimpleMLSyntacticSequencer.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SimpleMLSyntacticSequencer.kt new file mode 100644 index 000000000..c40a1b13e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/serializer/SimpleMLSyntacticSequencer.kt @@ -0,0 +1,3 @@ +package de.unibonn.simpleml.serializer + +class SimpleMLSyntacticSequencer: AbstractSimpleMLSyntacticSequencer() \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/AssigneeToValue.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/AssigneeToValue.kt new file mode 100644 index 000000000..4557bfcc5 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/AssigneeToValue.kt @@ -0,0 +1,50 @@ +package de.unibonn.simpleml.staticAnalysis + +import de.unibonn.simpleml.emf.assigneesOrEmpty +import de.unibonn.simpleml.emf.closestAncestorOrNull +import de.unibonn.simpleml.simpleML.SmlAbstractAssignee +import de.unibonn.simpleml.simpleML.SmlAbstractExpression +import de.unibonn.simpleml.simpleML.SmlAbstractObject +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlCall + +fun SmlAbstractAssignee.assignedOrNull(): SmlAbstractObject? { + return when (val maybeAssigned = this.maybeAssigned()) { + is AssignedResult.Assigned -> maybeAssigned.assigned + else -> null + } +} + +sealed interface AssignedResult { + object Unresolved : AssignedResult + object NotAssigned : AssignedResult + sealed class Assigned : AssignedResult { + abstract val assigned: SmlAbstractObject + } + + class AssignedExpression(override val assigned: SmlAbstractExpression) : Assigned() + class AssignedDeclaration(override val assigned: SmlAbstractObject) : Assigned() +} + +fun SmlAbstractAssignee.maybeAssigned(): AssignedResult { + val assignment = this.closestAncestorOrNull() ?: return AssignedResult.Unresolved + val expression = assignment.expression ?: return AssignedResult.NotAssigned + + val thisIndex = assignment.assigneeList.assignees.indexOf(this) + return when (expression) { + is SmlCall -> { + val results = expression.resultsOrNull() ?: return AssignedResult.Unresolved + val result = results.getOrNull(thisIndex) ?: return AssignedResult.NotAssigned + AssignedResult.AssignedDeclaration(result) + } + else -> when (thisIndex) { + 0 -> AssignedResult.AssignedExpression(expression) + else -> AssignedResult.NotAssigned + } + } +} + +fun SmlAbstractAssignee.indexOrNull(): Int? { + val assignment = closestAncestorOrNull() ?: return null + return assignment.assigneesOrEmpty().indexOf(this) +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/CallToCallable.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/CallToCallable.kt new file mode 100644 index 000000000..4ca22e5cc --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/CallToCallable.kt @@ -0,0 +1,111 @@ +package de.unibonn.simpleml.staticAnalysis + +import de.unibonn.simpleml.emf.blockLambdaResultsOrEmpty +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.resultsOrEmpty +import de.unibonn.simpleml.simpleML.SmlAbstractAssignee +import de.unibonn.simpleml.simpleML.SmlAbstractCallable +import de.unibonn.simpleml.simpleML.SmlAbstractExpression +import de.unibonn.simpleml.simpleML.SmlAbstractObject +import de.unibonn.simpleml.simpleML.SmlBlockLambda +import de.unibonn.simpleml.simpleML.SmlBlockLambdaResult +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlCallableType +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlExpressionLambda +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlMemberAccess +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlParenthesizedExpression +import de.unibonn.simpleml.simpleML.SmlReference +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlStep +import org.eclipse.emf.ecore.EObject + +fun SmlCall.callableOrNull(): SmlAbstractCallable? { + return when (val maybeCallable = this.maybeCallable()) { + is CallableResult.Callable -> maybeCallable.callable + else -> null + } +} + +sealed interface CallableResult { + object Unresolvable : CallableResult + object NotCallable : CallableResult + class Callable(val callable: SmlAbstractCallable) : CallableResult +} + +fun SmlCall.maybeCallable(): CallableResult { + val visited = mutableSetOf() + var current: EObject? = this.receiver + while (current != null && current !in visited) { + visited += current + + current = when { + current.eIsProxy() -> return CallableResult.Unresolvable + current is SmlAbstractCallable -> return CallableResult.Callable(current) + current is SmlCall -> { + val results = current.resultsOrNull() + if (results == null || results.size != 1) { + return CallableResult.Unresolvable + } + + results.first() + } + current is SmlAbstractAssignee -> current.assignedOrNull() + current is SmlMemberAccess -> current.member.declaration + current is SmlParameter -> return when (val typeOrNull = current.type) { + null -> CallableResult.Unresolvable + is SmlCallableType -> CallableResult.Callable(typeOrNull) + else -> CallableResult.NotCallable + } + current is SmlParenthesizedExpression -> current.expression + current is SmlReference -> current.declaration + current is SmlResult -> return when (val typeOrNull = current.type) { + null -> CallableResult.Unresolvable + is SmlCallableType -> CallableResult.Callable(typeOrNull) + else -> CallableResult.NotCallable + } + else -> return CallableResult.NotCallable + } + } + + return CallableResult.Unresolvable +} + +/** + * Returns the list of [SmlParameter]s of the called callable or `null` if it cannot be resolved. + */ +fun SmlCall.parametersOrNull(): List? { + return callableOrNull()?.parametersOrEmpty() +} + +/** + * Returns the list of [SmlAbstractObject]s that are returned by the called callable or `null` if it cannot be resolved. + * Possible types depend on the called callable: + * - [SmlBlockLambda] -> [SmlBlockLambdaResult] + * - [SmlCallableType] -> [SmlResult] + * - [SmlClass] -> [SmlClass] + * - [SmlEnumVariant] -> [SmlEnumVariant] + * - [SmlExpressionLambda] -> [SmlAbstractExpression] + * - [SmlFunction] -> [SmlResult] + * - [SmlStep] -> [SmlResult] + */ +fun SmlCall.resultsOrNull(): List? { + return when (val callable = this.callableOrNull()) { + is SmlBlockLambda -> callable.blockLambdaResultsOrEmpty() + is SmlCallableType -> callable.resultsOrEmpty() + is SmlClass -> listOf(callable) + is SmlEnumVariant -> listOf(callable) + is SmlExpressionLambda -> listOf(callable.result) + is SmlFunction -> callable.resultsOrEmpty() + is SmlStep -> callable.resultsOrEmpty() + else -> null + } +} + +sealed interface ResultsResult { + object Unresolved : ResultsResult + object NotCallable : ResultsResult +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/LocalDeclarationToReferences.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/LocalDeclarationToReferences.kt new file mode 100644 index 000000000..88932948f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/LocalDeclarationToReferences.kt @@ -0,0 +1,25 @@ +package de.unibonn.simpleml.staticAnalysis + +import de.unibonn.simpleml.emf.descendants +import de.unibonn.simpleml.emf.placeholdersOrEmpty +import de.unibonn.simpleml.simpleML.SmlAbstractObject +import de.unibonn.simpleml.simpleML.SmlAbstractStatement +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlReference + +fun SmlParameter.usesIn(obj: SmlAbstractObject): Sequence { + return obj + .descendants() + .filter { it.declaration == this } +} + +fun SmlPlaceholder.usesIn(obj: SmlAbstractObject): Sequence { + return obj + .descendants() + .dropWhile { it !is SmlAssignment || this !in it.placeholdersOrEmpty() } + .drop(1) + .flatMap { it.descendants() } + .filter { it.declaration == this } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/Recursion.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/Recursion.kt new file mode 100644 index 000000000..799ae257a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/Recursion.kt @@ -0,0 +1,34 @@ +package de.unibonn.simpleml.staticAnalysis + +import de.unibonn.simpleml.emf.containingCallableOrNull +import de.unibonn.simpleml.emf.immediateCalls +import de.unibonn.simpleml.simpleML.SmlAbstractCallable +import de.unibonn.simpleml.simpleML.SmlCall + +/** + * Returns whether this call might lead to recursion. + */ +fun SmlCall.isRecursive(): Boolean { + val visited = buildSet { + val containingCallable = containingCallableOrNull() + if (containingCallable != null) { + add(containingCallable) + } + } + + return isRecursive(visited) +} + +/** + * Returns whether this call might lead to recursion. + */ +private fun SmlCall.isRecursive(visited: Set): Boolean { + return when (val callable = this.callableOrNull()) { + is SmlAbstractCallable -> { + callable in visited || callable.immediateCalls().any { + it.isRecursive(visited + callable) + } + } + else -> false + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/SideEffects.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/SideEffects.kt new file mode 100644 index 000000000..33da04e22 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/SideEffects.kt @@ -0,0 +1,73 @@ +package de.unibonn.simpleml.staticAnalysis + +import de.unibonn.simpleml.emf.assigneesOrEmpty +import de.unibonn.simpleml.emf.immediateCalls +import de.unibonn.simpleml.simpleML.SmlAbstractCallable +import de.unibonn.simpleml.simpleML.SmlAbstractExpression +import de.unibonn.simpleml.simpleML.SmlAbstractLambda +import de.unibonn.simpleml.simpleML.SmlAbstractStatement +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlCallableType +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlExpressionStatement +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlWildcard +import de.unibonn.simpleml.stdlibAccess.hasNoSideEffects + +/** + * Whether this [SmlAbstractStatement] has no side effects and, thus, can be removed. + * + * @param resultIfUnknown What to return if neither purity nor impurity can be proven. Note that external functions are + * still always assumed to have side effects unless they are marked with `@Pure` or `@NoSideEffects. + */ +fun SmlAbstractStatement.statementHasNoSideEffects(resultIfUnknown: Boolean = false): Boolean { + return when (this) { + is SmlAssignment -> { + assigneesOrEmpty().all { it is SmlWildcard } && expression.expressionHasNoSideEffects(resultIfUnknown) + } + is SmlExpressionStatement -> { + expression.expressionHasNoSideEffects(resultIfUnknown) + } + else -> { + throw IllegalArgumentException("Missing case to handle statement $this.") + } + } +} + +/** + * Whether this [SmlAbstractExpression] has no side effects and, thus, can be removed. + * + * @param resultIfUnknown What to return if neither purity nor impurity can be proven. Note that external functions are + * still always assumed to have side effects unless they are marked with `@Pure` or `@NoSideEffects. + */ +fun SmlAbstractExpression.expressionHasNoSideEffects(resultIfUnknown: Boolean = false): Boolean { + return when (this) { + is SmlCall -> !isRecursive() && callableOrNull().callableHasNoSideEffects(resultIfUnknown) + else -> true + } +} + +/** + * Whether this [SmlAbstractCallable] has no side effects, so calls to this can be removed. + * + * @param resultIfUnknown What to return if neither purity nor impurity can be proven. Note that external functions are + * still always assumed to have side effects unless they are marked with `@Pure` or `@NoSideEffects. + */ +fun SmlAbstractCallable?.callableHasNoSideEffects(resultIfUnknown: Boolean = false): Boolean { + return when (this) { + null -> resultIfUnknown + + is SmlAbstractLambda -> immediateCalls().all { it.expressionHasNoSideEffects(resultIfUnknown) } + is SmlStep -> immediateCalls().all { it.expressionHasNoSideEffects(resultIfUnknown) } + + is SmlCallableType -> resultIfUnknown + is SmlClass -> true + is SmlEnumVariant -> true + is SmlFunction -> hasNoSideEffects() + + else -> throw IllegalArgumentException("Cannot handle callable of type '${this::class.simpleName}'.") + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/classHierarchy/ClassHierarchy.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/classHierarchy/ClassHierarchy.kt new file mode 100644 index 000000000..2e2ef6cda --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/classHierarchy/ClassHierarchy.kt @@ -0,0 +1,64 @@ +package de.unibonn.simpleml.staticAnalysis.classHierarchy + +import de.unibonn.simpleml.emf.classMembersOrEmpty +import de.unibonn.simpleml.emf.closestAncestorOrNull +import de.unibonn.simpleml.emf.parentTypesOrEmpty +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.staticAnalysis.typing.ClassType +import de.unibonn.simpleml.staticAnalysis.typing.type +import de.unibonn.simpleml.stdlibAccess.StdlibClasses +import de.unibonn.simpleml.stdlibAccess.getStdlibClassOrNull +import de.unibonn.simpleml.utils.uniqueOrNull + +fun SmlClass.isSubtypeOf(other: SmlClass) = + this == this.getStdlibClassOrNull(StdlibClasses.Nothing) || + this == other || other in superClasses() + +fun SmlClass.superClasses() = sequence { + val visited = mutableSetOf() + + // TODO: multiple parent classes + var current = parentClassOrNull() + while (current != null && current !in visited) { + yield(current) + visited += current + current = current.parentClassOrNull() + } + + val anyClass = this@superClasses.getStdlibClassOrNull(StdlibClasses.Any) + if (anyClass != null && this@superClasses != anyClass && visited.lastOrNull() != anyClass) { + yield(anyClass) + } +} + +fun SmlClass.superClassMembers() = + this.superClasses().flatMap { it.classMembersOrEmpty().asSequence() } + +// TODO only static methods can be hidden +fun SmlFunction.hiddenFunction(): SmlFunction? { + val containingClassOrInterface = closestAncestorOrNull() ?: return null + return containingClassOrInterface.superClassMembers() + .filterIsInstance() + .firstOrNull { it.name == name } +} + +fun SmlClass?.inheritedNonStaticMembersOrEmpty(): Set { + return this?.parentClassesOrEmpty() + ?.flatMap { it.classMembersOrEmpty() } + ?.filter { it is SmlAttribute && !it.isStatic || it is SmlFunction && !it.isStatic } + ?.toSet() + .orEmpty() +} + +fun SmlClass?.parentClassesOrEmpty(): List { + return this.parentTypesOrEmpty().mapNotNull { + (it.type() as? ClassType)?.smlClass + } +} + +fun SmlClass?.parentClassOrNull(): SmlClass? { + return this.parentClassesOrEmpty().uniqueOrNull() +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/linking/ArgumentToParameter.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/linking/ArgumentToParameter.kt new file mode 100644 index 000000000..9fdfc0f0b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/linking/ArgumentToParameter.kt @@ -0,0 +1,47 @@ +package de.unibonn.simpleml.staticAnalysis.linking + +import de.unibonn.simpleml.emf.asResolvedOrNull +import de.unibonn.simpleml.emf.closestAncestorOrNull +import de.unibonn.simpleml.emf.isNamed +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.simpleML.SmlAnnotationCall +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlArgumentList +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.staticAnalysis.parametersOrNull + +/** + * Returns the [SmlParameter] that corresponds to this [SmlArgument] or `null` if it cannot be resolved. + */ +fun SmlArgument.parameterOrNull(): SmlParameter? { + return when { + isNamed() -> parameter.asResolvedOrNull() + else -> { + val argumentList = closestAncestorOrNull() ?: return null + val parameters = argumentList.parametersOrNull() ?: return null + val lastParameter = parameters.lastOrNull() + + val firstNamedArgumentIndex = argumentList.arguments.indexOfFirst { it.isNamed() } + val thisIndex = argumentList.arguments.indexOf(this) + + return when { + lastParameter?.isVariadic == true && thisIndex >= parameters.size - 1 -> lastParameter + firstNamedArgumentIndex != -1 && thisIndex > firstNamedArgumentIndex -> null + else -> parameters.getOrNull(thisIndex) + } + } + } +} + +/** + * Returns the list of [SmlParameter]s that corresponds to this list of [SmlArgument]s or `null` if it cannot be + * resolved. + */ +fun SmlArgumentList.parametersOrNull(): List? { + return when (val parent = this.eContainer()) { + is SmlAnnotationCall -> parent.annotation.asResolvedOrNull()?.parametersOrEmpty() + is SmlCall -> parent.parametersOrNull() + else -> null + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/linking/ResultToYield.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/linking/ResultToYield.kt new file mode 100644 index 000000000..ea11001ac --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/linking/ResultToYield.kt @@ -0,0 +1,29 @@ +package de.unibonn.simpleml.staticAnalysis.linking + +import de.unibonn.simpleml.emf.closestAncestorOrNull +import de.unibonn.simpleml.emf.yieldsOrEmpty +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlResultList +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlYield +import de.unibonn.simpleml.utils.uniqueOrNull + +/** + * Returns the unique [SmlYield] that corresponds to this [SmlResult] or `null` if no or multiple [SmlYield]s exist. + * Note that an [SmlYield] can only be used inside an [SmlStep], so this will always return `null` for [SmlResult]s that + * are not inside an [SmlStep]. + */ +fun SmlResult.uniqueYieldOrNull(): SmlYield? { + return yieldsOrEmpty().uniqueOrNull() +} + +/** + * Returns all [SmlYield]s that corresponds to this [SmlResult]. Note that an [SmlYield] can only be used inside an + * [SmlStep], so this will always return an empty list for [SmlResult]s that are not inside an [SmlStep]. + */ +fun SmlResult.yieldsOrEmpty(): List { + val resultList = closestAncestorOrNull() ?: return emptyList() + val step = resultList.eContainer() as? SmlStep ?: return emptyList() + + return step.yieldsOrEmpty().filter { it.result == this } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/linking/TypeArgumentToTypeParameter.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/linking/TypeArgumentToTypeParameter.kt new file mode 100644 index 000000000..2799d54c7 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/linking/TypeArgumentToTypeParameter.kt @@ -0,0 +1,63 @@ +package de.unibonn.simpleml.staticAnalysis.linking + +import de.unibonn.simpleml.emf.closestAncestorOrNull +import de.unibonn.simpleml.emf.isNamed +import de.unibonn.simpleml.emf.isResolved +import de.unibonn.simpleml.emf.typeParametersOrEmpty +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlNamedType +import de.unibonn.simpleml.simpleML.SmlTypeArgument +import de.unibonn.simpleml.simpleML.SmlTypeArgumentList +import de.unibonn.simpleml.simpleML.SmlTypeParameter +import de.unibonn.simpleml.staticAnalysis.callableOrNull + +/** + * Returns the [SmlTypeParameter] that corresponds to this [SmlTypeArgument] or `null` if it cannot be resolved. + */ +fun SmlTypeArgument.typeParameterOrNull(): SmlTypeParameter? { + return when { + this.isNamed() -> typeParameter + else -> { + val typeArgumentList = closestAncestorOrNull() ?: return null + + // Cannot match positional type argument if it is preceded by named type arguments + val firstNamedTypeArgumentIndex = typeArgumentList.typeArguments.indexOfFirst { it.isNamed() } + val thisIndex = typeArgumentList.typeArguments.indexOf(this) + if (firstNamedTypeArgumentIndex != -1 && thisIndex > firstNamedTypeArgumentIndex) { + return null + } + + typeArgumentList.typeParametersOrNull()?.getOrNull(thisIndex) + } + } +} + +/** + * Returns the list of [SmlTypeParameter]s that corresponds to this list of [SmlTypeArgument]s or `null` if it cannot + * not be resolved. + */ +fun SmlTypeArgumentList.typeParametersOrNull(): List? { + return when (val parent = eContainer()) { + is SmlCall -> { + when (val callable = parent.callableOrNull()) { + is SmlClass -> callable.typeParametersOrEmpty() + is SmlEnumVariant -> callable.typeParametersOrEmpty() + is SmlFunction -> callable.typeParametersOrEmpty() + else -> null + } + } + is SmlNamedType -> { + val declaration = parent.declaration + when { + !declaration.isResolved() -> null + declaration is SmlClass -> declaration.typeParametersOrEmpty() + declaration is SmlEnumVariant -> declaration.typeParametersOrEmpty() + else -> null + } + } + else -> null + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/partialEvaluation/SmlSimplifiedExpression.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/partialEvaluation/SmlSimplifiedExpression.kt new file mode 100644 index 000000000..732147cbf --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/partialEvaluation/SmlSimplifiedExpression.kt @@ -0,0 +1,111 @@ +package de.unibonn.simpleml.staticAnalysis.partialEvaluation + +import de.unibonn.simpleml.simpleML.SmlAbstractExpression +import de.unibonn.simpleml.simpleML.SmlAbstractResult +import de.unibonn.simpleml.simpleML.SmlBlockLambdaResult +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlReference +import de.unibonn.simpleml.simpleML.SmlResult + +typealias ParameterSubstitutions = Map + +sealed interface SmlSimplifiedExpression + +internal sealed interface SmlIntermediateExpression : SmlSimplifiedExpression + +internal sealed interface SmlIntermediateCallable : SmlIntermediateExpression { + val parameters: List +} + +internal data class SmlIntermediateBlockLambda( + override val parameters: List, + val results: List, + val substitutionsOnCreation: ParameterSubstitutions +) : SmlIntermediateCallable + +internal data class SmlIntermediateExpressionLambda( + override val parameters: List, + val result: SmlAbstractExpression, + val substitutionsOnCreation: ParameterSubstitutions +) : SmlIntermediateCallable + +internal data class SmlIntermediateStep( + override val parameters: List, + val results: List +) : SmlIntermediateCallable + +internal class SmlIntermediateRecord( + resultSubstitutions: List> +) : SmlIntermediateExpression { + private val resultSubstitutions = resultSubstitutions.toMap() + + fun getSubstitutionByReferenceOrNull(reference: SmlReference): SmlSimplifiedExpression? { + val result = reference.declaration as? SmlAbstractResult ?: return null + return resultSubstitutions[result] + } + + fun getSubstitutionByIndexOrNull(index: Int?): SmlSimplifiedExpression? { + if (index == null) { + return null + } + return resultSubstitutions.values.toList().getOrNull(index) + } + + /** + * If the record contains exactly one substitution its value is returned. Otherwise, it returns `this`. + */ + fun unwrap(): SmlSimplifiedExpression? { + return when (resultSubstitutions.size) { + 1 -> resultSubstitutions.values.first() + else -> this + } + } + + override fun toString(): String { + return resultSubstitutions.entries.joinToString(prefix = "{", postfix = "}") { (result, value) -> + "${result.name}=$value" + } + } +} + +data class SmlIntermediateVariadicArguments( + private val arguments: List +) : SmlSimplifiedExpression { + fun getArgumentByIndexOrNull(index: Int?): SmlSimplifiedExpression? { + if (index == null) { + return null + } + return arguments.getOrNull(index) + } +} + +sealed interface SmlConstantExpression : SmlSimplifiedExpression + +data class SmlConstantBoolean(val value: Boolean) : SmlConstantExpression { + override fun toString(): String = value.toString() +} + +data class SmlConstantEnumVariant(val value: SmlEnumVariant) : SmlConstantExpression { + override fun toString(): String = value.name +} + +sealed class SmlConstantNumber : SmlConstantExpression { + abstract val value: Number +} + +data class SmlConstantFloat(override val value: Double) : SmlConstantNumber() { + override fun toString(): String = value.toString() +} + +data class SmlConstantInt(override val value: Int) : SmlConstantNumber() { + override fun toString(): String = value.toString() +} + +object SmlConstantNull : SmlConstantExpression { + override fun toString(): String = "null" +} + +data class SmlConstantString(val value: String) : SmlConstantExpression { + override fun toString(): String = value +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/partialEvaluation/ToConstantExpression.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/partialEvaluation/ToConstantExpression.kt new file mode 100644 index 000000000..622151fc8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/partialEvaluation/ToConstantExpression.kt @@ -0,0 +1,409 @@ +package de.unibonn.simpleml.staticAnalysis.partialEvaluation + +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.And +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.By +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.Elvis +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.Equals +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.GreaterThan +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.GreaterThanOrEquals +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.IdenticalTo +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.LessThan +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.LessThanOrEquals +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.NotEquals +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.NotIdenticalTo +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.Or +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.Plus +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.Times +import de.unibonn.simpleml.constant.SmlPrefixOperationOperator.Not +import de.unibonn.simpleml.constant.operator +import de.unibonn.simpleml.emf.argumentsOrEmpty +import de.unibonn.simpleml.emf.blockLambdaResultsOrEmpty +import de.unibonn.simpleml.emf.closestAncestorOrNull +import de.unibonn.simpleml.emf.isOptional +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.resultsOrEmpty +import de.unibonn.simpleml.simpleML.SmlAbstractAssignee +import de.unibonn.simpleml.simpleML.SmlAbstractExpression +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlBlockLambda +import de.unibonn.simpleml.simpleML.SmlBoolean +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlExpressionLambda +import de.unibonn.simpleml.simpleML.SmlFloat +import de.unibonn.simpleml.simpleML.SmlIndexedAccess +import de.unibonn.simpleml.simpleML.SmlInfixOperation +import de.unibonn.simpleml.simpleML.SmlInt +import de.unibonn.simpleml.simpleML.SmlMemberAccess +import de.unibonn.simpleml.simpleML.SmlNull +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlParenthesizedExpression +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlPrefixOperation +import de.unibonn.simpleml.simpleML.SmlReference +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlString +import de.unibonn.simpleml.simpleML.SmlTemplateString +import de.unibonn.simpleml.simpleML.SmlTemplateStringEnd +import de.unibonn.simpleml.simpleML.SmlTemplateStringInner +import de.unibonn.simpleml.simpleML.SmlTemplateStringStart +import de.unibonn.simpleml.staticAnalysis.callableHasNoSideEffects +import de.unibonn.simpleml.staticAnalysis.indexOrNull +import de.unibonn.simpleml.staticAnalysis.linking.parameterOrNull +import de.unibonn.simpleml.staticAnalysis.linking.uniqueYieldOrNull +import de.unibonn.simpleml.utils.uniqueOrNull +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.Minus as InfixMinus +import de.unibonn.simpleml.constant.SmlPrefixOperationOperator.Minus as PrefixMinus + +/** + * Tries to evaluate this expression. On success a [SmlConstantExpression] is returned, otherwise `null`. + */ +fun SmlAbstractExpression.toConstantExpressionOrNull(): SmlConstantExpression? { + return toConstantExpressionOrNull(emptyMap()) +} + +internal fun SmlAbstractExpression.toConstantExpressionOrNull(substitutions: ParameterSubstitutions): SmlConstantExpression? { + return when (val simplifiedExpression = simplify(substitutions)) { + is SmlConstantExpression? -> simplifiedExpression + is SmlIntermediateRecord -> simplifiedExpression.unwrap() as? SmlConstantExpression + else -> null + } +} + +internal fun SmlAbstractExpression.simplify(substitutions: ParameterSubstitutions): SmlSimplifiedExpression? { + return when (this) { + + // Base cases + is SmlBoolean -> SmlConstantBoolean(isTrue) + is SmlFloat -> SmlConstantFloat(value) + is SmlInt -> SmlConstantInt(value) + is SmlNull -> SmlConstantNull + is SmlString -> SmlConstantString(value) + is SmlTemplateStringStart -> SmlConstantString(value) + is SmlTemplateStringInner -> SmlConstantString(value) + is SmlTemplateStringEnd -> SmlConstantString(value) + is SmlBlockLambda -> simplifyBlockLambda(substitutions) + is SmlExpressionLambda -> simplifyExpressionLambda(substitutions) + + // Simple recursive cases + is SmlArgument -> value.simplify(substitutions) + is SmlInfixOperation -> simplifyInfixOp(substitutions) + is SmlParenthesizedExpression -> expression.simplify(substitutions) + is SmlPrefixOperation -> simplifyPrefixOp(substitutions) + is SmlTemplateString -> simplifyTemplateString(substitutions) + + // Complex recursive cases + is SmlCall -> simplifyCall(substitutions) + is SmlIndexedAccess -> simplifyIndexedAccess(substitutions) + is SmlMemberAccess -> simplifyMemberAccess(substitutions) + is SmlReference -> simplifyReference(substitutions) + + // Warn if case is missing + else -> throw IllegalArgumentException("Missing case to handle $this.") + } +} + +private fun SmlBlockLambda.simplifyBlockLambda(substitutions: ParameterSubstitutions): SmlIntermediateBlockLambda? { + return when { + callableHasNoSideEffects(resultIfUnknown = true) -> SmlIntermediateBlockLambda( + parameters = parametersOrEmpty(), + results = blockLambdaResultsOrEmpty(), + substitutionsOnCreation = substitutions + ) + else -> null + } +} + +private fun SmlExpressionLambda.simplifyExpressionLambda( + substitutions: ParameterSubstitutions +): SmlIntermediateExpressionLambda? { + return when { + callableHasNoSideEffects(resultIfUnknown = true) -> SmlIntermediateExpressionLambda( + parameters = parametersOrEmpty(), + result = result, + substitutionsOnCreation = substitutions + ) + else -> null + } +} + +private fun SmlInfixOperation.simplifyInfixOp(substitutions: ParameterSubstitutions): SmlConstantExpression? { + + // By design none of the operators are short-circuited + val constantLeft = leftOperand.toConstantExpressionOrNull(substitutions) ?: return null + val constantRight = rightOperand.toConstantExpressionOrNull(substitutions) ?: return null + + return when (operator()) { + Or -> simplifyLogicalOp(constantLeft, Boolean::or, constantRight) + And -> simplifyLogicalOp(constantLeft, Boolean::and, constantRight) + Equals -> SmlConstantBoolean(constantLeft == constantRight) + NotEquals -> SmlConstantBoolean(constantLeft != constantRight) + IdenticalTo -> SmlConstantBoolean(constantLeft == constantRight) + NotIdenticalTo -> SmlConstantBoolean(constantLeft != constantRight) + LessThan -> simplifyComparisonOp( + constantLeft, + { a, b -> a < b }, + { a, b -> a < b }, + constantRight + ) + LessThanOrEquals -> simplifyComparisonOp( + constantLeft, + { a, b -> a <= b }, + { a, b -> a <= b }, + constantRight + ) + GreaterThanOrEquals -> simplifyComparisonOp( + constantLeft, + { a, b -> a >= b }, + { a, b -> a >= b }, + constantRight + ) + GreaterThan -> simplifyComparisonOp( + constantLeft, + { a, b -> a > b }, + { a, b -> a > b }, + constantRight + ) + Plus -> simplifyArithmeticOp( + constantLeft, + { a, b -> a + b }, + { a, b -> a + b }, + constantRight + ) + InfixMinus -> simplifyArithmeticOp( + constantLeft, + { a, b -> a - b }, + { a, b -> a - b }, + constantRight + ) + Times -> simplifyArithmeticOp( + constantLeft, + { a, b -> a * b }, + { a, b -> a * b }, + constantRight + ) + By -> { + if (constantRight == SmlConstantFloat(0.0) || constantRight == SmlConstantInt(0)) { + return null + } + + simplifyArithmeticOp( + constantLeft, + { a, b -> a / b }, + { a, b -> a / b }, + constantRight + ) + } + Elvis -> when (constantLeft) { + SmlConstantNull -> constantRight + else -> constantLeft + } + } +} + +private fun simplifyLogicalOp( + leftOperand: SmlConstantExpression, + operation: (Boolean, Boolean) -> Boolean, + rightOperand: SmlConstantExpression, +): SmlConstantExpression? { + + return when { + leftOperand is SmlConstantBoolean && rightOperand is SmlConstantBoolean -> { + SmlConstantBoolean(operation(leftOperand.value, rightOperand.value)) + } + else -> null + } +} + +private fun simplifyComparisonOp( + leftOperand: SmlConstantExpression, + doubleOperation: (Double, Double) -> Boolean, + intOperation: (Int, Int) -> Boolean, + rightOperand: SmlConstantExpression, +): SmlConstantExpression? { + + return when { + leftOperand is SmlConstantInt && rightOperand is SmlConstantInt -> { + SmlConstantBoolean(intOperation(leftOperand.value, rightOperand.value)) + } + leftOperand is SmlConstantNumber && rightOperand is SmlConstantNumber -> { + SmlConstantBoolean(doubleOperation(leftOperand.value.toDouble(), rightOperand.value.toDouble())) + } + else -> null + } +} + +private fun simplifyArithmeticOp( + leftOperand: SmlConstantExpression, + doubleOperation: (Double, Double) -> Double, + intOperation: (Int, Int) -> Int, + rightOperand: SmlConstantExpression, +): SmlConstantExpression? { + + return when { + leftOperand is SmlConstantInt && rightOperand is SmlConstantInt -> { + SmlConstantInt(intOperation(leftOperand.value, rightOperand.value)) + } + leftOperand is SmlConstantNumber && rightOperand is SmlConstantNumber -> { + SmlConstantFloat(doubleOperation(leftOperand.value.toDouble(), rightOperand.value.toDouble())) + } + else -> null + } +} + +private fun SmlPrefixOperation.simplifyPrefixOp(substitutions: ParameterSubstitutions): SmlConstantExpression? { + val constantOperand = operand.toConstantExpressionOrNull(substitutions) ?: return null + + return when (operator()) { + Not -> when (constantOperand) { + is SmlConstantBoolean -> SmlConstantBoolean(!constantOperand.value) + else -> null + } + PrefixMinus -> when (constantOperand) { + is SmlConstantFloat -> SmlConstantFloat(-constantOperand.value) + is SmlConstantInt -> SmlConstantInt(-constantOperand.value) + else -> null + } + } +} + +private fun SmlTemplateString.simplifyTemplateString(substitutions: ParameterSubstitutions): SmlConstantExpression? { + val constExpressions = expressions.map { + it.toConstantExpressionOrNull(substitutions) ?: return null + } + + return SmlConstantString(constExpressions.joinToString("")) +} + +private fun SmlCall.simplifyCall(substitutions: ParameterSubstitutions): SmlSimplifiedExpression? { + val simpleReceiver = simplifyReceiver(substitutions) ?: return null + val newSubstitutions = buildNewSubstitutions(simpleReceiver, substitutions) + + return when (simpleReceiver) { + is SmlIntermediateBlockLambda -> { + SmlIntermediateRecord( + simpleReceiver.results.map { + it to it.simplifyAssignee(newSubstitutions) + } + ) + } + is SmlIntermediateExpressionLambda -> simpleReceiver.result.simplify(newSubstitutions) + is SmlIntermediateStep -> { + SmlIntermediateRecord( + simpleReceiver.results.map { + it to it.uniqueYieldOrNull()?.simplifyAssignee(newSubstitutions) + } + ) + } + } +} + +private fun SmlCall.simplifyReceiver(substitutions: ParameterSubstitutions): SmlIntermediateCallable? { + return when (val simpleReceiver = receiver.simplify(substitutions)) { + is SmlIntermediateRecord -> simpleReceiver.unwrap() as? SmlIntermediateCallable + is SmlIntermediateCallable -> simpleReceiver + else -> return null + } +} + +private fun SmlCall.buildNewSubstitutions( + simpleReceiver: SmlIntermediateCallable, + oldSubstitutions: ParameterSubstitutions +): ParameterSubstitutions { + + val substitutionsOnCreation = when (simpleReceiver) { + is SmlIntermediateBlockLambda -> simpleReceiver.substitutionsOnCreation + is SmlIntermediateExpressionLambda -> simpleReceiver.substitutionsOnCreation + else -> emptyMap() + } + + val substitutionsOnCall = argumentsOrEmpty() + .groupBy { it.parameterOrNull() } + .mapValues { (parameter, arguments) -> + when { + parameter == null -> null + parameter.isVariadic -> SmlIntermediateVariadicArguments( + arguments.map { it.simplify(oldSubstitutions) } + ) + else -> arguments.uniqueOrNull()?.simplify(oldSubstitutions) + } + } + + return buildMap { + putAll(substitutionsOnCreation) + substitutionsOnCall.entries.forEach { (parameter, argument) -> + if (parameter != null) { + put(parameter, argument) + } + } + } +} + +private fun SmlIndexedAccess.simplifyIndexedAccess(substitutions: ParameterSubstitutions): SmlSimplifiedExpression? { + val simpleReceiver = receiver.simplify(substitutions) as? SmlIntermediateVariadicArguments ?: return null + val simpleIndex = index.simplify(substitutions) as? SmlConstantInt ?: return null + + return simpleReceiver.getArgumentByIndexOrNull(simpleIndex.value) +} + +private fun SmlMemberAccess.simplifyMemberAccess(substitutions: ParameterSubstitutions): SmlSimplifiedExpression? { + if (member.declaration is SmlEnumVariant) { + return member.simplifyReference(substitutions) + } + + return when (val simpleReceiver = receiver.simplify(substitutions)) { + SmlConstantNull -> when { + isNullSafe -> SmlConstantNull + else -> null + } + is SmlIntermediateRecord -> simpleReceiver.getSubstitutionByReferenceOrNull(member) + else -> null + } +} + +private fun SmlReference.simplifyReference(substitutions: ParameterSubstitutions): SmlSimplifiedExpression? { + return when (val declaration = this.declaration) { + is SmlEnumVariant -> when { + declaration.parametersOrEmpty().isEmpty() -> SmlConstantEnumVariant(declaration) + else -> null + } + is SmlPlaceholder -> declaration.simplifyAssignee(substitutions) + is SmlParameter -> declaration.simplifyParameter(substitutions) + is SmlStep -> declaration.simplifyStep() + else -> null + } +} + +private fun SmlAbstractAssignee.simplifyAssignee(substitutions: ParameterSubstitutions): SmlSimplifiedExpression? { + val simpleFullAssignedExpression = closestAncestorOrNull() + ?.expression + ?.simplify(substitutions) + ?: return null + + return when (simpleFullAssignedExpression) { + is SmlIntermediateRecord -> simpleFullAssignedExpression.getSubstitutionByIndexOrNull(indexOrNull()) + else -> when { + indexOrNull() == 0 -> simpleFullAssignedExpression + else -> null + } + } +} + +private fun SmlParameter.simplifyParameter(substitutions: ParameterSubstitutions): SmlSimplifiedExpression? { + return when { + this in substitutions -> substitutions[this] + isOptional() -> defaultValue?.simplify(substitutions) + else -> null + } +} + +private fun SmlStep.simplifyStep(): SmlIntermediateStep? { + return when { + callableHasNoSideEffects(resultIfUnknown = true) -> SmlIntermediateStep( + parameters = parametersOrEmpty(), + results = resultsOrEmpty() + ) + else -> null + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/typing/TypeComputer.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/typing/TypeComputer.kt new file mode 100644 index 000000000..adae3618b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/typing/TypeComputer.kt @@ -0,0 +1,368 @@ +@file:Suppress("FunctionName") + +package de.unibonn.simpleml.staticAnalysis.typing + +import de.unibonn.simpleml.emf.blockLambdaResultsOrEmpty +import de.unibonn.simpleml.emf.closestAncestorOrNull +import de.unibonn.simpleml.emf.containingEnumOrNull +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.resultsOrEmpty +import de.unibonn.simpleml.emf.typeArgumentsOrEmpty +import de.unibonn.simpleml.emf.yieldsOrEmpty +import de.unibonn.simpleml.naming.qualifiedNameOrNull +import de.unibonn.simpleml.simpleML.SmlAbstractAssignee +import de.unibonn.simpleml.simpleML.SmlAbstractCallable +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlAbstractExpression +import de.unibonn.simpleml.simpleML.SmlAbstractLambda +import de.unibonn.simpleml.simpleML.SmlAbstractObject +import de.unibonn.simpleml.simpleML.SmlAbstractType +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.simpleML.SmlBlockLambda +import de.unibonn.simpleml.simpleML.SmlBlockLambdaResult +import de.unibonn.simpleml.simpleML.SmlBoolean +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlCallableType +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlExpressionLambda +import de.unibonn.simpleml.simpleML.SmlFloat +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlIndexedAccess +import de.unibonn.simpleml.simpleML.SmlInfixOperation +import de.unibonn.simpleml.simpleML.SmlInt +import de.unibonn.simpleml.simpleML.SmlMemberAccess +import de.unibonn.simpleml.simpleML.SmlMemberType +import de.unibonn.simpleml.simpleML.SmlNamedType +import de.unibonn.simpleml.simpleML.SmlNull +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlParenthesizedExpression +import de.unibonn.simpleml.simpleML.SmlParenthesizedType +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlPrefixOperation +import de.unibonn.simpleml.simpleML.SmlReference +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlString +import de.unibonn.simpleml.simpleML.SmlTemplateString +import de.unibonn.simpleml.simpleML.SmlTypeArgument +import de.unibonn.simpleml.simpleML.SmlTypeProjection +import de.unibonn.simpleml.simpleML.SmlUnionType +import de.unibonn.simpleml.simpleML.SmlYield +import de.unibonn.simpleml.staticAnalysis.assignedOrNull +import de.unibonn.simpleml.staticAnalysis.callableOrNull +import de.unibonn.simpleml.staticAnalysis.classHierarchy.superClasses +import de.unibonn.simpleml.staticAnalysis.linking.parameterOrNull +import de.unibonn.simpleml.stdlibAccess.StdlibClasses +import de.unibonn.simpleml.stdlibAccess.getStdlibClassOrNull +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtext.naming.QualifiedName + +fun SmlAbstractObject.type(): Type { + return inferType(this) +} + +fun SmlAbstractObject.hasPrimitiveType(): Boolean { + val type = type() + if (type !is ClassType) { + return false + } + + val qualifiedName = type.smlClass.qualifiedNameOrNull() + return qualifiedName in setOf( + StdlibClasses.Boolean, + StdlibClasses.Float, + StdlibClasses.Int, + StdlibClasses.String + ) +} + +private fun EObject.inferType(context: EObject): Type { + return when { + this.eIsProxy() -> UnresolvedType + this is SmlAbstractAssignee -> this.inferTypeForAssignee(context) + this is SmlAbstractDeclaration -> this.inferTypeForDeclaration(context) + this is SmlAbstractExpression -> this.inferTypeExpression(context) + this is SmlAbstractType -> this.inferTypeForType(context) + this is SmlTypeArgument -> this.value.inferType(context) + this is SmlTypeProjection -> this.type.inferTypeForType(context) + else -> Any(context) + } +} + +private fun SmlAbstractAssignee.inferTypeForAssignee(context: EObject): Type { + return when { + this.eIsProxy() -> UnresolvedType + this is SmlBlockLambdaResult || this is SmlPlaceholder || this is SmlYield -> { + val assigned = assignedOrNull() ?: return Nothing(context) + assigned.inferType(context) + } + else -> Any(context) + } +} + +private fun SmlAbstractDeclaration.inferTypeForDeclaration(context: EObject): Type { + return when { + this.eIsProxy() -> UnresolvedType + this is SmlAttribute -> type.inferTypeForType(context) + this is SmlClass -> ClassType(this, isNullable = false) + this is SmlEnum -> EnumType(this, isNullable = false) + this is SmlEnumVariant -> EnumVariantType(this, isNullable = false) + this is SmlFunction -> CallableType( + parametersOrEmpty().map { it.inferTypeForDeclaration(context) }, + resultsOrEmpty().map { it.inferTypeForDeclaration(context) } + ) + this is SmlParameter -> { + + // Declared parameter type + if (this.type != null) { + val declaredParameterType = this.type.inferTypeForType(context) + return when { + this.isVariadic -> VariadicType(declaredParameterType) + else -> declaredParameterType + } + } + + // Inferred lambda parameter type + val callable = this.closestAncestorOrNull() + val thisIndex = callable.parametersOrEmpty().indexOf(this) + if (callable is SmlAbstractLambda) { + val containerType = when (val container = callable.eContainer()) { + is SmlArgument -> container.parameterOrNull()?.inferType(context) + is SmlAssignment -> + container + .yieldsOrEmpty() + .find { it.assignedOrNull() == callable } + ?.result + ?.inferType(context) + else -> null + } + + return when (containerType) { + is CallableType -> containerType.parameters.getOrElse(thisIndex) { Any(context) } + else -> Any(context) + } + } + + // We don't know better + return Any(context) + } + this is SmlResult -> type.inferTypeForType(context) + this is SmlStep -> CallableType( + parametersOrEmpty().map { it.inferTypeForDeclaration(context) }, + resultsOrEmpty().map { it.inferTypeForDeclaration(context) } + ) + else -> Any(context) + } +} + +private fun SmlAbstractExpression.inferTypeExpression(context: EObject): Type { + return when { + + // Terminal cases + this.eIsProxy() -> UnresolvedType + this is SmlBoolean -> Boolean(context) + this is SmlFloat -> Float(context) + this is SmlInt -> Int(context) + this is SmlNull -> Nothing(context, isNullable = true) + this is SmlString -> String(context) + this is SmlTemplateString -> String(context) + + // Recursive cases + this is SmlArgument -> this.value.inferTypeExpression(context) + this is SmlBlockLambda -> CallableType( + this.parametersOrEmpty().map { it.inferTypeForDeclaration(context) }, + blockLambdaResultsOrEmpty().map { it.inferTypeForAssignee(context) } + ) + this is SmlCall -> when (val callable = callableOrNull()) { + is SmlClass -> ClassType(callable, isNullable = false) + is SmlCallableType -> { + val results = callable.resultsOrEmpty() + when (results.size) { + 1 -> results.first().inferTypeForDeclaration(context) + else -> RecordType(results.map { it.name to it.inferTypeForDeclaration(context) }) + } + } + is SmlFunction -> { + val results = callable.resultsOrEmpty() + when (results.size) { + 1 -> results.first().inferTypeForDeclaration(context) + else -> RecordType(results.map { it.name to it.inferTypeForDeclaration(context) }) + } + } + is SmlBlockLambda -> { + val results = callable.blockLambdaResultsOrEmpty() + when (results.size) { + 1 -> results.first().inferTypeForAssignee(context) + else -> RecordType(results.map { it.name to it.inferTypeForAssignee(context) }) + } + } + is SmlEnumVariant -> { + EnumVariantType(callable, isNullable = false) + } + is SmlExpressionLambda -> { + callable.result.inferTypeExpression(context) + } + is SmlStep -> { + val results = callable.resultsOrEmpty() + when (results.size) { + 1 -> results.first().inferTypeForDeclaration(context) + else -> RecordType(results.map { it.name to it.inferTypeForDeclaration(context) }) + } + } + else -> Any(context) + } + this is SmlExpressionLambda -> CallableType( + this.parametersOrEmpty().map { it.inferTypeForDeclaration(context) }, + listOf(result.inferTypeExpression(context)) + ) + this is SmlIndexedAccess -> { + when (val receiverType = this.receiver.inferTypeExpression(context)) { + is UnresolvedType -> UnresolvedType + is VariadicType -> receiverType.elementType + else -> Nothing(context) + } + } + this is SmlInfixOperation -> when (operator) { + "<", "<=", ">=", ">" -> Boolean(context) + "==", "!=" -> Boolean(context) + "===", "!==" -> Boolean(context) + "or", "and" -> Boolean(context) + "+", "-", "*", "/" -> when { + this.leftOperand.inferTypeExpression(context) == Int(context) && + this.rightOperand.inferTypeExpression(context) == Int(context) -> Int(context) + else -> Float(context) + } + "?:" -> { + val leftOperandType = this.leftOperand.inferTypeExpression(context) + if (leftOperandType.isNullable) { + lowestCommonSupertype( + context, + listOf( + leftOperandType.setIsNullableOnCopy(isNullable = false), + this.rightOperand.inferTypeExpression(context) + ) + ) + } else { + leftOperandType + } + } + else -> Nothing(context) + } + this is SmlMemberAccess -> { + val memberType = this.member.inferTypeExpression(context) + memberType.setIsNullableOnCopy(this.isNullSafe || memberType.isNullable) + } + this is SmlParenthesizedExpression -> this.expression.inferTypeExpression(context) + this is SmlPrefixOperation -> when (operator) { + "not" -> Boolean(context) + "-" -> when (this.operand.inferTypeExpression(context)) { + Int(context) -> Int(context) + else -> Float(context) + } + else -> Nothing(context) + } + this is SmlReference -> this.declaration.inferType(context) + else -> Any(context) + } +} + +private fun SmlAbstractType.inferTypeForType(context: EObject): Type { + return when { + this.eIsProxy() -> UnresolvedType + this is SmlCallableType -> CallableType( + this.parametersOrEmpty().map { it.inferTypeForDeclaration(context) }, + this.resultsOrEmpty().map { it.inferTypeForDeclaration(context) } + ) + this is SmlMemberType -> { + this.member.inferTypeForType(context) + } + this is SmlNamedType -> { + this.declaration.inferTypeForDeclaration(context).setIsNullableOnCopy(this.isNullable) + } + this is SmlParenthesizedType -> { + this.type.inferTypeForType(context) + } + this is SmlUnionType -> { + UnionType(this.typeArgumentsOrEmpty().map { it.value.inferType(context) }.toSet()) + } + else -> Any(context) + } +} + +private fun lowestCommonSupertype(context: EObject, types: List): Type { + if (types.isEmpty()) { + return Nothing(context) + } + + val unwrappedTypes = unwrapUnionTypes(types) + val isNullable = unwrappedTypes.any { it.isNullable } + var candidate = unwrappedTypes.first().setIsNullableOnCopy(isNullable) + + while (!isLowestCommonSupertype(candidate, unwrappedTypes)) { + candidate = when (candidate) { + is CallableType -> Any(context, candidate.isNullable) + is ClassType -> { + val superClass = candidate.smlClass.superClasses().firstOrNull() + ?: return Any(context, candidate.isNullable) + ClassType(superClass, candidate.isNullable) + } + is EnumType -> Any(context, candidate.isNullable) + is EnumVariantType -> { + val containingEnum = candidate.smlEnumVariant.containingEnumOrNull() + ?: return Any(context, candidate.isNullable) + EnumType(containingEnum, candidate.isNullable) + } + is RecordType -> Any(context, candidate.isNullable) + is UnionType -> throw AssertionError("Union types should have been unwrapped.") + UnresolvedType -> Any(context, candidate.isNullable) + is VariadicType -> Any(context, candidate.isNullable) + } + } + + return candidate +} + +private fun unwrapUnionTypes(types: List): List { + return types.flatMap { + when (it) { + is UnionType -> it.possibleTypes + else -> listOf(it) + } + } +} + +private fun isLowestCommonSupertype(candidate: Type, otherTypes: List): Boolean { + if (candidate is ClassType && candidate.smlClass.qualifiedNameOrNull() == StdlibClasses.Any) { + return true + } + + return otherTypes.all { it.isSubstitutableFor(candidate) } +} + +private fun Any(context: EObject, isNullable: Boolean = false) = stdlibType( + context, + StdlibClasses.Any, + isNullable +) + +private fun Boolean(context: EObject) = stdlibType(context, StdlibClasses.Boolean) +private fun Float(context: EObject) = stdlibType(context, StdlibClasses.Float) +private fun Int(context: EObject) = stdlibType(context, StdlibClasses.Int) +private fun Nothing(context: EObject, isNullable: Boolean = false) = stdlibType( + context, + StdlibClasses.Nothing, + isNullable +) + +private fun String(context: EObject) = stdlibType(context, StdlibClasses.String) + +internal fun stdlibType(context: EObject, qualifiedName: QualifiedName, isNullable: Boolean = false): Type { + return when (val smlClass = context.getStdlibClassOrNull(qualifiedName)) { + null -> UnresolvedType + else -> ClassType(smlClass, isNullable) + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/typing/TypeConformance.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/typing/TypeConformance.kt new file mode 100644 index 000000000..eb6505a98 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/typing/TypeConformance.kt @@ -0,0 +1,130 @@ +package de.unibonn.simpleml.staticAnalysis.typing + +import de.unibonn.simpleml.emf.variantsOrEmpty +import de.unibonn.simpleml.naming.qualifiedNameOrNull +import de.unibonn.simpleml.staticAnalysis.classHierarchy.isSubtypeOf +import de.unibonn.simpleml.stdlibAccess.StdlibClasses + +fun Type.isSubstitutableFor(other: Type, resultIfUnresolved: Boolean = false): Boolean { + if (other == UnresolvedType) { + return resultIfUnresolved + } + + return when (this) { + is CallableType -> this.isSubstitutableFor(other) + is ClassType -> this.isSubstitutableFor(other) + is EnumType -> this.isSubstitutableFor(other) + is EnumVariantType -> this.isSubstitutableFor(other) + is UnionType -> this.isSubstitutableFor(other) + is VariadicType -> this.isSubstitutableFor(other) + is RecordType -> false + UnresolvedType -> resultIfUnresolved + } +} + +private fun CallableType.isSubstitutableFor(other: Type): Boolean { + return when (val unwrappedOther = unwrapVariadicType(other)) { + is CallableType -> { + // TODO: We need to compare names of parameters & results and can allow additional optional parameters + + // Sizes must match (too strict requirement -> should be loosened later) + if (this.parameters.size != unwrappedOther.parameters.size || this.results.size != this.results.size) { + return false + } + + // Actual parameters must be supertypes of expected parameters (contravariance) + this.parameters.zip(unwrappedOther.parameters).forEach { (thisParameter, otherParameter) -> + if (!otherParameter.isSubstitutableFor(thisParameter)) { + return false + } + } + + // Expected results must be subtypes of expected results (covariance) + this.results.zip(unwrappedOther.results).forEach { (thisResult, otherResult) -> + if (!thisResult.isSubstitutableFor(otherResult)) { + return false + } + } + + true + } + is ClassType -> { + unwrappedOther.smlClass.qualifiedNameOrNull() == StdlibClasses.Any + } + is UnionType -> { + unwrappedOther.possibleTypes.any { this.isSubstitutableFor(it) } + } + else -> false + } +} + +private fun ClassType.isSubstitutableFor(other: Type): Boolean { + return when (val unwrappedOther = unwrapVariadicType(other)) { + is ClassType -> { + (!this.isNullable || unwrappedOther.isNullable) && this.smlClass.isSubtypeOf(unwrappedOther.smlClass) + } + is UnionType -> { + unwrappedOther.possibleTypes.any { this.isSubstitutableFor(it) } + } + else -> false + } +} + +private fun EnumType.isSubstitutableFor(other: Type): Boolean { + return when (val unwrappedOther = unwrapVariadicType(other)) { + is ClassType -> { + (!this.isNullable || unwrappedOther.isNullable) && + unwrappedOther.smlClass.qualifiedNameOrNull() == StdlibClasses.Any + } + is EnumType -> { + (!this.isNullable || unwrappedOther.isNullable) && this.smlEnum == unwrappedOther.smlEnum + } + is UnionType -> { + unwrappedOther.possibleTypes.any { this.isSubstitutableFor(it) } + } + else -> false + } +} + +private fun EnumVariantType.isSubstitutableFor(other: Type): Boolean { + return when (val unwrappedOther = unwrapVariadicType(other)) { + is ClassType -> { + (!this.isNullable || unwrappedOther.isNullable) && + unwrappedOther.smlClass.qualifiedNameOrNull() == StdlibClasses.Any + } + is EnumType -> { + (!this.isNullable || unwrappedOther.isNullable) && + this.smlEnumVariant in unwrappedOther.smlEnum.variantsOrEmpty() + } + is EnumVariantType -> { + (!this.isNullable || unwrappedOther.isNullable) && this.smlEnumVariant == unwrappedOther.smlEnumVariant + } + is UnionType -> unwrappedOther.possibleTypes.any { this.isSubstitutableFor(it) } + else -> false + } +} + +/** + * A [UnionType] can be substituted for another type of all its possible types can be substituted for the other type. + */ +private fun UnionType.isSubstitutableFor(other: Type): Boolean { + return this.possibleTypes.all { it.isSubstitutableFor(other) } +} + +private fun VariadicType.isSubstitutableFor(other: Type): Boolean { + return when (other) { + is ClassType -> other.smlClass.qualifiedNameOrNull() == StdlibClasses.Any + is VariadicType -> this.elementType.isSubstitutableFor(other) + else -> false + } +} + +/** + * Returns the elementType of a [VariadicType] or, otherwise, the type itself. + */ +private fun unwrapVariadicType(type: Type): Type { + return when (type) { + is VariadicType -> type.elementType + else -> type + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/typing/Types.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/typing/Types.kt new file mode 100644 index 000000000..a2c4c4154 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis/typing/Types.kt @@ -0,0 +1,155 @@ +package de.unibonn.simpleml.staticAnalysis.typing + +import de.unibonn.simpleml.emf.containingEnumOrNull +import de.unibonn.simpleml.naming.qualifiedNameOrNull +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import org.eclipse.xtext.naming.QualifiedName + +sealed class Type { + abstract val isNullable: Boolean + abstract fun setIsNullableOnCopy(isNullable: Boolean): Type + abstract fun toSimpleString(): String +} + +class RecordType(resultToType: List>) : Type() { + private val resultToType = resultToType.toMap() + + override val isNullable = false + override fun setIsNullableOnCopy(isNullable: Boolean) = this + + override fun toString(): String { + val types = resultToType.entries.joinToString { (name, type) -> "$name: $type" } + return "($types)" + } + + override fun toSimpleString(): String { + val types = resultToType.entries.joinToString { (name, type) -> "$name: ${type.toSimpleString()}" } + return "($types)" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as RecordType + + if (resultToType != other.resultToType) return false + if (isNullable != other.isNullable) return false + + return true + } + + override fun hashCode(): Int { + var result = resultToType.hashCode() + result = 31 * result + isNullable.hashCode() + return result + } +} + +data class CallableType(val parameters: List, val results: List) : Type() { + override val isNullable = false + override fun setIsNullableOnCopy(isNullable: Boolean) = this + + override fun toString(): String { + val parameters = parameters.joinToString() + val results = results.joinToString() + + return "($parameters) -> ($results)" + } + + override fun toSimpleString(): String { + val parameters = parameters.joinToString { it.toSimpleString() } + val results = results.joinToString { it.toSimpleString() } + + return "($parameters) -> ($results)" + } +} + +sealed class NamedType(smlDeclaration: SmlAbstractDeclaration) : Type() { + val simpleName: String = smlDeclaration.name + val qualifiedName: QualifiedName = smlDeclaration.qualifiedNameOrNull()!! + + override fun toString() = buildString { + append(qualifiedName) + if (isNullable) { + append("?") + } + } + + override fun toSimpleString() = buildString { + append(simpleName) + if (isNullable) { + append("?") + } + } +} + +data class ClassType( + val smlClass: SmlClass, + override val isNullable: Boolean +) : NamedType(smlClass) { + override fun setIsNullableOnCopy(isNullable: Boolean) = this.copy(isNullable = isNullable) + + override fun toString() = super.toString() +} + +data class EnumType( + val smlEnum: SmlEnum, + override val isNullable: Boolean +) : NamedType(smlEnum) { + override fun setIsNullableOnCopy(isNullable: Boolean) = this.copy(isNullable = isNullable) + + override fun toString() = super.toString() +} + +data class EnumVariantType( + val smlEnumVariant: SmlEnumVariant, + override val isNullable: Boolean +) : NamedType(smlEnumVariant) { + override fun setIsNullableOnCopy(isNullable: Boolean) = this.copy(isNullable = isNullable) + + override fun toString() = super.toString() + + override fun toSimpleString() = buildString { + smlEnumVariant.containingEnumOrNull()?.let { append("${it.name}.") } + append(smlEnumVariant.name) + } +} + +data class UnionType(val possibleTypes: Set) : Type() { + override val isNullable = false + override fun setIsNullableOnCopy(isNullable: Boolean) = this + + override fun toString(): String { + return "union<${possibleTypes.joinToString()}>" + } + + override fun toSimpleString(): String { + return "union<${possibleTypes.joinToString { it.toSimpleString() }}>" + } +} + +data class VariadicType(val elementType: Type) : Type() { + override val isNullable = false + override fun setIsNullableOnCopy(isNullable: Boolean) = this + + override fun toString(): String { + return "vararg<$elementType>" + } + + override fun toSimpleString(): String { + return "vararg<${elementType.toSimpleString()}>" + } +} + +object UnresolvedType : Type() { + override val isNullable = false + override fun setIsNullableOnCopy(isNullable: Boolean) = this + + override fun toString() = "\$Unresolved" + + override fun toSimpleString() = toString() +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibAnnotations.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibAnnotations.kt new file mode 100644 index 000000000..ad01aecbe --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibAnnotations.kt @@ -0,0 +1,243 @@ +@file:Suppress("MemberVisibilityCanBePrivate") + +package de.unibonn.simpleml.stdlibAccess + +import de.unibonn.simpleml.emf.annotationCallsOrEmpty +import de.unibonn.simpleml.emf.argumentsOrEmpty +import de.unibonn.simpleml.naming.qualifiedNameOrNull +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.simpleML.SmlAnnotationCall +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.staticAnalysis.linking.parameterOrNull +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.SmlConstantEnumVariant +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.SmlConstantExpression +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.SmlConstantString +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.toConstantExpressionOrNull +import de.unibonn.simpleml.stdlibAccess.StdlibEnums.AnnotationTarget +import de.unibonn.simpleml.utils.uniqueOrNull +import org.eclipse.xtext.naming.QualifiedName + +/** + * Important annotations in the standard library. + */ +object StdlibAnnotations { + + /** + * Values assigned to this parameter must be constant. + * + * @see isConstant + */ + val Constant: QualifiedName = StdlibPackages.lang.append("Constant") + + /** + * The declaration should no longer be used. + * + * @see isDeprecated + */ + val Deprecated: QualifiedName = StdlibPackages.lang.append("Deprecated") + + /** + * The purpose of a declaration. + * + * @see descriptionOrNull + */ + val Description: QualifiedName = StdlibPackages.lang.append("Description") + + /** + * This parameter should only be used by expert users. + * + * @see isExpert + */ + val Expert: QualifiedName = StdlibPackages.lang.append("Expert") + + /** + * The function has no side effects. + * + * @see hasNoSideEffects + */ + val NoSideEffects: QualifiedName = StdlibPackages.lang.append("NoSideEffects") + + /** + * The qualified name of the corresponding module in Python. + * + * @see pythonModuleOrNull + */ + val PythonModule: QualifiedName = StdlibPackages.lang.append("PythonModule") + + /** + * The name of the corresponding API element in Python. + * + * @see pythonNameOrNull + */ + val PythonName: QualifiedName = StdlibPackages.lang.append("PythonName") + + /** + * The function has no side effects and returns the same results for the same arguments. + * + * @see isPure + */ + val Pure: QualifiedName = StdlibPackages.lang.append("Pure") + + /** + * The annotation can be used multiple times for the same declaration. + * + * @see isRepeatable + */ + val Repeatable: QualifiedName = StdlibPackages.lang.append("Repeatable") + + /** + * The version in which a declaration was added. + * + * @see sinceVersionOrNull + */ + val Since: QualifiedName = StdlibPackages.lang.append("Since") + + /** + * The annotation can target only a subset of declaration types. + * + * @see validTargets + */ + val Target: QualifiedName = StdlibPackages.lang.append("Target") +} + +/** + * Returns all calls of the annotation with the given qualified name. + */ +fun SmlAbstractDeclaration.annotationCallsOrEmpty(qualifiedName: QualifiedName): List { + return this.annotationCallsOrEmpty().filter { + it.annotation.qualifiedNameOrNull() == qualifiedName + } +} + +/** + * Returns the unique use of the annotation with the given qualified name or `null` if none or multiple exist. + */ +fun SmlAbstractDeclaration.uniqueAnnotationCallOrNull(qualifiedName: QualifiedName): SmlAnnotationCall? { + return this.annotationCallsOrEmpty(qualifiedName).uniqueOrNull() +} + +/** + * Returns the description attached to the declaration with a `simpleml.lang.Description` annotation. + */ +fun SmlAbstractDeclaration.descriptionOrNull(): String? { + val value = annotationCallArgumentValueOrNull(StdlibAnnotations.Description, "description") + return (value as? SmlConstantString)?.value +} + +/** + * Checks if the parameter is annotated with the `simpleml.lang.Constant` annotation. + */ +fun SmlParameter.isConstant(): Boolean { + return hasAnnotationCallTo(StdlibAnnotations.Constant) +} + +/** + * Checks if the declaration is annotated with the `simpleml.lang.Deprecated` annotation. + */ +fun SmlAbstractDeclaration.isDeprecated(): Boolean { + return hasAnnotationCallTo(StdlibAnnotations.Deprecated) +} + +/** + * Checks if the parameter is annotated with the `simpleml.lang.Expert` annotation. + */ +fun SmlParameter.isExpert(): Boolean { + return hasAnnotationCallTo(StdlibAnnotations.Expert) +} + +/** + * Checks if the function is annotated with the `simpleml.lang.Pure` annotation. + */ +fun SmlFunction.isPure(): Boolean { + return hasAnnotationCallTo(StdlibAnnotations.Pure) +} + +/** + * Checks if the annotation is annotated with the `simpleml.lang.Repeatable` annotation. + */ +fun SmlAnnotation.isRepeatable(): Boolean { + return hasAnnotationCallTo(StdlibAnnotations.Repeatable) +} + +/** + * Checks if the function is annotated with the `simpleml.lang.Pure` or the `simpleml.lang.NoSideEffects` + * annotation. + */ +fun SmlFunction.hasNoSideEffects(): Boolean { + return isPure() || hasAnnotationCallTo(StdlibAnnotations.NoSideEffects) +} + +/** + * Returns the qualified name of the Python module that corresponds to this compilation unit. It is attached to the + * compilation unit with a `simpleml.lang.PythonModule` annotation. + */ +fun SmlCompilationUnit.pythonModuleOrNull(): String? { + val value = annotationCallArgumentValueOrNull( + StdlibAnnotations.PythonModule, + "qualifiedName" + ) + return (value as? SmlConstantString)?.value +} + +/** + * Returns the name of the Python API element that corresponds to this declaration. It is attached to the declaration + * with a `simpleml.lang.PythonName` annotation. + */ +fun SmlAbstractDeclaration.pythonNameOrNull(): String? { + val value = annotationCallArgumentValueOrNull(StdlibAnnotations.PythonName, "name") + return (value as? SmlConstantString)?.value +} + +/** + * Returns the version when the declaration was added. This is attached to the declaration with a `simpleml.lang.Since` + * annotation. + */ +fun SmlAbstractDeclaration.sinceVersionOrNull(): String? { + val value = annotationCallArgumentValueOrNull(StdlibAnnotations.Since, "version") + return (value as? SmlConstantString)?.value +} + +/** + * Returns the possible targets of this annotation. + */ +fun SmlAnnotation.validTargets(): List { + val targetAnnotationCall = uniqueAnnotationCallOrNull(StdlibAnnotations.Target) + ?: return AnnotationTarget.values().toList() + + return targetAnnotationCall + .argumentsOrEmpty() + .asSequence() + .mapNotNull { it.value.toConstantExpressionOrNull() } + .filterIsInstance() + .mapNotNull { it.value.qualifiedNameOrNull() } + .filter { it.segmentCount == 4 && it.skipLast(1) == AnnotationTarget.enum } + .mapNotNull { AnnotationTarget.valueOfOrNull(it.lastSegment) } + .toList() +} + +/** + * Returns whether this [SmlAbstractDeclaration] has at least one annotation call to the annotation with the given + * qualified name. + */ +private fun SmlAbstractDeclaration.hasAnnotationCallTo(qualifiedName: QualifiedName): Boolean { + return annotationCallsOrEmpty().any { + it.annotation.qualifiedNameOrNull() == qualifiedName + } +} + +/** + * Finds the unique call to a declaration with the given qualified name and looks up the value assigned to the parameter + * with the given name. + */ +private fun SmlAbstractDeclaration.annotationCallArgumentValueOrNull( + qualifiedName: QualifiedName, + parameterName: String +): SmlConstantExpression? { + return uniqueAnnotationCallOrNull(qualifiedName) + .argumentsOrEmpty() + .uniqueOrNull { it.parameterOrNull()?.name == parameterName } + ?.toConstantExpressionOrNull() +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibClasses.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibClasses.kt new file mode 100644 index 000000000..33ab5f736 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibClasses.kt @@ -0,0 +1,18 @@ +@file:Suppress("MemberVisibilityCanBePrivate") + +package de.unibonn.simpleml.stdlibAccess + +import org.eclipse.xtext.naming.QualifiedName + +/** + * Important classes in the standard library. + */ +object StdlibClasses { + val Any: QualifiedName = StdlibPackages.lang.append("Any") + val Boolean: QualifiedName = StdlibPackages.lang.append("Boolean") + val Number: QualifiedName = StdlibPackages.lang.append("Number") + val Float: QualifiedName = StdlibPackages.lang.append("Float") + val Int: QualifiedName = StdlibPackages.lang.append("Int") + val Nothing: QualifiedName = StdlibPackages.lang.append("Nothing") + val String: QualifiedName = StdlibPackages.lang.append("String") +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibEnums.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibEnums.kt new file mode 100644 index 000000000..cda7fc9f5 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibEnums.kt @@ -0,0 +1,37 @@ +@file:Suppress("MemberVisibilityCanBePrivate") + +package de.unibonn.simpleml.stdlibAccess + +import org.eclipse.xtext.naming.QualifiedName + +/** + * Important enums in the standard library. + */ +object StdlibEnums { + + /** + * Describes declaration types that can be targeted by annotations. + */ + enum class AnnotationTarget { + Annotation, + Attribute, + Class, + CompilationUnit, + Enum, + EnumVariant, + Function, + Parameter, + Result, + Step, + TypeParameter, + Workflow; + + companion object { + val enum: QualifiedName = QualifiedName.create("simpleml", "lang", "AnnotationTarget") + + fun valueOfOrNull(name: String): AnnotationTarget? { + return values().firstOrNull { it.name == name } + } + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibLoader.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibLoader.kt new file mode 100644 index 000000000..cd2846387 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibLoader.kt @@ -0,0 +1,124 @@ +package de.unibonn.simpleml.stdlibAccess + +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.emf.resourceSetOrNull +import de.unibonn.simpleml.scoping.allGlobalDeclarations +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlClass +import org.eclipse.core.runtime.FileLocator +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.resource.ResourceSet +import org.eclipse.xtext.naming.QualifiedName +import java.nio.file.FileSystem +import java.nio.file.FileSystems +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.util.WeakHashMap + +private val cache = WeakHashMap>() +private val classLoader = object {}.javaClass.classLoader + +/** + * Loads the standard library into this resource set. + */ +fun ResourceSet.loadStdlib() { + listStdlibFiles().forEach { (path, uri) -> + createResource(uri).load(Files.newInputStream(path), loadOptions) + } +} + +/** + * Lists all files that comprise the standard library. + */ +fun listStdlibFiles(): Sequence> { + val resourcesUrl = classLoader.getResource("stdlib") ?: return emptySequence() + val resourcesUri = FileLocator.resolve(resourcesUrl).toURI() + + return sequence { + var fileSystem: FileSystem? = null + val stdlibBase = when (resourcesUri.scheme) { + + // Without this code tests fail with a FileSystemNotFoundException since stdlib resources are in a jar + "jar" -> { + fileSystem = FileSystems.newFileSystem( + resourcesUri, + emptyMap(), + null + ) + fileSystem.getPath("stdlib") + } + else -> Paths.get(resourcesUri) + } + + val stdlibFiles = Files.walk(stdlibBase) + .filter { it.toString().endsWith(".${SmlFileExtension.Stub}") } + + for (path in stdlibFiles) { + val relativePath = path.toString().replace("stdlib/", "") + val uri = URI.createURI("$resourcesUri/$relativePath".replace("%3A", ":")) + yield(path to uri) + } + + fileSystem?.close() + } +} + +/** + * Returns the [SmlClass] with the given [QualifiedName] within this context. If the declaration cannot be found, `null` + * is returned. + * + * @receiver The context for the search. + */ +fun EObject.getStdlibClassOrNull(qualifiedName: QualifiedName): SmlClass? { + return getStdlibDeclarationOrNull(qualifiedName) +} + +/** + * Returns the declaration with the given [QualifiedName] within this context. If the declaration cannot be found, + * `null` is returned. + * + * @receiver The context for the search. + */ +private inline fun EObject.getStdlibDeclarationOrNull( + qualifiedName: QualifiedName +): T? { + return try { + getStdlibDeclaration(qualifiedName) + } catch (e: IllegalStateException) { + null + } +} + +/** + * Returns the declaration with the given [QualifiedName] within this context. If the declaration cannot be found an + * exception is thrown. + * + * @receiver The context for the search. + * @throws IllegalStateException If the context is not in a resource set. + * @throws IllegalStateException If no declaration with the qualified name exists in the resource set. + * @throws IllegalStateException If the declaration with the qualified name does not have the desired type. + */ +private inline fun EObject.getStdlibDeclaration(qualifiedName: QualifiedName): T { + val resourceSet = resourceSetOrNull() ?: throw IllegalStateException("This context is not in a resource set.") + val cacheForResourceSet = cache.getOrPut(resourceSet) { WeakHashMap() } + + val eObject = cacheForResourceSet.computeIfAbsent(qualifiedName) { + val description = allGlobalDeclarations() + .find { it.qualifiedName == qualifiedName } + ?: throw IllegalStateException("Failed to load stdlib declaration '$qualifiedName'.") + + var eObject = description.eObjectOrProxy + if (eObject != null && eObject.eIsProxy()) { + eObject = eResource().resourceSet.getEObject(description.eObjectURI, true) + } + + eObject + } + + return when (eObject) { + is T -> eObject + else -> throw IllegalStateException("Stdlib declaration '$qualifiedName' is not an ${T::class.simpleName}.") + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibPackages.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibPackages.kt new file mode 100644 index 000000000..200c1731f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibPackages.kt @@ -0,0 +1,16 @@ +@file:Suppress("MemberVisibilityCanBePrivate") + +package de.unibonn.simpleml.stdlibAccess + +import org.eclipse.xtext.naming.QualifiedName + +/** + * Important packages in the standard library. + */ +object StdlibPackages { + + /** + * Core package that is implicitly imported into all Simple-ML programs. + */ + val lang: QualifiedName = QualifiedName.create("simpleml", "lang") +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibDocumentation/DocumentationGenerator.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibDocumentation/DocumentationGenerator.kt new file mode 100644 index 000000000..945d9fdb8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibDocumentation/DocumentationGenerator.kt @@ -0,0 +1,408 @@ +// The duplication gives us the flexibility to change parts of the generated Markdown individually +@file:Suppress("DuplicatedCode") + +package de.unibonn.simpleml.stdlibDocumentation + +import de.unibonn.simpleml.emf.classMembersOrEmpty +import de.unibonn.simpleml.emf.containingCompilationUnitOrNull +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.resultsOrEmpty +import de.unibonn.simpleml.emf.variantsOrEmpty +import de.unibonn.simpleml.scoping.allGlobalDeclarations +import de.unibonn.simpleml.serializer.SerializationResult +import de.unibonn.simpleml.serializer.serializeToFormattedString +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.stdlibAccess.descriptionOrNull +import de.unibonn.simpleml.stdlibAccess.validTargets +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtext.EcoreUtil2 +import java.nio.file.Path +import kotlin.io.path.createDirectories +import kotlin.io.path.writeText + +private val horizontalRule = "-".repeat(10) + +private val autogenWarning = """ + |$horizontalRule + | + |**This file was created automatically. Do not change it manually!** +""".trimMargin() + +/** + * Generates documentation for all declarations that are visible from the given context. + * + * @receiver The context. + * @param outputDirectory Where to place the created files. + */ +fun EObject.generateDocumentation(outputDirectory: Path) { + outputDirectory.toFile().deleteRecursively() + outputDirectory.createDirectories() + + val packagesToDeclarations: Map> = allGlobalDeclarations() + .map { it.eObjectOrProxy } + .filter { it.containingCompilationUnitOrNull()?.name != null } + .groupBy { it.containingCompilationUnitOrNull()!!.name } + + createReadme(outputDirectory, packagesToDeclarations) + createPackageFiles(outputDirectory, packagesToDeclarations) +} + +private fun createReadme(outputDirectory: Path, packagesToDeclarations: Map>) { + val packagesDocumentation = packagesToDeclarations.keys + .sorted() + .joinToString(separator = "\n") { packageName -> + "* [$packageName](./${packageName.replace(".", "_")}.md)" + } + + outputDirectory.resolve("README.md").writeText( + """ + |# Simple-ML API Documentation + | + |## Packages + | + |$packagesDocumentation + | + |$autogenWarning + | + """.trimMargin() + ) +} + +private fun createPackageFiles(outputDirectory: Path, packagesToDeclarations: Map>) { + packagesToDeclarations.forEach { (packageName, globalDeclarations) -> + createPackageFile(outputDirectory, packageName, globalDeclarations) + } +} + +private fun createPackageFile(outputDirectory: Path, packageName: String, globalDeclarations: List) { + outputDirectory + .resolve("${packageName.replace(".", "_")}.md") + .writeText(createPackageDocumentation(packageName, globalDeclarations)) +} + +private fun createPackageDocumentation( + packageName: String, + globalDeclarations: List +) = buildString { + val classes = globalDeclarations.filterIsInstance().sortedBy { it.name } + val globalFunctions = globalDeclarations.filterIsInstance().sortedBy { it.name } + val enums = globalDeclarations.filterIsInstance().sortedBy { it.name } + val annotations = globalDeclarations.filterIsInstance().sortedBy { it.name } + + appendLine("# Package `$packageName`") + + // Table of contents + if (annotations.isNotEmpty() || classes.isNotEmpty() || enums.isNotEmpty() || globalFunctions.isNotEmpty()) { + appendLine("\n## Table of Contents\n") + + if (classes.isNotEmpty()) { + appendLine("* Classes") + classes.forEach { + appendLine(" * [`${it.name}`](#class-${it.name})") + } + } + + if (globalFunctions.isNotEmpty()) { + appendLine("* Global functions") + globalFunctions.forEach { + appendLine(" * [`${it.name}`](#global-function-${it.name})") + } + } + + if (enums.isNotEmpty()) { + appendLine("* Enums") + enums.forEach { + appendLine(" * [`${it.name}`](#enum-${it.name})") + } + } + + if (enums.isNotEmpty()) { + appendLine("* Annotations") + annotations.forEach { + appendLine(" * [`${it.name}`](#annotation-${it.name})") + } + } + + appendLine("\n$horizontalRule\n") + } + + // Classes + classes.forEach { + appendLine(createClassDocumentation(it, nestingLevel = 2)) + if (it != classes.last()) { + appendLine("$horizontalRule\n") + } + } + + // Global functions + if (globalFunctions.isNotEmpty()) { + appendLine("## Global Functions\n") + globalFunctions.forEach { + appendLine(createFunctionDocumentation(it, nestingLevel = 3, isGlobalFunction = true)) + } + } + + // Enums + enums.forEach { + appendLine(createEnumDocumentation(it, nestingLevel = 2)) + } + + // Annotations + annotations.forEach { + appendLine(createAnnotationDocumentation(it)) + } + + appendLine(autogenWarning) +} + +private fun createAnnotationDocumentation(annotation: SmlAnnotation) = buildString { + + // Heading + appendLine("## Annotation `${annotation.name}`") + + // Description + appendLine(annotation.descriptionOrAltText()) + + // Parameters + if (annotation.parametersOrEmpty().isNotEmpty()) { + append("\n" + createParametersDocumentation(annotation.parametersOrEmpty())) + } + + // Targets + appendLine("\n**Valid targets:**") + val validTargets = annotation + .validTargets() + .sortedBy { it.name } + .joinToString(separator = "\n") { + "* ${it.name}" + } + appendLine(validTargets) +} + +private fun createAttributeDocumentation(attribute: SmlAttribute) = buildString { + + // Remember description before annotation calls are removed + val description = attribute.descriptionOrAltText() + + // Remove annotation calls, so they don't show up in the serialized code + attribute.annotationCalls + .toList() + .forEach { annotationCall -> + EcoreUtil2.remove(annotationCall) + } + + // Try to serialize the attribute + val itemHeading = when (val serializationResult = attribute.serializeToFormattedString()) { + is SerializationResult.Success -> "`${serializationResult.code}`" + else -> attribute.name + } + + // Heading + append("* $itemHeading") + + // Description + appendLine(" - $description") +} + +private fun createClassDocumentation(`class`: SmlClass, nestingLevel: Int): String = buildString { + val attributes = `class`.classMembersOrEmpty().filterIsInstance().sortedBy { it.name } + val methods = `class`.classMembersOrEmpty().filterIsInstance().sortedBy { it.name } + val classes = `class`.classMembersOrEmpty().filterIsInstance().sortedBy { it.name } + val enums = `class`.classMembersOrEmpty().filterIsInstance().sortedBy { it.name } + + // Heading + if (isGlobal(nestingLevel)) { + appendLine("## Class `${`class`.name}`") + } else { + appendLine("${heading(nestingLevel)} Nested Class `${`class`.name}") + } + + // Description + appendLine(`class`.descriptionOrAltText()) + + // Constructor + appendLine("\n" + createConstructorDocumentation(`class`)) + + // Attributes + if (attributes.isNotEmpty()) { + appendLine("**Attributes:**") + attributes.forEach { + append(createAttributeDocumentation(it)) + } + appendLine() + } + + // Methods + methods.forEach { + appendLine(createFunctionDocumentation(it, nestingLevel + 1, isGlobalFunction = false)) + } + + // Classes + classes.forEach { + appendLine(createClassDocumentation(it, nestingLevel + 1)) + } + + // Enums + enums.forEach { + appendLine(createEnumDocumentation(it, nestingLevel + 1)) + } +} + +private fun createConstructorDocumentation(`class`: SmlClass) = buildString { + if (`class`.parameterList == null) { + appendLine("**Constructor:** _Class has no constructor._") + } else if (`class`.parametersOrEmpty().isEmpty()) { + appendLine("**Constructor parameters:** _None expected._") + } else { + appendLine("**Constructor parameters:**") + `class`.parametersOrEmpty().forEach { + appendLine(createParameterDocumentation(it)) + } + } +} + +private fun createEnumDocumentation(enum: SmlEnum, nestingLevel: Int) = buildString { + val variants = enum.variantsOrEmpty().sortedBy { it.name } + + // Heading + if (isGlobal(nestingLevel)) { + appendLine("## Enum `${enum.name}`") + } else { + appendLine("${heading(nestingLevel)} Nested Enum `${enum.name}") + } + + // Description + appendLine(enum.descriptionOrAltText()) + + // Variants + variants.forEach { + appendLine(createEnumVariantDocumentation(it, nestingLevel + 1)) + } +} + +private fun createEnumVariantDocumentation(enumVariant: SmlEnumVariant, nestingLevel: Int) = buildString { + + // Heading + appendLine("${heading(nestingLevel)} Enum Variant `${enumVariant.name}`") + + // Description + appendLine(enumVariant.descriptionOrAltText()) + + // Parameters + appendLine("\n" + createParametersDocumentation(enumVariant.parametersOrEmpty())) +} + +private fun SmlAbstractDeclaration.descriptionOrAltText(): String { + return descriptionOrNull() ?: "_No description available._" +} + +private fun createFunctionDocumentation(function: SmlFunction, nestingLevel: Int, isGlobalFunction: Boolean) = buildString { + + // Heading + if (isGlobalFunction) { + appendLine("## Global Function `${function.name}`") + } else if (function.isStatic) { + appendLine("${heading(nestingLevel)} `${function.name}` (Static Method)") + } else { + appendLine("${heading(nestingLevel)} `${function.name}` (Instance Method )") + } + + // Description + appendLine(function.descriptionOrAltText()) + + // Parameters + appendLine("\n" + createParametersDocumentation(function.parametersOrEmpty())) + + // Results + append(createResultsDocumentation(function.resultsOrEmpty())) +} + +private fun createParametersDocumentation(parameters: List) = buildString { + if (parameters.isEmpty()) { + appendLine("**Parameters:** _None expected._") + } else { + appendLine("**Parameters:**") + parameters.forEach { + appendLine(createParameterDocumentation(it)) + } + } +} + +private fun createParameterDocumentation(parameter: SmlParameter) = buildString { + + // Remember description before annotation calls are removed + val description = parameter.descriptionOrAltText() + + // Remove annotation calls, so they don't show up in the serialized code + parameter.annotationCalls + .toList() + .forEach { annotationCall -> + EcoreUtil2.remove(annotationCall) + } + + // Try to serialize the parameter + val itemHeading = when (val serializationResult = parameter.serializeToFormattedString()) { + is SerializationResult.Success -> "`${serializationResult.code}`" + else -> parameter.name + } + + // Heading + append("* $itemHeading") + + // Description + append(" - $description") +} + +private fun createResultsDocumentation(result: List) = buildString { + if (result.isEmpty()) { + appendLine("**Results:** _None returned._") + } else { + appendLine("**Results:**") + result.forEach { + appendLine(createResultDocumentation(it)) + } + } +} + +private fun createResultDocumentation(result: SmlResult) = buildString { + + // Remember description before annotation calls are removed + val description = result.descriptionOrAltText() + + // Remove annotation calls, so they don't show up in the serialized code + result.annotationCalls + .toList() + .forEach { annotationCall -> + EcoreUtil2.remove(annotationCall) + } + + // Try to serialize the result + val itemHeading = when (val serializationResult = result.serializeToFormattedString()) { + is SerializationResult.Success -> "`${serializationResult.code}`" + else -> result.name + } + + // Heading + append("* $itemHeading") + + // Description + append(" - $description") +} + +/** + * Creates the appropriate Markdown keyword for a heading with this [nestingLevel]. Markdown allows up to six levels of + * headings. + */ +private fun heading(nestingLevel: Int): String { + return "#".repeat(minOf(nestingLevel, 6)) +} + +private fun isGlobal(nestingLevel: Int) = nestingLevel == 2 diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibDocumentation/Main.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibDocumentation/Main.kt new file mode 100644 index 000000000..7f24b164d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/stdlibDocumentation/Main.kt @@ -0,0 +1,63 @@ +package de.unibonn.simpleml.stdlibDocumentation + +import com.google.inject.Inject +import com.google.inject.Provider +import de.unibonn.simpleml.SimpleMLStandaloneSetup +import de.unibonn.simpleml.emf.compilationUnitOrNull +import de.unibonn.simpleml.stdlibAccess.loadStdlib +import org.eclipse.emf.ecore.resource.ResourceSet +import org.eclipse.xtext.diagnostics.Severity +import org.eclipse.xtext.util.CancelIndicator +import org.eclipse.xtext.validation.CheckMode +import org.eclipse.xtext.validation.IResourceValidator +import java.nio.file.Path +import kotlin.system.exitProcess + +@Suppress("unused") +class Main @Inject constructor( + private val resourceSetProvider: Provider, + private val validator: IResourceValidator +) { + + fun runStdlibDocumentationGenerator(outputDirectory: Path) { + + // Load the standard library + val resourceSet = resourceSetProvider.get() + resourceSet.loadStdlib() + + // Validate all resources + var hasErrors = false + resourceSet.resources.forEach { resource -> + val issues = validator.validate(resource, CheckMode.ALL, CancelIndicator.NullImpl) + if (issues.any { it.severity == Severity.ERROR }) { + issues.forEach { println(it) } + hasErrors = true + } + } + + if (hasErrors) { + System.err.println("Aborting: A resource has errors.") + exitProcess(20) + } else { + val context = resourceSet.resources.getOrNull(0)?.compilationUnitOrNull() + if (context == null) { + System.err.println("Aborting: Resource set is empty.") + exitProcess(30) + } + context.generateDocumentation(outputDirectory) + } + + println("Generation of stdlib documentation finished.") + } +} + +fun main(args: Array) { + if (args.isEmpty()) { + System.err.println("Aborting: No path to output directory provided.") + exitProcess(10) + } + + val injector = SimpleMLStandaloneSetup().createInjectorAndDoEMFRegistration() + val main = injector.getInstance(Main::class.java) + main.runStdlibDocumentationGenerator(Path.of(args[0])) +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/utils/CollectionExtensions.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/utils/CollectionExtensions.kt new file mode 100644 index 000000000..d42af9f3e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/utils/CollectionExtensions.kt @@ -0,0 +1,58 @@ +package de.unibonn.simpleml.utils + +import kotlin.math.max + +/** + * Creates a list of all elements in the iterable that the labeler maps to the same label as at least one other element. + * Values that are mapped to `null` by the labeler are removed. + */ +internal fun Iterable.duplicatesBy(labeler: (I) -> K): List { + return this + .groupBy(labeler) + .filterKeys { it != null } + .values + .filter { it.size > 1 } + .flatten() +} + +/** + * Creates a list of all elements in the iterable that the labeler maps to a unique label. Values that are mapped to + * `null` by the labeler are removed. + */ +internal fun Iterable.uniqueBy(labeler: (I) -> K): List { + return this + .groupBy(labeler) + .filterKeys { it != null } + .values + .filter { it.size == 1 } + .flatten() +} + +/** + * Returns the unique element in the iterable that matches the filter or `null` if none or multiple exist. + */ +internal fun Iterable.uniqueOrNull(filter: (I) -> Boolean = { true }): I? { + val candidates = this.filter(filter) + return when (candidates.size) { + 1 -> candidates[0] + else -> null + } +} + +/** + * Returns `null` if this [List] is empty. Otherwise, calls [init] on the list. + */ +internal fun List.nullIfEmptyElse(init: (List) -> O): O? { + return ifEmpty { null }?.let { init(it) } +} + +/** + * Maps corresponding elements of the left and right list using the given zipper. The shorter list is padded with + * `null`s at the end, so the resulting list has the same length as the longer list. + */ +internal fun outerZipBy(left: List, right: List, zipper: (L?, R?) -> O): List { + val maxSize = max(left.size, right.size) + return (0 until maxSize).map { i -> + zipper(left.getOrNull(i), right.getOrNull(i)) + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/utils/IdManager.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/utils/IdManager.kt new file mode 100644 index 000000000..e91e2ae44 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/utils/IdManager.kt @@ -0,0 +1,76 @@ +package de.unibonn.simpleml.utils + +import de.unibonn.simpleml.simpleML.SmlAbstractObject +import java.util.WeakHashMap + +/** + * The ID of an object. + */ +@JvmInline +@Suppress("unused") +value class Id(val value: Int) + +/** + * Handles the mapping of objects, usually [SmlAbstractObject]s in the Simple-ML AST, to their IDs. + */ +class IdManager { + + /** + * Maps an object to an ID. + */ + private val objToId = WeakHashMap>() + + /** + * Maps an ID to an object. + */ + private val idToObj = WeakHashMap, UPPER>() + + /** + * The next available ID. + */ + private var nextId = 0 + + /** + * Assigns the next available ID to the given object unless it already has one and returns the ID for this object. + */ + fun assignIdIfAbsent(obj: T): Id { + if (obj !in objToId) { + val id = nextId() + objToId[obj] = id + idToObj[id] = obj + } + + // We only write to objToId here and can be sure the stored ID has the correct type + @Suppress("UNCHECKED_CAST") + return objToId[obj]!! as Id + } + + /** + * Returns the next available ID. + */ + private fun nextId() = Id(nextId++) + + /** + * Returns the object with the given ID or `null` if the ID was not assigned yet. + */ + fun getObjectById(id: Id<*>) = idToObj[id] + + /** + * Checks if the given object already has an ID. + */ + fun knowsObject(obj: Any) = obj in objToId + + /** + * Check if the given ID has already been assigned to some object. + */ + fun knowsId(id: Id<*>) = id in idToObj + + /** + * Removes all mappings between object and ID and resets the counter. + */ + fun reset() { + objToId.clear() + idToObj.clear() + nextId = 0 + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/AbstractSimpleMLChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/AbstractSimpleMLChecker.kt new file mode 100644 index 000000000..562bcfa2d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/AbstractSimpleMLChecker.kt @@ -0,0 +1,116 @@ +package de.unibonn.simpleml.validation + +import de.unibonn.simpleml.simpleML.SimpleMLPackage +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.utils.duplicatesBy +import de.unibonn.simpleml.validation.codes.ErrorCode +import de.unibonn.simpleml.validation.codes.InfoCode +import de.unibonn.simpleml.validation.codes.WarningCode +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.EStructuralFeature +import org.eclipse.xtext.validation.EValidatorRegistrar + +abstract class AbstractSimpleMLChecker : AbstractSimpleMLValidator() { + override fun register(registrar: EValidatorRegistrar) { + // This is overridden to prevent duplicate validation errors. + } + + protected fun List.reportDuplicateNames(message: (SmlAbstractDeclaration) -> String) { + this.duplicatesBy { it.name } + .forEach { + error( + message(it), + it, + SimpleMLPackage.Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.REDECLARATION + ) + } + } + + fun error(message: String, feature: EStructuralFeature?, code: ErrorCode, vararg issueData: String) { + super.error(message, feature, code.name, *issueData) + } + + fun error(message: String, feature: EStructuralFeature?, index: Int, code: ErrorCode, vararg issueData: String) { + super.error(message, feature, index, code.name, *issueData) + } + + fun error( + message: String, + source: EObject, + feature: EStructuralFeature?, + code: ErrorCode, + vararg issueData: String + ) { + super.error(message, source, feature, code.name, *issueData) + } + + fun error( + message: String, + source: EObject, + feature: EStructuralFeature?, + index: Int, + code: ErrorCode, + vararg issueData: String + ) { + super.error(message, source, feature, index, code.name, *issueData) + } + + fun warning(message: String, feature: EStructuralFeature?, code: WarningCode, vararg issueData: String) { + super.warning(message, feature, code.name, *issueData) + } + + fun warning( + message: String, + feature: EStructuralFeature?, + index: Int, + code: WarningCode, + vararg issueData: String + ) { + super.warning(message, feature, index, code.name, *issueData) + } + + fun warning( + message: String, + source: EObject, + feature: EStructuralFeature?, + code: WarningCode, + vararg issueData: String + ) { + super.warning(message, source, feature, code.name, *issueData) + } + + fun warning( + message: String, + source: EObject, + feature: EStructuralFeature?, + index: Int, + code: WarningCode, + vararg issueData: String + ) { + super.warning(message, source, feature, index, code.name, *issueData) + } + + fun info(message: String, feature: EStructuralFeature?, code: InfoCode, vararg issueData: String) { + super.info(message, feature, code.name, *issueData) + } + + fun info(message: String, feature: EStructuralFeature?, index: Int, code: InfoCode, vararg issueData: String) { + super.info(message, feature, index, code.name, *issueData) + } + + fun info(message: String, source: EObject, feature: EStructuralFeature?, code: InfoCode, vararg issueData: String) { + super.info(message, source, feature, code.name, *issueData) + } + + fun info( + message: String, + source: EObject, + feature: EStructuralFeature?, + index: Int, + code: InfoCode, + vararg issueData: String + ) { + super.info(message, source, feature, index, code.name, *issueData) + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/SimpleMLValidator.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/SimpleMLValidator.kt new file mode 100644 index 000000000..b7114bba8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/SimpleMLValidator.kt @@ -0,0 +1,105 @@ +package de.unibonn.simpleml.validation + +import de.unibonn.simpleml.validation.declarations.AnnotationChecker +import de.unibonn.simpleml.validation.declarations.AttributeChecker +import de.unibonn.simpleml.validation.declarations.ClassChecker +import de.unibonn.simpleml.validation.declarations.CompilationUnitChecker +import de.unibonn.simpleml.validation.declarations.DeclarationChecker +import de.unibonn.simpleml.validation.declarations.EnumChecker +import de.unibonn.simpleml.validation.declarations.EnumVariantChecker +import de.unibonn.simpleml.validation.declarations.FunctionChecker +import de.unibonn.simpleml.validation.declarations.ImportChecker +import de.unibonn.simpleml.validation.declarations.NameConventionChecker +import de.unibonn.simpleml.validation.declarations.ParameterChecker +import de.unibonn.simpleml.validation.declarations.ParameterListChecker +import de.unibonn.simpleml.validation.declarations.PlaceholderChecker +import de.unibonn.simpleml.validation.declarations.ResultChecker +import de.unibonn.simpleml.validation.declarations.StepChecker +import de.unibonn.simpleml.validation.declarations.WorkflowChecker +import de.unibonn.simpleml.validation.expressions.ArgumentChecker +import de.unibonn.simpleml.validation.expressions.CallChecker +import de.unibonn.simpleml.validation.expressions.InfixOperationChecker +import de.unibonn.simpleml.validation.expressions.LambdaChecker +import de.unibonn.simpleml.validation.expressions.MemberAccessChecker +import de.unibonn.simpleml.validation.expressions.ReferenceChecker +import de.unibonn.simpleml.validation.expressions.TemplateStringChecker +import de.unibonn.simpleml.validation.other.AnnotationCallChecker +import de.unibonn.simpleml.validation.other.ArgumentListChecker +import de.unibonn.simpleml.validation.other.DeprecationChecker +import de.unibonn.simpleml.validation.other.ProtocolChecker +import de.unibonn.simpleml.validation.other.TypeArgumentListChecker +import de.unibonn.simpleml.validation.statements.AssignmentChecker +import de.unibonn.simpleml.validation.statements.ExpressionsStatementChecker +import de.unibonn.simpleml.validation.typeChecking.ArgumentTypeChecker +import de.unibonn.simpleml.validation.typeChecking.DefaultValueTypeChecker +import de.unibonn.simpleml.validation.typeChecking.IndexedAccessTypeChecker +import de.unibonn.simpleml.validation.typeChecking.InfixOperationTypeChecker +import de.unibonn.simpleml.validation.typeChecking.PrefixOperationTypeChecker +import de.unibonn.simpleml.validation.typeChecking.YieldTypeChecker +import de.unibonn.simpleml.validation.types.CallableTypeChecker +import de.unibonn.simpleml.validation.types.NamedTypeChecker +import de.unibonn.simpleml.validation.types.UnionTypeChecker +import org.eclipse.xtext.validation.ComposedChecks + +/** + * This class contains custom validation rules. + * + * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation + */ +@ComposedChecks( + validators = [ + + // Declarations + AnnotationChecker::class, + AttributeChecker::class, + ClassChecker::class, + CompilationUnitChecker::class, + DeclarationChecker::class, + EnumChecker::class, + EnumVariantChecker::class, + FunctionChecker::class, + ImportChecker::class, + ParameterChecker::class, + ParameterListChecker::class, + PlaceholderChecker::class, + ResultChecker::class, + WorkflowChecker::class, + StepChecker::class, + + NameConventionChecker::class, + + // Expressions + ArgumentChecker::class, + CallChecker::class, + InfixOperationChecker::class, + LambdaChecker::class, + MemberAccessChecker::class, + ReferenceChecker::class, + TemplateStringChecker::class, + + // Statements + AssignmentChecker::class, + ExpressionsStatementChecker::class, + + // Type Checking + ArgumentTypeChecker::class, + DefaultValueTypeChecker::class, + IndexedAccessTypeChecker::class, + InfixOperationTypeChecker::class, + PrefixOperationTypeChecker::class, + YieldTypeChecker::class, + + // Types + CallableTypeChecker::class, + NamedTypeChecker::class, + UnionTypeChecker::class, + + // Other + AnnotationCallChecker::class, + ArgumentListChecker::class, + DeprecationChecker::class, + ProtocolChecker::class, + TypeArgumentListChecker::class, + ] +) +class SimpleMLValidator : AbstractSimpleMLValidator() diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/codes/ErrorCode.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/codes/ErrorCode.kt new file mode 100644 index 000000000..32bfec433 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/codes/ErrorCode.kt @@ -0,0 +1,94 @@ +package de.unibonn.simpleml.validation.codes + +enum class ErrorCode { + AttributeMustHaveType, + ParameterMustHaveType, + ResultMustHaveType, + + CLASS_MUST_HAVE_UNIQUE_INHERITED_MEMBERS, + CLASS_MUST_HAVE_UNIQUE_PARENT_TYPES, + CLASS_MUST_INHERIT_ONLY_CLASSES, + CLASS_MUST_NOT_BE_SUBTYPE_OF_ITSELF, + REDECLARATION, + + FileMustDeclarePackage, + StubFileMustNotDeclareWorkflowsOrSteps, + WorkflowFileMustOnlyDeclareWorkflowsAndSteps, + + ANNOTATION_IS_SINGLE_USE, + DEPRECATED_REQUIRED_PARAMETER, + + UNRESOLVED_IMPORTED_NAMESPACE, + WILDCARD_IMPORT_WITH_ALIAS, + + NoRequiredParametersAfterFirstOptionalParameter, + NoMoreParametersAfterVariadicParameter, + NoVariadicParameterAfterOptionalParameter, + + NO_YIELD_IN_WORKFLOW, + + CONTEXT_OF_CALL_WITHOUT_RESULTS, + CONTEXT_OF_CALL_WITH_MANY_RESULTS, + NO_RECURSION, + RECEIVER_MUST_BE_CALLABLE, + CALLED_CLASS_MUST_HAVE_CONSTRUCTOR, + + INSTANCE_METHOD_MUST_BE_CALLED, + ENUM_VARIANT_MUST_BE_INSTANTIATED, + + MISSING_REQUIRED_PARAMETER, + NO_POSITIONAL_ARGUMENTS_AFTER_FIRST_NAMED_ARGUMENT, + TOO_MANY_ARGUMENTS, + TooManyTypeArguments, + UNIQUE_PARAMETERS, + UniqueTypeParameters, + + MISSING_REQUIRED_TYPE_PARAMETER, + NO_POSITIONAL_TYPE_ARGUMENTS_AFTER_FIRST_NAMED_TYPE_ARGUMENT, + + ASSIGNEE_WITHOUT_VALUE, + + MISSING_TYPE_ARGUMENT_LIST, + + UNION_TYPE_WITHOUT_TYPE_ARGUMENTS, + + NON_STATIC_PROPAGATES, + PURE_PROPAGATES, + STATIC_PROPAGATES, + + REDECLARATION_IN_OTHER_FILE, + + MISSING_ARGUMENT_LIST, + WRONG_TARGET, + + OneProtocolPerClass, + OnlyReferenceInstanceMembers, + + MissingTemplateExpression, + + MissingSafeAccess, + + UnassignedResult, + DuplicateResultAssignment, + MustBeConstant, + + MustNotStaticallyReferenceClass, + MustNotStaticallyReferenceEnum, + + DivisionByZero, + + UnsupportedAnnotationParameterType, + + LambdaMustBeTypedArgumentOrYielded, + + NoOptionalParametersInCallableType, + + VariadicParametersMustNotHaveDefaultValue, + VariadicParameterMustNotBeAssignedByName, + + BlockLambdaPrefix, + + WrongType, + + ExpertMustBeOptional +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/codes/InfoCode.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/codes/InfoCode.kt new file mode 100644 index 000000000..30f809ce5 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/codes/InfoCode.kt @@ -0,0 +1,21 @@ +package de.unibonn.simpleml.validation.codes + +enum class InfoCode { + + // Unnecessary syntax that can be simplified + UnnecessaryAssignment, + UnnecessaryArgumentList, + UnnecessaryBody, + UnnecessaryElvisOperator, + UnnecessarySafeAccess, + UnnecessaryParameterList, + UnnecessaryResultList, + UnnecessaryTypeArgumentList, + UnnecessaryTypeParameterList, + UnnecessaryUnionType, + + // Annotation calls + IdenticalPythonModule, + IdenticalPythonName, + PureImpliesNoSideEffects +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/codes/WarningCode.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/codes/WarningCode.kt new file mode 100644 index 000000000..a31b31caa --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/codes/WarningCode.kt @@ -0,0 +1,25 @@ +package de.unibonn.simpleml.validation.codes + +enum class WarningCode { + + // Deprecation + AssignedDeclarationIsDeprecated, + CorrespondingParameterIsDeprecated, + CorrespondingTypeParameterIsDeprecated, + ReferencedDeclarationIsDeprecated, + + // Name conventions + NameShouldBeLowerCamelCase, + NameShouldBeUpperCamelCase, + SegmentsShouldBeLowerCamelCase, + + // Unused declarations + UnusedParameter, + UnusedPlaceholder, + + // Other + DuplicateTarget, + ImplicitlyIgnoredResultOfCall, + PlaceholderIsRenamingOfDeclaration, + StatementDoesNothing, +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/AnnotationChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/AnnotationChecker.kt new file mode 100644 index 000000000..b368dfdca --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/AnnotationChecker.kt @@ -0,0 +1,69 @@ +package de.unibonn.simpleml.validation.declarations + +import com.google.errorprone.annotations.Var +import de.unibonn.simpleml.emf.isConstant +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.staticAnalysis.typing.ClassType +import de.unibonn.simpleml.staticAnalysis.typing.EnumType +import de.unibonn.simpleml.staticAnalysis.typing.VariadicType +import de.unibonn.simpleml.staticAnalysis.typing.type +import de.unibonn.simpleml.stdlibAccess.StdlibClasses +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import de.unibonn.simpleml.validation.codes.InfoCode +import org.eclipse.xtext.validation.Check + +class AnnotationChecker : AbstractSimpleMLChecker() { + + @Check + fun uniqueNames(smlAnnotation: SmlAnnotation) { + smlAnnotation.parametersOrEmpty().reportDuplicateNames { + "A parameter with name '${it.name}' exists already in this annotation." + } + } + + @Check + fun unnecessaryParameterList(smlAnnotation: SmlAnnotation) { + if (smlAnnotation.parameterList != null && smlAnnotation.parametersOrEmpty().isEmpty()) { + info( + "Unnecessary parameter list.", + Literals.SML_ABSTRACT_CALLABLE__PARAMETER_LIST, + InfoCode.UnnecessaryParameterList + ) + } + } + + private val validParameterTypes = setOf( + StdlibClasses.Boolean, + StdlibClasses.Float, + StdlibClasses.Int, + StdlibClasses.String, + ) + + @Check + fun parameterTypes(smlAnnotation: SmlAnnotation) { + smlAnnotation.parametersOrEmpty().forEach { + val unwrappedParameterType = when (val parameterType = it.type()) { + is VariadicType -> parameterType.elementType + else -> parameterType + } + + val isValid = when (unwrappedParameterType) { + is ClassType -> unwrappedParameterType.qualifiedName in validParameterTypes + is EnumType -> unwrappedParameterType.smlEnum.isConstant() + else -> false + } + + if (!isValid) { + error( + "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum.", + it, + Literals.SML_PARAMETER__TYPE, + ErrorCode.UnsupportedAnnotationParameterType + ) + } + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/AttributeChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/AttributeChecker.kt new file mode 100644 index 000000000..aab3eeae0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/AttributeChecker.kt @@ -0,0 +1,21 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check + +class AttributeChecker : AbstractSimpleMLChecker() { + + @Check + fun type(smlAttribute: SmlAttribute) { + if (smlAttribute.type == null) { + error( + "An attribute must have a type.", + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.AttributeMustHaveType + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ClassChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ClassChecker.kt new file mode 100644 index 000000000..413edba39 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ClassChecker.kt @@ -0,0 +1,132 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.emf.classMembersOrEmpty +import de.unibonn.simpleml.emf.objectsInBodyOrEmpty +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.parentTypesOrEmpty +import de.unibonn.simpleml.emf.protocolsOrEmpty +import de.unibonn.simpleml.emf.typeParametersOrEmpty +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.staticAnalysis.classHierarchy.inheritedNonStaticMembersOrEmpty +import de.unibonn.simpleml.staticAnalysis.classHierarchy.isSubtypeOf +import de.unibonn.simpleml.staticAnalysis.typing.ClassType +import de.unibonn.simpleml.staticAnalysis.typing.UnresolvedType +import de.unibonn.simpleml.staticAnalysis.typing.type +import de.unibonn.simpleml.utils.duplicatesBy +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import de.unibonn.simpleml.validation.codes.InfoCode +import org.eclipse.xtext.validation.Check + +class ClassChecker : AbstractSimpleMLChecker() { + + @Check + fun acyclicSuperTypes(smlClass: SmlClass) { + smlClass.parentTypesOrEmpty() + .filter { + val resolvedClass = (it.type() as? ClassType)?.smlClass + resolvedClass != null && resolvedClass.isSubtypeOf(smlClass) + } + .forEach { + error( + "A class must not directly or indirectly be a subtype of itself.", + it, + null, + ErrorCode.CLASS_MUST_NOT_BE_SUBTYPE_OF_ITSELF + ) + } + } + + @Check + fun body(smlClass: SmlClass) { + if (smlClass.body != null && smlClass.objectsInBodyOrEmpty().isEmpty()) { + info( + "Unnecessary class body.", + Literals.SML_CLASS__BODY, + InfoCode.UnnecessaryBody + ) + } + } + + @Check + fun mustInheritOnlyClasses(smlClass: SmlClass) { + smlClass.parentTypesOrEmpty() + .filterNot { + val type = it.type() + type is ClassType || type is UnresolvedType + } + .forEach { + error( + "A class must only inherit classes.", + it, + null, + ErrorCode.CLASS_MUST_INHERIT_ONLY_CLASSES + ) + } + } + + @Check + fun mustHaveUniqueInheritedMembers(smlClass: SmlClass) { + smlClass.inheritedNonStaticMembersOrEmpty() + .groupBy { it.name } + .forEach { (name, declarationsWithName) -> + if (declarationsWithName.size > 1) { + error( + "Inherits multiple members called '$name'.", + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.CLASS_MUST_HAVE_UNIQUE_INHERITED_MEMBERS + ) + } + } + } + + @Check + fun uniqueNames(smlClass: SmlClass) { + smlClass.parametersOrEmpty() + .reportDuplicateNames { "A parameter with name '${it.name}' exists already in this class." } + + smlClass.classMembersOrEmpty() + .reportDuplicateNames { "A declaration with name '${it.name}' exists already in this class." } + } + + @Check + fun uniqueParentTypes(smlClass: SmlClass) { + smlClass.parentTypesOrEmpty() + .duplicatesBy { (it.type() as? ClassType)?.smlClass } + .forEach { + error( + "Parent types must be unique.", + it, + null, + ErrorCode.CLASS_MUST_HAVE_UNIQUE_PARENT_TYPES + ) + } + } + + @Check + fun unnecessaryTypeParameterList(smlClass: SmlClass) { + if (smlClass.typeParameterList != null && smlClass.typeParametersOrEmpty().isEmpty()) { + info( + "Unnecessary type parameter list.", + Literals.SML_CLASS__TYPE_PARAMETER_LIST, + InfoCode.UnnecessaryTypeParameterList + ) + } + } + + @Check + fun multipleProtocols(smlClass: SmlClass) { + val protocols = smlClass.protocolsOrEmpty() + if (protocols.size > 1) { + protocols.forEach { + error( + "A class must have only one protocol.", + it, + null, + ErrorCode.OneProtocolPerClass + ) + } + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/CompilationUnitChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/CompilationUnitChecker.kt new file mode 100644 index 000000000..b9d3053f0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/CompilationUnitChecker.kt @@ -0,0 +1,131 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.constant.isInStubFile +import de.unibonn.simpleml.constant.isInTestFile +import de.unibonn.simpleml.emf.compilationUnitMembersOrEmpty +import de.unibonn.simpleml.emf.importedNameOrNull +import de.unibonn.simpleml.emf.isQualified +import de.unibonn.simpleml.naming.qualifiedNameOrNull +import de.unibonn.simpleml.scoping.externalGlobalDeclarations +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlImport +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlWorkflow +import de.unibonn.simpleml.utils.duplicatesBy +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check +import org.eclipse.xtext.validation.CheckType + +class CompilationUnitChecker : AbstractSimpleMLChecker() { + + @Check + fun members(smlCompilationUnit: SmlCompilationUnit) { + if (smlCompilationUnit.isInStubFile()) { + smlCompilationUnit.compilationUnitMembersOrEmpty() + .filter { it is SmlWorkflow || it is SmlStep } + .forEach { + error( + "A stub file must not declare workflows or steps.", + it, + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.StubFileMustNotDeclareWorkflowsOrSteps + ) + } + } else if (!smlCompilationUnit.isInTestFile()) { + smlCompilationUnit.compilationUnitMembersOrEmpty() + .filter { it !is SmlWorkflow && it !is SmlStep } + .forEach { + error( + "A workflow file must only declare workflows and steps.", + it, + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.WorkflowFileMustOnlyDeclareWorkflowsAndSteps + ) + } + } + } + + @Check + fun uniquePackageDeclaration(smlCompilationUnit: SmlCompilationUnit) { + if (smlCompilationUnit.isInTestFile()) { + return + } + + if (smlCompilationUnit.name == null) { + smlCompilationUnit.compilationUnitMembersOrEmpty().firstOrNull()?.let { + error( + "A file with declarations must declare its package.", + it, + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.FileMustDeclarePackage + ) + } + } + } + + @Check + fun uniqueNames(smlCompilationUnit: SmlCompilationUnit) { + val namedEObjects = smlCompilationUnit.imports.filter { it.isQualified() } + smlCompilationUnit.members + + namedEObjects.duplicatesBy { + when (it) { + is SmlImport -> it.importedNameOrNull() + is SmlAbstractDeclaration -> it.name + else -> null + } + }.forEach { + when { + it is SmlImport && it.alias == null -> { + error( + "A declaration with name '${it.importedNameOrNull()}' exists already in this file.", + it, + Literals.SML_IMPORT__IMPORTED_NAMESPACE, + ErrorCode.REDECLARATION + ) + } + it is SmlImport && it.alias != null -> { + error( + "A declaration with name '${it.importedNameOrNull()}' exists already in this file.", + it.alias, + Literals.SML_IMPORT_ALIAS__NAME, + ErrorCode.REDECLARATION + ) + } + it is SmlAbstractDeclaration -> { + error( + "A declaration with name '${it.name}' exists already in this file.", + it, + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.REDECLARATION + ) + } + } + } + } + + @Check(CheckType.NORMAL) + fun uniqueNamesAcrossFiles(smlCompilationUnit: SmlCompilationUnit) { + + // Since the stdlib is automatically loaded into a workspace, every declaration would be marked as a duplicate + // when editing the stdlib + if (smlCompilationUnit.isInStubFile() && smlCompilationUnit.name.startsWith("simpleml")) { + return + } + + val externalGlobalDeclarations = smlCompilationUnit.externalGlobalDeclarations() + smlCompilationUnit.compilationUnitMembersOrEmpty().forEach { member -> + val qualifiedName = member.qualifiedNameOrNull() + if (externalGlobalDeclarations.any { it.qualifiedName == qualifiedName }) { + error( + "A declaration with qualified name '$qualifiedName' exists already.", + member, + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.REDECLARATION_IN_OTHER_FILE + ) + } + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/DeclarationChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/DeclarationChecker.kt new file mode 100644 index 000000000..ea962a4b2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/DeclarationChecker.kt @@ -0,0 +1,49 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.emf.annotationCallsOrEmpty +import de.unibonn.simpleml.emf.isRequired +import de.unibonn.simpleml.naming.qualifiedNameOrNull +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.stdlibAccess.StdlibAnnotations +import de.unibonn.simpleml.stdlibAccess.isRepeatable +import de.unibonn.simpleml.utils.duplicatesBy +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check + +class DeclarationChecker : AbstractSimpleMLChecker() { + + @Check + fun annotationCardinality(smlDeclaration: SmlAbstractDeclaration) { + smlDeclaration.annotationCallsOrEmpty() + .filter { it.annotation != null && !it.annotation.eIsProxy() && !it.annotation.isRepeatable() } + .duplicatesBy { it.annotation.qualifiedNameOrNull() } + .forEach { + error( + "This annotation can only be used once.", + it, + null, + ErrorCode.ANNOTATION_IS_SINGLE_USE + ) + } + } + + @Check + fun mustNotDeprecateRequiredParameter(smlParameter: SmlParameter) { + if (smlParameter.isRequired()) { + val deprecatedAnnotationOrNull = smlParameter.annotationCallsOrEmpty().firstOrNull { + it.annotation.qualifiedNameOrNull() == StdlibAnnotations.Deprecated + } + + if (deprecatedAnnotationOrNull != null) { + error( + "A required parameter cannot be deprecated.", + deprecatedAnnotationOrNull, + null, + ErrorCode.DEPRECATED_REQUIRED_PARAMETER + ) + } + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/EnumChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/EnumChecker.kt new file mode 100644 index 000000000..41cc4b32a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/EnumChecker.kt @@ -0,0 +1,28 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.emf.variantsOrEmpty +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.InfoCode +import org.eclipse.xtext.validation.Check + +class EnumChecker : AbstractSimpleMLChecker() { + + @Check + fun body(smlEnum: SmlEnum) { + if (smlEnum.body != null && smlEnum.variantsOrEmpty().isEmpty()) { + info( + "Unnecessary enum body.", + Literals.SML_ENUM__BODY, + InfoCode.UnnecessaryBody + ) + } + } + + @Check + fun uniqueNames(smlEnum: SmlEnum) { + smlEnum.variantsOrEmpty() + .reportDuplicateNames { "A declaration with name '${it.name}' exists already in this enum." } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/EnumVariantChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/EnumVariantChecker.kt new file mode 100644 index 000000000..a30e95538 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/EnumVariantChecker.kt @@ -0,0 +1,40 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.typeParametersOrEmpty +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.InfoCode +import org.eclipse.xtext.validation.Check + +class EnumVariantChecker : AbstractSimpleMLChecker() { + + @Check + fun typeParameterList(smlEnumVariant: SmlEnumVariant) { + if (smlEnumVariant.typeParameterList != null && smlEnumVariant.typeParametersOrEmpty().isEmpty()) { + info( + "Unnecessary type parameter list.", + Literals.SML_ENUM_VARIANT__TYPE_PARAMETER_LIST, + InfoCode.UnnecessaryTypeParameterList + ) + } + } + + @Check + fun parameterList(smlEnumVariant: SmlEnumVariant) { + if (smlEnumVariant.parameterList != null && smlEnumVariant.parametersOrEmpty().isEmpty()) { + info( + "Unnecessary parameter list.", + Literals.SML_ABSTRACT_CALLABLE__PARAMETER_LIST, + InfoCode.UnnecessaryParameterList + ) + } + } + + @Check + fun uniqueNames(smlEnumVariant: SmlEnumVariant) { + smlEnumVariant.parametersOrEmpty() + .reportDuplicateNames { "A parameter with name '${it.name}' exists already in this enum variant." } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/FunctionChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/FunctionChecker.kt new file mode 100644 index 000000000..9b83a3fe8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/FunctionChecker.kt @@ -0,0 +1,88 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.resultsOrEmpty +import de.unibonn.simpleml.emf.typeParametersOrEmpty +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.staticAnalysis.classHierarchy.hiddenFunction +import de.unibonn.simpleml.stdlibAccess.isPure +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import de.unibonn.simpleml.validation.codes.InfoCode +import org.eclipse.xtext.validation.Check + +class FunctionChecker : AbstractSimpleMLChecker() { + + @Check + fun nonStaticPropagates(smlFunction: SmlFunction) { + if (smlFunction.isStatic) { + val hiddenFunction = smlFunction.hiddenFunction() + if (hiddenFunction != null && !hiddenFunction.isStatic) { + error( + "One of the supertypes of this class declares a non-static function with this name, so this must be non-static as well.", + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.NON_STATIC_PROPAGATES + ) + } + } + } + + @Check + fun purePropagates(smlFunction: SmlFunction) { + if (!smlFunction.isPure()) { + val hiddenFunction = smlFunction.hiddenFunction() + if (hiddenFunction != null && hiddenFunction.isPure()) { + error( + "One of the supertypes of this class declares a pure function with this name, so this must be pure as well.", + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.PURE_PROPAGATES + ) + } + } + } + + @Check + fun staticPropagates(smlFunction: SmlFunction) { + if (!smlFunction.isStatic) { + val hiddenFunction = smlFunction.hiddenFunction() + if (hiddenFunction != null && hiddenFunction.isStatic) { + error( + "One of the supertypes of this class declares a static function with this name, so this must be static as well.", + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.STATIC_PROPAGATES + ) + } + } + } + + @Check + fun uniqueNames(smlFunction: SmlFunction) { + val declarations = smlFunction.parametersOrEmpty() + smlFunction.resultsOrEmpty() + declarations.reportDuplicateNames { + "A parameter or result with name '${it.name}' exists already in this function." + } + } + + @Check + fun unnecessaryResultList(smlFunction: SmlFunction) { + if (smlFunction.resultList != null && smlFunction.resultsOrEmpty().isEmpty()) { + info( + "Unnecessary result list.", + Literals.SML_FUNCTION__RESULT_LIST, + InfoCode.UnnecessaryResultList + ) + } + } + + @Check + fun unnecessaryTypeParameterList(smlFunction: SmlFunction) { + if (smlFunction.typeParameterList != null && smlFunction.typeParametersOrEmpty().isEmpty()) { + info( + "Unnecessary type parameter list.", + Literals.SML_FUNCTION__TYPE_PARAMETER_LIST, + InfoCode.UnnecessaryTypeParameterList + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ImportChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ImportChecker.kt new file mode 100644 index 000000000..643ff6663 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ImportChecker.kt @@ -0,0 +1,64 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.emf.aliasNameOrNull +import de.unibonn.simpleml.emf.isQualified +import de.unibonn.simpleml.emf.isWildcard +import de.unibonn.simpleml.scoping.allGlobalDeclarations +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlImport +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.naming.QualifiedName +import org.eclipse.xtext.validation.Check +import org.eclipse.xtext.validation.CheckType + +class ImportChecker : AbstractSimpleMLChecker() { + + @Check(CheckType.NORMAL) + fun unresolvedNamespace(smlImport: SmlImport) { + if (smlImport.isQualified()) { + val importedNamespace = QualifiedName.create( + smlImport.importedNamespace.split(".") + ) + + val isUnresolved = smlImport + .allGlobalDeclarations() + .none { it.qualifiedName == importedNamespace } + + if (isUnresolved) { + error( + "No declaration with qualified name '$importedNamespace' exists.", + Literals.SML_IMPORT__IMPORTED_NAMESPACE, + ErrorCode.UNRESOLVED_IMPORTED_NAMESPACE + ) + } + } else { + val importedNamespace = QualifiedName.create( + smlImport.importedNamespace.removeSuffix(".*").split(".") + ) + + val isUnresolved = smlImport + .allGlobalDeclarations() + .none { it.qualifiedName.startsWith(importedNamespace) } + + if (isUnresolved) { + error( + "No package with qualified name '$importedNamespace' exists.", + Literals.SML_IMPORT__IMPORTED_NAMESPACE, + ErrorCode.UNRESOLVED_IMPORTED_NAMESPACE + ) + } + } + } + + @Check + fun wildcardImportWithAlias(smlImport: SmlImport) { + if (smlImport.isWildcard() && smlImport.aliasNameOrNull() != null) { + error( + "A wildcard import must not have an alias.", + Literals.SML_IMPORT__ALIAS, + ErrorCode.WILDCARD_IMPORT_WITH_ALIAS + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/NameConventionChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/NameConventionChecker.kt new file mode 100644 index 000000000..9786f8e6f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/NameConventionChecker.kt @@ -0,0 +1,150 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.simpleML.SmlBlockLambdaResult +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlProtocolSubterm +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlTypeParameter +import de.unibonn.simpleml.simpleML.SmlWorkflow +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import de.unibonn.simpleml.validation.codes.WarningCode +import org.eclipse.xtext.validation.Check + +class NameConventionChecker : AbstractSimpleMLChecker() { + + @Check + fun segmentsShouldBeLowercase(smlCompilationUnit: SmlCompilationUnit) { + val hasInvalidSegments = smlCompilationUnit.name + .split('.') + .any { !it.isLowerCamelCase() } + + if (hasInvalidSegments) { + warning( + "All segments of the qualified name of a package should be lowerCamelCase.", + Literals.SML_ABSTRACT_DECLARATION__NAME, + WarningCode.SegmentsShouldBeLowerCamelCase + ) + } + } + + @Check + fun blockLambdaPrefix(smlDeclaration: SmlAbstractDeclaration) { + if (smlDeclaration.name.startsWith("__block_lambda_")) { + error( + "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas.", + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.BlockLambdaPrefix + ) + } + } + + @Check + fun annotationNamesShouldBeUpperCamelCase(smlAnnotation: SmlAnnotation) { + smlAnnotation.nameShouldBeUpperCamelCase("annotations") + } + + @Check + fun attributeNamesShouldBeLowerCamelCase(smlAttribute: SmlAttribute) { + smlAttribute.nameShouldBeLowerCamelCase("attributes") + } + + @Check + fun classNamesShouldBeUpperCamelCase(smlClass: SmlClass) { + smlClass.nameShouldBeUpperCamelCase("classes") + } + + @Check + fun enumNamesShouldBeUpperCamelCase(smlEnum: SmlEnum) { + smlEnum.nameShouldBeUpperCamelCase("enums") + } + + @Check + fun enumVariantNamesShouldBeUpperCamelCase(smlEnumVariant: SmlEnumVariant) { + smlEnumVariant.nameShouldBeUpperCamelCase("enum variants") + } + + @Check + fun functionNamesShouldBeLowerCamelCase(smlFunction: SmlFunction) { + smlFunction.nameShouldBeLowerCamelCase("functions") + } + + @Check + fun lambdaResultNamesShouldBeLowerCamelCase(smlBlockLambdaResult: SmlBlockLambdaResult) { + smlBlockLambdaResult.nameShouldBeLowerCamelCase("lambda results") + } + + @Check + fun parameterNamesShouldBeLowerCamelCase(smlParameter: SmlParameter) { + smlParameter.nameShouldBeLowerCamelCase("parameters") + } + + @Check + fun placeholderNamesShouldBeLowerCamelCase(smlPlaceholder: SmlPlaceholder) { + smlPlaceholder.nameShouldBeLowerCamelCase("placeholders") + } + + @Check + fun protocolSubtermNamesShouldBeLowerCamelCase(smlProtocolSubterm: SmlProtocolSubterm) { + smlProtocolSubterm.nameShouldBeLowerCamelCase("protocol subterms") + } + + @Check + fun resultNamesShouldBeLowerCamelCase(smlResult: SmlResult) { + smlResult.nameShouldBeLowerCamelCase("results") + } + + @Check + fun stepNamesShouldBeLowerCamelCase(smlStep: SmlStep) { + smlStep.nameShouldBeLowerCamelCase("steps") + } + + @Check + fun typeParameterNamesShouldBeUpperCamelCase(smlTypeParameter: SmlTypeParameter) { + smlTypeParameter.nameShouldBeUpperCamelCase("type parameters") + } + + @Check + fun workflowNamesShouldBeLowerCamelCase(smlWorkflow: SmlWorkflow) { + smlWorkflow.nameShouldBeLowerCamelCase("workflows") + } + + private fun SmlAbstractDeclaration.nameShouldBeUpperCamelCase(declarationType: String) { + if (this.name != null && !this.name.isUpperCamelCase()) { + warning( + "Names of $declarationType should be UpperCamelCase.", + Literals.SML_ABSTRACT_DECLARATION__NAME, + WarningCode.NameShouldBeUpperCamelCase + ) + } + } + + private fun SmlAbstractDeclaration.nameShouldBeLowerCamelCase(declarationType: String) { + if (this.name != null && !this.name.isLowerCamelCase()) { + warning( + "Names of $declarationType should be lowerCamelCase.", + Literals.SML_ABSTRACT_DECLARATION__NAME, + WarningCode.NameShouldBeLowerCamelCase + ) + } + } + + private fun String.isUpperCamelCase(): Boolean { + return Regex("^[A-Z][a-zA-Z0-9]*$").matches(this) + } + + private fun String.isLowerCamelCase(): Boolean { + return Regex("^[a-z][a-zA-Z0-9]*$").matches(this) + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ParameterChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ParameterChecker.kt new file mode 100644 index 000000000..ba09a2959 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ParameterChecker.kt @@ -0,0 +1,69 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.emf.closestAncestorOrNull +import de.unibonn.simpleml.emf.isOptional +import de.unibonn.simpleml.emf.isRequired +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlAbstractLambda +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlParameterList +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.toConstantExpressionOrNull +import de.unibonn.simpleml.stdlibAccess.StdlibAnnotations +import de.unibonn.simpleml.stdlibAccess.annotationCallsOrEmpty +import de.unibonn.simpleml.stdlibAccess.isExpert +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check +import org.eclipse.xtext.validation.CheckType + +class ParameterChecker : AbstractSimpleMLChecker() { + + @Check + fun type(smlParameter: SmlParameter) { + val parameterList = smlParameter.closestAncestorOrNull() ?: return + if (smlParameter.type == null && parameterList.eContainer() !is SmlAbstractLambda) { + error( + "A parameter must have a type.", + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.ParameterMustHaveType + ) + } + } + + @Check(CheckType.NORMAL) + fun defaultValueMustBeConstant(smlParameter: SmlParameter) { + val defaultValue = smlParameter.defaultValue ?: return + if (defaultValue.toConstantExpressionOrNull() == null) { + error( + "Default values of parameters must be constant.", + Literals.SML_PARAMETER__DEFAULT_VALUE, + ErrorCode.MustBeConstant + ) + } + } + + @Check + fun variadicParametersMustHaveNoDefaultValue(smlParameter: SmlParameter) { + if (smlParameter.isVariadic && smlParameter.isOptional()) { + error( + "Variadic parameters must not have default values.", + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.VariadicParametersMustNotHaveDefaultValue + ) + } + } + + @Check + fun expertMustBeOptional(smlParameter: SmlParameter) { + if (smlParameter.isRequired() && smlParameter.isExpert()) { + smlParameter.annotationCallsOrEmpty(StdlibAnnotations.Expert).forEach { + error( + "An expert parameter must be optional.", + it, + null, + ErrorCode.MustBeConstant + ) + } + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ParameterListChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ParameterListChecker.kt new file mode 100644 index 000000000..badbbad6c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ParameterListChecker.kt @@ -0,0 +1,59 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.emf.isOptional +import de.unibonn.simpleml.emf.isRequired +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlParameterList +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check + +class ParameterListChecker : AbstractSimpleMLChecker() { + + @Check + fun noRequiredOrVariadicParametersAfterFirstOptionalParameter(smlParameterList: SmlParameterList) { + val firstOptionalParameterIndex = smlParameterList.parameters.indexOfFirst { it.isOptional() } + if (firstOptionalParameterIndex == -1) { + return + } + + smlParameterList.parameters + .drop(firstOptionalParameterIndex + 1) + .forEach { + if (it.isRequired()) { + error( + "After the first optional parameter all parameters must be optional.", + it, + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.NoRequiredParametersAfterFirstOptionalParameter + ) + } else if (it.isVariadic) { + error( + "A callable with optional parameters must not have a variadic parameter.", + it, + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.NoVariadicParameterAfterOptionalParameter + ) + } + } + } + + @Check + fun noMoreParametersAfterVariadic(smlParameterList: SmlParameterList) { + val firstVariadicParameterIndex = smlParameterList.parameters.indexOfFirst { it.isVariadic } + if (firstVariadicParameterIndex == -1) { + return + } + + smlParameterList.parameters + .drop(firstVariadicParameterIndex + 1) + .forEach { + error( + "After a variadic parameter no more parameters must be specified.", + it, + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.NoMoreParametersAfterVariadicParameter + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/PlaceholderChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/PlaceholderChecker.kt new file mode 100644 index 000000000..dd9395b64 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/PlaceholderChecker.kt @@ -0,0 +1,47 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.emf.closestAncestorOrNull +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlBlock +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlReference +import de.unibonn.simpleml.staticAnalysis.assignedOrNull +import de.unibonn.simpleml.staticAnalysis.usesIn +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.WarningCode +import org.eclipse.xtext.validation.Check + +class PlaceholderChecker : AbstractSimpleMLChecker() { + + @Check + fun renamingOfDeclaration(smlPlaceholder: SmlPlaceholder) { + val assigned = smlPlaceholder.assignedOrNull() + if (assigned is SmlReference) { + val declaration = assigned.declaration + if (declaration is SmlClass || declaration is SmlEnum || declaration is SmlFunction || declaration is SmlParameter || declaration is SmlPlaceholder) + warning( + "This placeholder only provides another name for a declaration.", + Literals.SML_ABSTRACT_DECLARATION__NAME, + WarningCode.PlaceholderIsRenamingOfDeclaration + ) + } + } + + @Check + fun unused(smlPlaceholder: SmlPlaceholder) { + val block = smlPlaceholder.closestAncestorOrNull() ?: return + val assignment = smlPlaceholder.closestAncestorOrNull() ?: return + if (assignment != block.statements.lastOrNull() && smlPlaceholder.usesIn(block).none()) { + warning( + "This placeholder is unused.", + Literals.SML_ABSTRACT_DECLARATION__NAME, + WarningCode.UnusedPlaceholder + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ResultChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ResultChecker.kt new file mode 100644 index 000000000..56f8b8ffd --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/ResultChecker.kt @@ -0,0 +1,21 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check + +class ResultChecker : AbstractSimpleMLChecker() { + + @Check + fun type(smlResult: SmlResult) { + if (smlResult.type == null) { + error( + "A result must have a type.", + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.ResultMustHaveType + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/StepChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/StepChecker.kt new file mode 100644 index 000000000..a008b582a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/StepChecker.kt @@ -0,0 +1,82 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.emf.asResolvedOrNull +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.placeholdersOrEmpty +import de.unibonn.simpleml.emf.resultsOrEmpty +import de.unibonn.simpleml.emf.yieldsOrEmpty +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.staticAnalysis.linking.yieldsOrEmpty +import de.unibonn.simpleml.staticAnalysis.usesIn +import de.unibonn.simpleml.utils.duplicatesBy +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import de.unibonn.simpleml.validation.codes.InfoCode +import de.unibonn.simpleml.validation.codes.WarningCode +import org.eclipse.xtext.validation.Check + +class StepChecker : AbstractSimpleMLChecker() { + + @Check + fun parameterIsUnused(smlStep: SmlStep) { + smlStep.parametersOrEmpty() + .filter { it.usesIn(smlStep).none() } + .forEach { + warning( + "This parameter is unused.", + it, + Literals.SML_ABSTRACT_DECLARATION__NAME, + WarningCode.UnusedParameter + ) + } + } + + @Check + fun uniqueNames(smlStep: SmlStep) { + val declarations = + smlStep.parametersOrEmpty() + smlStep.resultsOrEmpty() + smlStep.placeholdersOrEmpty() + declarations.reportDuplicateNames { + "A parameter, result or placeholder with name '${it.name}' exists already in this step." + } + } + + @Check + fun unnecessaryResultList(smlStep: SmlStep) { + if (smlStep.resultList != null && smlStep.resultsOrEmpty().isEmpty()) { + info( + "Unnecessary result list.", + Literals.SML_STEP__RESULT_LIST, + InfoCode.UnnecessaryResultList + ) + } + } + + @Check + fun unassignedResult(smlStep: SmlStep) { + smlStep.resultsOrEmpty().forEach { + if (it.yieldsOrEmpty().isEmpty()) { + error( + "No value is assigned to this result.", + it, + Literals.SML_ABSTRACT_DECLARATION__NAME, + ErrorCode.UnassignedResult + ) + } + } + } + + @Check + fun duplicateResultAssignment(smlStep: SmlStep) { + smlStep.yieldsOrEmpty() + .duplicatesBy { it.result.asResolvedOrNull() } + .forEach { + error( + "This result is assigned multiple times.", + it, + Literals.SML_YIELD__RESULT, + ErrorCode.DuplicateResultAssignment + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/WorkflowChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/WorkflowChecker.kt new file mode 100644 index 000000000..d028e5d36 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/declarations/WorkflowChecker.kt @@ -0,0 +1,34 @@ +package de.unibonn.simpleml.validation.declarations + +import de.unibonn.simpleml.emf.placeholdersOrEmpty +import de.unibonn.simpleml.emf.statementsOrEmpty +import de.unibonn.simpleml.emf.yieldsOrEmpty +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlWorkflow +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check + +class WorkflowChecker : AbstractSimpleMLChecker() { + + @Check + fun noYield(smlWorkflow: SmlWorkflow) { + smlWorkflow.statementsOrEmpty() + .filterIsInstance() + .flatMap { it.yieldsOrEmpty() } + .forEach { + error( + "Yield must not be used in a workflow.", + it, + null, + ErrorCode.NO_YIELD_IN_WORKFLOW + ) + } + } + + @Check + fun uniqueNames(smlWorkflow: SmlWorkflow) { + smlWorkflow.placeholdersOrEmpty() + .reportDuplicateNames { "A declaration with name '${it.name}' exists already in this workflow." } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/ArgumentChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/ArgumentChecker.kt new file mode 100644 index 000000000..da6857f17 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/ArgumentChecker.kt @@ -0,0 +1,39 @@ +package de.unibonn.simpleml.validation.expressions + +import de.unibonn.simpleml.emf.isNamed +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.staticAnalysis.linking.parameterOrNull +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.toConstantExpressionOrNull +import de.unibonn.simpleml.stdlibAccess.isConstant +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check +import org.eclipse.xtext.validation.CheckType + +class ArgumentChecker : AbstractSimpleMLChecker() { + + @Check(CheckType.NORMAL) + fun argumentMustBeConstant(smlArgument: SmlArgument) { + val parameterIsConstant = smlArgument.parameterOrNull()?.isConstant() ?: false + + if (parameterIsConstant && smlArgument.value?.toConstantExpressionOrNull() == null) { + error( + "Arguments assigned to constant parameters must be constant.", + Literals.SML_ARGUMENT__VALUE, + ErrorCode.MustBeConstant + ) + } + } + + @Check + fun variadicParameterMustNotBeAssignedByName(smlArgument: SmlArgument) { + if (smlArgument.isNamed() && (smlArgument.parameterOrNull()?.isVariadic == true)) { + error( + "A variadic parameter must not be assigned by name.", + Literals.SML_ARGUMENT__PARAMETER, + ErrorCode.VariadicParameterMustNotBeAssignedByName + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/CallChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/CallChecker.kt new file mode 100644 index 000000000..c748371bf --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/CallChecker.kt @@ -0,0 +1,153 @@ +package de.unibonn.simpleml.validation.expressions + +import de.unibonn.simpleml.emf.argumentsOrEmpty +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.typeArgumentsOrEmpty +import de.unibonn.simpleml.emf.typeParametersOrEmpty +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlExpressionStatement +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlMemberAccess +import de.unibonn.simpleml.staticAnalysis.CallableResult +import de.unibonn.simpleml.staticAnalysis.callableOrNull +import de.unibonn.simpleml.staticAnalysis.isRecursive +import de.unibonn.simpleml.staticAnalysis.maybeCallable +import de.unibonn.simpleml.staticAnalysis.resultsOrNull +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import de.unibonn.simpleml.validation.codes.InfoCode +import org.eclipse.xtext.validation.Check + +class CallChecker : AbstractSimpleMLChecker() { + + @Check + fun missingTypeArgumentList(smlCall: SmlCall) { + if (smlCall.typeArgumentList != null) { + return + } + + val typeParameters = when (val callable = smlCall.callableOrNull()) { + is SmlClass -> callable.typeParametersOrEmpty() + is SmlEnumVariant -> callable.typeParametersOrEmpty() + is SmlFunction -> callable.typeParametersOrEmpty() + else -> return + } + + if (typeParameters.isNotEmpty()) { + error( + "Missing type argument list.", + Literals.SML_ABSTRACT_CHAINED_EXPRESSION__RECEIVER, + ErrorCode.MISSING_TYPE_ARGUMENT_LIST + ) + } + } + + @Check + fun context(smlCall: SmlCall) { + val results = smlCall.resultsOrNull() ?: return + val source = when (smlCall.receiver) { + is SmlMemberAccess -> smlCall.receiver + else -> smlCall + } + val feature = when (smlCall.receiver) { + is SmlMemberAccess -> Literals.SML_MEMBER_ACCESS__MEMBER + else -> Literals.SML_ABSTRACT_CHAINED_EXPRESSION__RECEIVER + } + + if (results.isEmpty() && !smlCall.hasValidContextForCallWithoutResults()) { + error( + "A call that produces no results is not allowed in this context.", + source, + feature, + ErrorCode.CONTEXT_OF_CALL_WITHOUT_RESULTS + ) + } else if (results.size > 1 && !smlCall.hasValidContextForCallWithMultipleResults()) { + error( + "A call that produces multiple results is not allowed in this context.", + source, + feature, + ErrorCode.CONTEXT_OF_CALL_WITH_MANY_RESULTS + ) + } + } + + private fun SmlCall.hasValidContextForCallWithoutResults(): Boolean { + val context = this.eContainer() + return context is SmlExpressionStatement + } + + private fun SmlCall.hasValidContextForCallWithMultipleResults(): Boolean { + val context = this.eContainer() + return context is SmlAssignment || context is SmlExpressionStatement || context is SmlMemberAccess + } + + @Check + fun recursion(smlCall: SmlCall) { + if (smlCall.isRecursive()) { + error( + "Recursive calls are not allowed.", + Literals.SML_ABSTRACT_CHAINED_EXPRESSION__RECEIVER, + ErrorCode.NO_RECURSION + ) + } + } + + @Check + fun receiver(smlCall: SmlCall) { + when (val maybeCallable = smlCall.maybeCallable()) { + CallableResult.NotCallable -> { + error( + "This expression must not be called.", + Literals.SML_ABSTRACT_CHAINED_EXPRESSION__RECEIVER, + ErrorCode.RECEIVER_MUST_BE_CALLABLE + ) + } + is CallableResult.Callable -> { + val callable = maybeCallable.callable + if (callable is SmlClass && callable.parameterList == null) { + error( + "Cannot create an instance of a class that has no constructor.", + Literals.SML_ABSTRACT_CHAINED_EXPRESSION__RECEIVER, + ErrorCode.CALLED_CLASS_MUST_HAVE_CONSTRUCTOR + ) + } + } + else -> {} + } + } + + @Check + fun unnecessaryArgumentList(smlCall: SmlCall) { + + // Call has no argument list anyway + if (smlCall.argumentList == null) { + return + } + + // Call is used to pass type arguments or arguments + if (smlCall.typeArgumentsOrEmpty().isNotEmpty() || smlCall.argumentsOrEmpty().isNotEmpty()) { + return + } + + // Receiver is not callable or cannot be resolved + val callable = smlCall.callableOrNull() ?: return + + // Only calls to enum variants can sometimes be omitted without changing the meaning of the program + if (callable !is SmlEnumVariant) { + return + } + + // This enum variant does not need to be called + if (callable.typeParametersOrEmpty().isEmpty() && callable.parametersOrEmpty().isEmpty()) { + info( + "Unnecessary argument list.", + Literals.SML_ABSTRACT_CALL__ARGUMENT_LIST, + InfoCode.UnnecessaryArgumentList + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/InfixOperationChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/InfixOperationChecker.kt new file mode 100644 index 000000000..da0e93955 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/InfixOperationChecker.kt @@ -0,0 +1,77 @@ +package de.unibonn.simpleml.validation.expressions + +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.By +import de.unibonn.simpleml.constant.SmlInfixOperationOperator.Elvis +import de.unibonn.simpleml.simpleML.SmlInfixOperation +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.SmlConstantFloat +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.SmlConstantInt +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.SmlConstantNull +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.toConstantExpressionOrNull +import de.unibonn.simpleml.staticAnalysis.typing.NamedType +import de.unibonn.simpleml.staticAnalysis.typing.type +import de.unibonn.simpleml.stdlibAccess.StdlibClasses +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import de.unibonn.simpleml.validation.codes.InfoCode +import org.eclipse.xtext.validation.Check + +class InfixOperationChecker : AbstractSimpleMLChecker() { + + @Check + fun dispatchCheckInfixOperation(smlInfixOperation: SmlInfixOperation) { + when (smlInfixOperation.operator) { + By.operator -> checkByOperator(smlInfixOperation) + Elvis.operator -> checkElvisOperator(smlInfixOperation) + } + } + + private fun checkByOperator(smlInfixOperation: SmlInfixOperation) { + val leftType = smlInfixOperation.leftOperand.type() + if (!(leftType is NamedType && leftType.qualifiedName in setOf(StdlibClasses.Float, StdlibClasses.Int))) { + return + } + + val rightValue = smlInfixOperation.rightOperand.toConstantExpressionOrNull() + if (rightValue in setOf(SmlConstantFloat(0.0), SmlConstantFloat(-0.0), SmlConstantInt(0))) { + error( + "Division by zero.", + null, + ErrorCode.DivisionByZero + ) + } + } + + private fun checkElvisOperator(smlInfixOperation: SmlInfixOperation) { + val leftType = smlInfixOperation.leftOperand.type() + if (!(leftType is NamedType && leftType.isNullable)) { + info( + "The left operand is never null so the elvis operator is unnecessary (keep left operand).", + null, + InfoCode.UnnecessaryElvisOperator + ) + return + } + + val leftValue = smlInfixOperation.leftOperand.toConstantExpressionOrNull() + val rightValue = smlInfixOperation.rightOperand.toConstantExpressionOrNull() + if (leftValue is SmlConstantNull && rightValue is SmlConstantNull) { + info( + "Both operands are always null so the elvis operator is unnecessary (replace with null).", + null, + InfoCode.UnnecessaryElvisOperator + ) + } else if (leftValue is SmlConstantNull) { + info( + "The left operand is always null so the elvis operator is unnecessary (keep right operand).", + null, + InfoCode.UnnecessaryElvisOperator + ) + } else if (rightValue is SmlConstantNull) { + info( + "The right operand is always null so the elvis operator is unnecessary (keep left operand).", + null, + InfoCode.UnnecessaryElvisOperator + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/LambdaChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/LambdaChecker.kt new file mode 100644 index 000000000..a82b08ea4 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/LambdaChecker.kt @@ -0,0 +1,53 @@ +package de.unibonn.simpleml.validation.expressions + +import de.unibonn.simpleml.emf.assigneesOrEmpty +import de.unibonn.simpleml.emf.blockLambdaResultsOrEmpty +import de.unibonn.simpleml.emf.closestAncestorOrNull +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.placeholdersOrEmpty +import de.unibonn.simpleml.simpleML.SmlAbstractLambda +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlBlockLambda +import de.unibonn.simpleml.simpleML.SmlParenthesizedExpression +import de.unibonn.simpleml.simpleML.SmlYield +import de.unibonn.simpleml.staticAnalysis.linking.parameterOrNull +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check + +class LambdaChecker : AbstractSimpleMLChecker() { + + @Check + fun uniqueNames(smlBlockLambda: SmlBlockLambda) { + val declarations = + smlBlockLambda.parametersOrEmpty() + smlBlockLambda.placeholdersOrEmpty() + smlBlockLambda.blockLambdaResultsOrEmpty() + declarations.reportDuplicateNames { + "A parameter, result or placeholder with name '${it.name}' exists already in this lambda." + } + } + + @Check + fun context(smlLambda: SmlAbstractLambda) { + val context = smlLambda.closestAncestorOrNull { it !is SmlParenthesizedExpression } ?: return + + val contextIsValid = when (context) { + is SmlArgument -> { + when (val parameter = context.parameterOrNull()) { + null -> true // Resolution of parameter failed, so this already shows another error + else -> parameter.type != null + } + } + is SmlAssignment -> context.assigneesOrEmpty().firstOrNull() is SmlYield + else -> false + } + + if (!contextIsValid) { + error( + "A lambda must either be yielded in a step or assigned to a typed parameter.", + null, + ErrorCode.LambdaMustBeTypedArgumentOrYielded + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/MemberAccessChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/MemberAccessChecker.kt new file mode 100644 index 000000000..9d7586d76 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/MemberAccessChecker.kt @@ -0,0 +1,67 @@ +package de.unibonn.simpleml.validation.expressions + +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.typeParametersOrEmpty +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlMemberAccess +import de.unibonn.simpleml.staticAnalysis.typing.NamedType +import de.unibonn.simpleml.staticAnalysis.typing.type +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import de.unibonn.simpleml.validation.codes.InfoCode +import org.eclipse.xtext.validation.Check + +class MemberAccessChecker : AbstractSimpleMLChecker() { + + @Check + fun mustBeCalled(smlMemberAccess: SmlMemberAccess) { + when (val member = smlMemberAccess.member.declaration) { + is SmlFunction -> { + if (!member.isStatic && smlMemberAccess.eContainer() !is SmlCall) { + error( + "An instance method must be called.", + Literals.SML_MEMBER_ACCESS__MEMBER, + ErrorCode.INSTANCE_METHOD_MUST_BE_CALLED + ) + } + } + is SmlEnumVariant -> { + val mustBeInstantiated = + member.typeParametersOrEmpty().isNotEmpty() || member.parametersOrEmpty().isNotEmpty() + if (mustBeInstantiated && smlMemberAccess.eContainer() !is SmlCall) { + error( + "An enum variant with parameters or type parameters must be instantiated.", + Literals.SML_MEMBER_ACCESS__MEMBER, + ErrorCode.ENUM_VARIANT_MUST_BE_INSTANTIATED + ) + } + } + } + } + + @Check + fun unnecessarySafeAccess(smlMemberAccess: SmlMemberAccess) { + val type = smlMemberAccess.receiver.type() + + if (smlMemberAccess.isNullSafe) { + if (!(type is NamedType && type.isNullable)) { + info( + "The receiver is never null so the safe access is unnecessary.", + null, + InfoCode.UnnecessarySafeAccess + ) + } + } else { + if (type is NamedType && type.isNullable) { + error( + "The receiver can be null so a safe access must be used.", + null, + ErrorCode.MissingSafeAccess + ) + } + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/ReferenceChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/ReferenceChecker.kt new file mode 100644 index 000000000..5b055f44a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/ReferenceChecker.kt @@ -0,0 +1,64 @@ +package de.unibonn.simpleml.validation.expressions + +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlAbstractChainedExpression +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlMemberAccess +import de.unibonn.simpleml.simpleML.SmlReference +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.emf.ecore.EObject +import org.eclipse.xtext.validation.Check + +class ReferenceChecker : AbstractSimpleMLChecker() { + + @Check + fun mustNotStaticallyReferenceClass(smlReference: SmlReference) { + val declaration = smlReference.declaration + if (declaration !is SmlClass || declaration.parameterList != null) { + return + } + + // Reference must eventually be the receiver of a chained expression + var previous: EObject = smlReference + var current: EObject = previous.eContainer() + while (current is SmlAbstractChainedExpression) { + if (current.receiver == previous) { + return + } + previous = current + current = current.eContainer() + } + + error( + "Must not statically reference class.", + Literals.SML_REFERENCE__DECLARATION, + ErrorCode.MustNotStaticallyReferenceClass + ) + } + + @Check + fun mustNotStaticallyReferenceEnum(smlReference: SmlReference) { + if (smlReference.declaration !is SmlEnum) { + return + } + + // Reference must eventually be the receiver of a member access + var previous: EObject = smlReference + var current: EObject = previous.eContainer() + while (current is SmlMemberAccess) { + if (current.receiver == previous) { + return + } + previous = current + current = current.eContainer() + } + + error( + "Must not statically reference enum.", + Literals.SML_REFERENCE__DECLARATION, + ErrorCode.MustNotStaticallyReferenceEnum + ) + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/TemplateStringChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/TemplateStringChecker.kt new file mode 100644 index 000000000..872079601 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/expressions/TemplateStringChecker.kt @@ -0,0 +1,26 @@ +package de.unibonn.simpleml.validation.expressions + +import de.unibonn.simpleml.simpleML.SmlAbstractTemplateStringPart +import de.unibonn.simpleml.simpleML.SmlTemplateString +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check + +class TemplateStringChecker : AbstractSimpleMLChecker() { + + @Check + fun missingTemplateExpression(smlTemplateString: SmlTemplateString) { + smlTemplateString.expressions + .windowed(size = 2, step = 1) + .forEach { (first, second) -> + if (first is SmlAbstractTemplateStringPart && second is SmlAbstractTemplateStringPart) { + error( + "There must be a template expression between two template string parts.", + second, + null, + ErrorCode.MissingTemplateExpression + ) + } + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/AnnotationCallChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/AnnotationCallChecker.kt new file mode 100644 index 000000000..16d60fe15 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/AnnotationCallChecker.kt @@ -0,0 +1,227 @@ +package de.unibonn.simpleml.validation.other + +import de.unibonn.simpleml.emf.argumentsOrEmpty +import de.unibonn.simpleml.emf.isRequired +import de.unibonn.simpleml.emf.isResolved +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.targetOrNull +import de.unibonn.simpleml.naming.qualifiedNameOrNull +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.simpleML.SmlAnnotationCall +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlMemberAccess +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlTypeParameter +import de.unibonn.simpleml.simpleML.SmlWorkflow +import de.unibonn.simpleml.staticAnalysis.linking.parametersOrNull +import de.unibonn.simpleml.staticAnalysis.partialEvaluation.toConstantExpressionOrNull +import de.unibonn.simpleml.stdlibAccess.StdlibAnnotations +import de.unibonn.simpleml.stdlibAccess.StdlibEnums.AnnotationTarget +import de.unibonn.simpleml.stdlibAccess.isPure +import de.unibonn.simpleml.stdlibAccess.pythonModuleOrNull +import de.unibonn.simpleml.stdlibAccess.pythonNameOrNull +import de.unibonn.simpleml.stdlibAccess.validTargets +import de.unibonn.simpleml.utils.duplicatesBy +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import de.unibonn.simpleml.validation.codes.InfoCode +import de.unibonn.simpleml.validation.codes.WarningCode +import org.eclipse.xtext.validation.Check +import org.eclipse.xtext.validation.CheckType + +class AnnotationCallChecker : AbstractSimpleMLChecker() { + + @Check + fun duplicateTargetInTargetAnnotation(smlAnnotationCall: SmlAnnotationCall) { + val annotation = smlAnnotationCall.annotation + if (!annotation.isResolved() || annotation.qualifiedNameOrNull() != StdlibAnnotations.Target) { + return + } + + smlAnnotationCall + .argumentsOrEmpty() + .map { it.value } + .filterIsInstance() + .duplicatesBy { it.member.declaration.qualifiedNameOrNull() } + .forEach { + warning( + "This annotation target is used multiple times.", + it, + null, + WarningCode.DuplicateTarget + ) + } + } + + @Check + fun missingArgumentList(smlAnnotationCall: SmlAnnotationCall) { + if (smlAnnotationCall.argumentList != null) { + return + } + + val annotation = smlAnnotationCall.annotation + if (!annotation.isResolved()) { + return + } + + val parameters = smlAnnotationCall.annotation.parametersOrEmpty() + if (parameters.any { it.isRequired() }) { + error( + "Missing argument list.", + Literals.SML_ANNOTATION_CALL__ANNOTATION, + ErrorCode.MISSING_ARGUMENT_LIST + ) + } + } + + @Check + fun target(smlAnnotationCall: SmlAnnotationCall) { + + // Get target of annotation use + val actualTarget = smlAnnotationCall.targetOrNull() ?: return + + // Get legal targets of used annotation + val annotation = smlAnnotationCall.annotation + if (!annotation.isResolved()) { + return + } + + val legalTargets = annotation.validTargets() + + // Compare actual and legal targets + val wrongTarget: String? = when { + actualTarget is SmlAnnotation && AnnotationTarget.Annotation !in legalTargets -> { + "an annotation" + } + actualTarget is SmlAttribute && AnnotationTarget.Attribute !in legalTargets -> { + "an attribute" + } + actualTarget is SmlClass && AnnotationTarget.Class !in legalTargets -> { + "a class" + } + actualTarget is SmlCompilationUnit && AnnotationTarget.CompilationUnit !in legalTargets -> { + "a compilation unit" + } + actualTarget is SmlEnum && AnnotationTarget.Enum !in legalTargets -> { + "an enum" + } + actualTarget is SmlEnumVariant && AnnotationTarget.EnumVariant !in legalTargets -> { + "an enum variant" + } + actualTarget is SmlFunction && AnnotationTarget.Function !in legalTargets -> { + "a function" + } + actualTarget is SmlParameter && AnnotationTarget.Parameter !in legalTargets -> { + "a parameter" + } + actualTarget is SmlResult && AnnotationTarget.Result !in legalTargets -> { + "a result" + } + actualTarget is SmlTypeParameter && AnnotationTarget.TypeParameter !in legalTargets -> { + "a type parameter" + } + actualTarget is SmlWorkflow && AnnotationTarget.Workflow !in legalTargets -> { + "a workflow" + } + actualTarget is SmlStep && AnnotationTarget.Step !in legalTargets -> { + "a step" + } + else -> null + } + + // Show error + if (wrongTarget != null) { + error( + "This annotation cannot be applied to $wrongTarget.", + null, + ErrorCode.WRONG_TARGET + ) + } + } + + @Check + fun unnecessaryArgumentList(smlAnnotationCall: SmlAnnotationCall) { + if (smlAnnotationCall.argumentList == null || smlAnnotationCall.argumentsOrEmpty().isNotEmpty()) { + return + } + + val parametersOrNull = smlAnnotationCall.argumentList.parametersOrNull() + if (parametersOrNull != null && parametersOrNull.none { it.isRequired() }) { + info( + "Unnecessary argument list.", + Literals.SML_ABSTRACT_CALL__ARGUMENT_LIST, + InfoCode.UnnecessaryArgumentList + ) + } + } + + @Check(CheckType.NORMAL) + fun argumentsMustBeConstant(smlAnnotationCall: SmlAnnotationCall) { + smlAnnotationCall.argumentsOrEmpty().forEach { + if (it.value?.toConstantExpressionOrNull() == null) { + error( + "Arguments in annotation call must be constant.", + it, + Literals.SML_ARGUMENT__VALUE, + ErrorCode.MustBeConstant + ) + } + } + } + + @Check + fun pureImpliesNoSideEffects(smlAnnotationCall: SmlAnnotationCall) { + if (smlAnnotationCall.annotation.qualifiedNameOrNull() != StdlibAnnotations.NoSideEffects) { + return + } + + val target = smlAnnotationCall.targetOrNull() ?: return + if (target is SmlFunction && target.isPure()) { + info( + "Purity implies absence of side effects (remove this annotation call).", + null, + InfoCode.PureImpliesNoSideEffects + ) + } + } + + @Check + fun identicalPythonModule(smlAnnotationCall: SmlAnnotationCall) { + if (smlAnnotationCall.annotation.qualifiedNameOrNull() != StdlibAnnotations.PythonModule) { + return + } + + val target = smlAnnotationCall.targetOrNull() as? SmlCompilationUnit ?: return + if (target.name == target.pythonModuleOrNull()) { + info( + "Python module is identical to Simple-ML package (can remove annotation call).", + null, + InfoCode.IdenticalPythonModule + ) + } + } + + @Check + fun identicalPythonName(smlAnnotationCall: SmlAnnotationCall) { + if (smlAnnotationCall.annotation.qualifiedNameOrNull() != StdlibAnnotations.PythonName) { + return + } + + val target = smlAnnotationCall.targetOrNull() ?: return + if (target.name == target.pythonNameOrNull()) { + info( + "Python name is identical to Simple-ML name (can remove annotation call).", + null, + InfoCode.IdenticalPythonName + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/ArgumentListChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/ArgumentListChecker.kt new file mode 100644 index 000000000..b07eb34b2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/ArgumentListChecker.kt @@ -0,0 +1,105 @@ +package de.unibonn.simpleml.validation.other + +import de.unibonn.simpleml.emf.isNamed +import de.unibonn.simpleml.emf.isPositional +import de.unibonn.simpleml.emf.isRequired +import de.unibonn.simpleml.simpleML.SmlArgumentList +import de.unibonn.simpleml.staticAnalysis.linking.parameterOrNull +import de.unibonn.simpleml.staticAnalysis.linking.parametersOrNull +import de.unibonn.simpleml.utils.duplicatesBy +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check + +class ArgumentListChecker : AbstractSimpleMLChecker() { + + @Check + fun missingRequiredParameter(smlArgumentList: SmlArgumentList) { + val parameters = smlArgumentList.parametersOrNull() ?: return + val requiredParameters = parameters.filter { it.isRequired() } + val givenParameters = smlArgumentList.arguments.mapNotNull { it.parameterOrNull() } + val missingRequiredParameters = requiredParameters - givenParameters.toSet() + + missingRequiredParameters.forEach { + error( + "The parameter '${it.name}' is required and must be set here.", + null, + ErrorCode.MISSING_REQUIRED_PARAMETER + ) + } + } + + @Check + fun noPositionalArgumentsAfterFirstNamedArgument(smlArgumentList: SmlArgumentList) { + val firstNamedArgumentIndex = smlArgumentList.arguments.indexOfFirst { it.isNamed() } + if (firstNamedArgumentIndex == -1) { + return + } + + smlArgumentList.arguments + .drop(firstNamedArgumentIndex + 1) + .filter { it.isPositional() } + .forEach { + error( + "After the first named argument all arguments must be named.", + it, + null, + ErrorCode.NO_POSITIONAL_ARGUMENTS_AFTER_FIRST_NAMED_ARGUMENT + ) + } + } + + @Check + fun tooManyArguments(smlArgumentList: SmlArgumentList) { + val parameters = smlArgumentList.parametersOrNull() + if (parameters == null || parameters.any { it.isVariadic }) { + return + } + + val maximumExpectedNumberOfArguments = parameters.size + val actualNumberOfArguments = smlArgumentList.arguments.size + + if (actualNumberOfArguments > maximumExpectedNumberOfArguments) { + val minimumExpectedNumberOfArguments = parameters.filter { it.isRequired() }.size + val message = buildString { + append("Expected ") + + when { + minimumExpectedNumberOfArguments != maximumExpectedNumberOfArguments -> { + append("between $minimumExpectedNumberOfArguments and $maximumExpectedNumberOfArguments arguments") + } + minimumExpectedNumberOfArguments == 1 -> append("exactly 1 argument") + else -> append("exactly $minimumExpectedNumberOfArguments arguments") + } + + append(" but got $actualNumberOfArguments.") + } + + error( + message, + null, + ErrorCode.TOO_MANY_ARGUMENTS + ) + } + } + + @Check + fun uniqueParameters(smlArgumentList: SmlArgumentList) { + smlArgumentList.arguments + .duplicatesBy { + val parameter = it.parameterOrNull() ?: return@duplicatesBy null + when { + parameter.isVariadic -> null + else -> parameter.name + } + } + .forEach { + error( + "The parameter '${it.parameterOrNull()?.name}' is already set.", + it, + null, + ErrorCode.UNIQUE_PARAMETERS + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/DeprecationChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/DeprecationChecker.kt new file mode 100644 index 000000000..7acf9fafd --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/DeprecationChecker.kt @@ -0,0 +1,98 @@ +package de.unibonn.simpleml.validation.other + +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlAbstractAssignee +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlAnnotationCall +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlNamedType +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlReference +import de.unibonn.simpleml.simpleML.SmlTypeArgument +import de.unibonn.simpleml.simpleML.SmlWildcard +import de.unibonn.simpleml.staticAnalysis.assignedOrNull +import de.unibonn.simpleml.staticAnalysis.linking.parameterOrNull +import de.unibonn.simpleml.staticAnalysis.linking.typeParameterOrNull +import de.unibonn.simpleml.stdlibAccess.isDeprecated +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.WarningCode +import org.eclipse.xtext.validation.Check + +class DeprecationChecker : AbstractSimpleMLChecker() { + + @Check + fun annotationUseReferenceDeprecatedAnnotation(smlAnnotationCall: SmlAnnotationCall) { + val annotation = smlAnnotationCall.annotation ?: return + if (annotation.isDeprecated()) { + warning( + "The used annotation is deprecated.", + Literals.SML_ANNOTATION_CALL__ANNOTATION, + WarningCode.ReferencedDeclarationIsDeprecated + ) + } + } + + @Check + fun assigneeAssignedToDeprecatedValue(smlAssignee: SmlAbstractAssignee) { + if (smlAssignee is SmlWildcard) { + return + } + + val assigned = smlAssignee.assignedOrNull() ?: return + if (assigned is SmlAbstractDeclaration && assigned.isDeprecated()) { + warning( + "The assigned declaration is deprecated.", + null, + WarningCode.AssignedDeclarationIsDeprecated + ) + } + } + + @Check + fun argumentReferencesDeprecatedParameter(smlArgument: SmlArgument) { + val parameter = smlArgument.parameterOrNull() ?: return + if (parameter.isDeprecated()) { + warning( + "The corresponding parameter is deprecated.", + null, + WarningCode.CorrespondingParameterIsDeprecated + ) + } + } + + @Check + fun namedTypeReferencesDeprecatedDeclaration(smlNamedType: SmlNamedType) { + val declaration = smlNamedType.declaration ?: return + if (declaration.isDeprecated()) { + warning( + "The referenced declaration is deprecated.", + Literals.SML_NAMED_TYPE__DECLARATION, + WarningCode.ReferencedDeclarationIsDeprecated + ) + } + } + + @Check + fun referenceReferencesDeprecatedDeclaration(smlReference: SmlReference) { + val declaration = smlReference.declaration ?: return + if (declaration !is SmlParameter && declaration.isDeprecated()) { + warning( + "The referenced declaration is deprecated.", + Literals.SML_REFERENCE__DECLARATION, + WarningCode.ReferencedDeclarationIsDeprecated + ) + } + } + + @Check + fun typeArgumentReferencesDeprecatedTypeParameter(smlTypeArgument: SmlTypeArgument) { + val typeParameter = smlTypeArgument.typeParameterOrNull() ?: return + if (typeParameter.isDeprecated()) { + warning( + "The corresponding type parameter is deprecated.", + null, + WarningCode.CorrespondingTypeParameterIsDeprecated + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/ProtocolChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/ProtocolChecker.kt new file mode 100644 index 000000000..4cf021786 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/ProtocolChecker.kt @@ -0,0 +1,34 @@ +package de.unibonn.simpleml.validation.other + +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlProtocolReference +import de.unibonn.simpleml.simpleML.SmlProtocolSubtermList +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check + +class ProtocolChecker : AbstractSimpleMLChecker() { + + @Check + fun mustOnlyReferToInstanceMembers(smlProtocolReference: SmlProtocolReference) { + val token = smlProtocolReference.token + val isStaticAttribute = token is SmlAttribute && token.isStatic + val isStaticFunction = token is SmlFunction && token.isStatic + + if (isStaticAttribute || isStaticFunction) { + error( + "Must only reference instance members.", + null, + ErrorCode.OnlyReferenceInstanceMembers + ) + } + } + + @Check + fun uniqueNames(smlProtocolSubtermList: SmlProtocolSubtermList) { + smlProtocolSubtermList.subterms.reportDuplicateNames { + "A subterm with name '${it.name}' exists already in this protocol." + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/TypeArgumentListChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/TypeArgumentListChecker.kt new file mode 100644 index 000000000..de679e6fe --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/other/TypeArgumentListChecker.kt @@ -0,0 +1,103 @@ +package de.unibonn.simpleml.validation.other + +import de.unibonn.simpleml.emf.isNamed +import de.unibonn.simpleml.emf.isPositional +import de.unibonn.simpleml.simpleML.SmlTypeArgumentList +import de.unibonn.simpleml.staticAnalysis.linking.typeParameterOrNull +import de.unibonn.simpleml.staticAnalysis.linking.typeParametersOrNull +import de.unibonn.simpleml.utils.duplicatesBy +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import de.unibonn.simpleml.validation.codes.InfoCode +import org.eclipse.xtext.validation.Check + +class TypeArgumentListChecker : AbstractSimpleMLChecker() { + + @Check + fun missingRequiredTypeParameter(smlTypeArgumentList: SmlTypeArgumentList) { + val requiredTypeParameters = smlTypeArgumentList.typeParametersOrNull() ?: return + val givenTypeParameters = smlTypeArgumentList.typeArguments.mapNotNull { it.typeParameterOrNull() } + val missingRequiredTypeParameters = requiredTypeParameters - givenTypeParameters.toSet() + + missingRequiredTypeParameters.forEach { + error( + "The type parameter '${it.name}' is required and must be set here.", + null, + ErrorCode.MISSING_REQUIRED_TYPE_PARAMETER + ) + } + } + + @Check + fun noPositionalArgumentsAfterFirstNamedArgument(smlTypeArgumentList: SmlTypeArgumentList) { + val firstNamedTypeArgumentIndex = smlTypeArgumentList.typeArguments.indexOfFirst { it.isNamed() } + if (firstNamedTypeArgumentIndex == -1) { + return + } + + smlTypeArgumentList.typeArguments + .drop(firstNamedTypeArgumentIndex + 1) + .filter { it.isPositional() } + .forEach { + error( + "After the first named type argument all type arguments must be named.", + it, + null, + ErrorCode.NO_POSITIONAL_TYPE_ARGUMENTS_AFTER_FIRST_NAMED_TYPE_ARGUMENT + ) + } + } + + @Check + fun tooManyTypeArguments(smlTypeArgumentList: SmlTypeArgumentList) { + val typeParameter = smlTypeArgumentList.typeParametersOrNull() ?: return + + val maximumExpectedNumberOfArguments = typeParameter.size + val actualNumberOfArguments = smlTypeArgumentList.typeArguments.size + + if (actualNumberOfArguments > maximumExpectedNumberOfArguments) { + val message = buildString { + append("Expected ") + + when (maximumExpectedNumberOfArguments) { + 1 -> append("exactly 1 type argument") + else -> append("exactly $maximumExpectedNumberOfArguments type arguments") + } + + append(" but got $actualNumberOfArguments.") + } + + error( + message, + null, + ErrorCode.TooManyTypeArguments + ) + } + } + + @Check + fun uniqueTypeParameters(smlTypeArgumentList: SmlTypeArgumentList) { + smlTypeArgumentList.typeArguments + .duplicatesBy { it.typeParameterOrNull()?.name } + .forEach { + error( + "The type parameter '${it.typeParameterOrNull()?.name}' is already set.", + it, + null, + ErrorCode.UniqueTypeParameters + ) + } + } + + @Check + fun unnecessaryTypeArgumentList(smlTypeArgumentList: SmlTypeArgumentList) { + val typeParametersOrNull = smlTypeArgumentList.typeParametersOrNull() + if (typeParametersOrNull != null && typeParametersOrNull.isEmpty()) { + info( + "Unnecessary type argument list.", + null, + InfoCode.UnnecessaryTypeArgumentList + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/statements/AssignmentChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/statements/AssignmentChecker.kt new file mode 100644 index 000000000..461c8ca6d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/statements/AssignmentChecker.kt @@ -0,0 +1,84 @@ +package de.unibonn.simpleml.validation.statements + +import de.unibonn.simpleml.emf.assigneesOrEmpty +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlBlockLambdaResult +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlWildcard +import de.unibonn.simpleml.simpleML.SmlYield +import de.unibonn.simpleml.staticAnalysis.AssignedResult +import de.unibonn.simpleml.staticAnalysis.expressionHasNoSideEffects +import de.unibonn.simpleml.staticAnalysis.maybeAssigned +import de.unibonn.simpleml.staticAnalysis.resultsOrNull +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import de.unibonn.simpleml.validation.codes.InfoCode +import de.unibonn.simpleml.validation.codes.WarningCode +import org.eclipse.xtext.validation.Check + +class AssignmentChecker : AbstractSimpleMLChecker() { + + @Check + fun unnecessaryAssigneeList(smlAssignment: SmlAssignment) { + if (smlAssignment.assigneesOrEmpty().all { it is SmlWildcard }) { + info( + "This assignment can be converted to an expression statement.", + null, + InfoCode.UnnecessaryAssignment + ) + } + } + + @Check + fun assigneeWithoutValue(smlAssignment: SmlAssignment) { + smlAssignment.assigneesOrEmpty() + .filter { it.maybeAssigned() == AssignedResult.NotAssigned } + .forEach { + error( + "No value is assigned to this assignee.", + it, + null, + ErrorCode.ASSIGNEE_WITHOUT_VALUE + ) + } + } + + @Check + fun hasNoEffect(smlAssignment: SmlAssignment) { + if (smlAssignment.assigneesOrEmpty() + .any { it is SmlPlaceholder || it is SmlYield || it is SmlBlockLambdaResult } + ) { + return + } + + if (smlAssignment.expression.expressionHasNoSideEffects()) { + warning( + "This statement does nothing.", + null, + WarningCode.StatementDoesNothing + ) + } + } + + @Check + fun ignoredResultOfCall(smlAssignment: SmlAssignment) { + val expression = smlAssignment.expression + if (expression is SmlCall) { + val results = (expression.resultsOrNull() ?: listOf()) + val unassignedResults = results.drop(smlAssignment.assigneesOrEmpty().size) + + unassignedResults + .filterIsInstance() + .forEach { + warning( + "The result '${it.name}' is implicitly ignored.", + Literals.SML_ASSIGNMENT__ASSIGNEE_LIST, + WarningCode.ImplicitlyIgnoredResultOfCall + ) + } + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/statements/ExpressionsStatementChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/statements/ExpressionsStatementChecker.kt new file mode 100644 index 000000000..6a3b33ea7 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/statements/ExpressionsStatementChecker.kt @@ -0,0 +1,21 @@ +package de.unibonn.simpleml.validation.statements + +import de.unibonn.simpleml.simpleML.SmlExpressionStatement +import de.unibonn.simpleml.staticAnalysis.expressionHasNoSideEffects +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.WarningCode +import org.eclipse.xtext.validation.Check + +class ExpressionsStatementChecker : AbstractSimpleMLChecker() { + + @Check + fun hasNoEffect(smlExpressionStatement: SmlExpressionStatement) { + if (smlExpressionStatement.expression.expressionHasNoSideEffects()) { + warning( + "This statement does nothing.", + null, + WarningCode.StatementDoesNothing + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/ArgumentTypeChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/ArgumentTypeChecker.kt new file mode 100644 index 000000000..97b882607 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/ArgumentTypeChecker.kt @@ -0,0 +1,41 @@ +package de.unibonn.simpleml.validation.typeChecking + +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.staticAnalysis.linking.parameterOrNull +import de.unibonn.simpleml.staticAnalysis.typing.UnresolvedType +import de.unibonn.simpleml.staticAnalysis.typing.isSubstitutableFor +import de.unibonn.simpleml.staticAnalysis.typing.type +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check +import org.eclipse.xtext.validation.CheckType + +class ArgumentTypeChecker : AbstractSimpleMLChecker() { + + @Check(CheckType.NORMAL) + fun value(smlArgument: SmlArgument) { + val argumentType = smlArgument.type() + if (argumentType is UnresolvedType) { + return // Scoping error already shown + } + + val parameterType = (smlArgument.parameterOrNull() ?: return).type() + + if (!argumentType.isSubstitutableFor(parameterType)) { + var argumentTypeString = argumentType.toSimpleString() + var parameterTypeString = parameterType.toSimpleString() + + if (argumentTypeString == parameterTypeString) { + argumentTypeString = argumentType.toString() + parameterTypeString = parameterType.toString() + } + + error( + "An argument of type '$argumentTypeString' cannot be assigned to a parameter of type '$parameterTypeString'.", + Literals.SML_ARGUMENT__VALUE, + ErrorCode.WrongType + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/DefaultValueTypeChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/DefaultValueTypeChecker.kt new file mode 100644 index 000000000..453dbdc64 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/DefaultValueTypeChecker.kt @@ -0,0 +1,41 @@ +package de.unibonn.simpleml.validation.typeChecking + +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.staticAnalysis.typing.UnresolvedType +import de.unibonn.simpleml.staticAnalysis.typing.isSubstitutableFor +import de.unibonn.simpleml.staticAnalysis.typing.type +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check +import org.eclipse.xtext.validation.CheckType + +class DefaultValueTypeChecker : AbstractSimpleMLChecker() { + + @Check(CheckType.NORMAL) + fun defaultValue(smlParameter: SmlParameter) { + val defaultValue = smlParameter.defaultValue ?: return + val defaultValueType = defaultValue.type() + if (defaultValueType is UnresolvedType) { + return // Scoping error already shown + } + + val parameterType = smlParameter.type() + + if (!defaultValueType.isSubstitutableFor(parameterType)) { + var defaultValueTypeString = defaultValueType.toSimpleString() + var parameterTypeString = parameterType.toSimpleString() + + if (defaultValueTypeString == parameterTypeString) { + defaultValueTypeString = defaultValueType.toString() + parameterTypeString = parameterType.toString() + } + + error( + "A default value of type '$defaultValueTypeString' cannot be assigned to a parameter of type '$parameterTypeString'.", + Literals.SML_PARAMETER__DEFAULT_VALUE, + ErrorCode.WrongType + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/IndexedAccessTypeChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/IndexedAccessTypeChecker.kt new file mode 100644 index 000000000..4c9ac7df2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/IndexedAccessTypeChecker.kt @@ -0,0 +1,53 @@ +package de.unibonn.simpleml.validation.typeChecking + +import de.unibonn.simpleml.naming.qualifiedNameOrNull +import de.unibonn.simpleml.simpleML.SimpleMLPackage +import de.unibonn.simpleml.simpleML.SmlIndexedAccess +import de.unibonn.simpleml.staticAnalysis.typing.ClassType +import de.unibonn.simpleml.staticAnalysis.typing.UnresolvedType +import de.unibonn.simpleml.staticAnalysis.typing.VariadicType +import de.unibonn.simpleml.staticAnalysis.typing.type +import de.unibonn.simpleml.stdlibAccess.StdlibClasses +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check +import org.eclipse.xtext.validation.CheckType + +class IndexedAccessTypeChecker : AbstractSimpleMLChecker() { + + @Check(CheckType.NORMAL) + fun receiverMustBeVariadic(smlIndexedAccess: SmlIndexedAccess) { + val receiverType = smlIndexedAccess.receiver.type() + if (receiverType is UnresolvedType) { + return // Scoping error already shown + } + + if (receiverType !is VariadicType) { + error( + "The receiver of an indexed access must refer to a variadic parameter.", + SimpleMLPackage.Literals.SML_ABSTRACT_CHAINED_EXPRESSION__RECEIVER, + ErrorCode.WrongType + ) + } + } + + @Check + fun indexMustBeInt(smlIndexedAccess: SmlIndexedAccess) { + val indexType = smlIndexedAccess.index.type() + if (indexType is UnresolvedType) { + return + } + + val hasWrongType = indexType !is ClassType || + indexType.isNullable || + indexType.smlClass.qualifiedNameOrNull() != StdlibClasses.Int + + if (hasWrongType) { + error( + "The index of an indexed access must be an instance of the class 'Int'.", + SimpleMLPackage.Literals.SML_INDEXED_ACCESS__INDEX, + ErrorCode.WrongType + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/InfixOperationTypeChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/InfixOperationTypeChecker.kt new file mode 100644 index 000000000..88a11abc0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/InfixOperationTypeChecker.kt @@ -0,0 +1,106 @@ +package de.unibonn.simpleml.validation.typeChecking + +import de.unibonn.simpleml.constant.SmlInfixOperationOperator +import de.unibonn.simpleml.constant.operator +import de.unibonn.simpleml.naming.qualifiedNameOrNull +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlAbstractExpression +import de.unibonn.simpleml.simpleML.SmlInfixOperation +import de.unibonn.simpleml.staticAnalysis.typing.ClassType +import de.unibonn.simpleml.staticAnalysis.typing.UnresolvedType +import de.unibonn.simpleml.staticAnalysis.typing.type +import de.unibonn.simpleml.stdlibAccess.StdlibClasses +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.emf.ecore.EReference +import org.eclipse.xtext.validation.Check +import org.eclipse.xtext.validation.CheckType + +class InfixOperationTypeChecker : AbstractSimpleMLChecker() { + + @Check(CheckType.NORMAL) + fun leftOperand(smlInfixOperation: SmlInfixOperation) { + checkOperand(smlInfixOperation, Literals.SML_INFIX_OPERATION__LEFT_OPERAND) + } + + @Check(CheckType.NORMAL) + fun rightOperand(smlInfixOperation: SmlInfixOperation) { + checkOperand(smlInfixOperation, Literals.SML_INFIX_OPERATION__RIGHT_OPERAND) + } + + private fun checkOperand(smlInfixOperation: SmlInfixOperation, feature: EReference) { + val operandType = operand(smlInfixOperation, feature).type() + if (operandType is UnresolvedType) { + return // Scoping error already shown + } + + when (smlInfixOperation.operator()) { + SmlInfixOperationOperator.Or, + SmlInfixOperationOperator.And -> { + val hasWrongType = operandType !is ClassType || + operandType.isNullable || + operandType.smlClass.qualifiedNameOrNull() != StdlibClasses.Boolean + + if (hasWrongType) { + error( + "The ${operandPositionToString(feature)} operand of a logical infix operation must be an instance of the class 'Boolean'.", + feature, + ErrorCode.WrongType + ) + } + } + + SmlInfixOperationOperator.Plus, + SmlInfixOperationOperator.Minus, + SmlInfixOperationOperator.Times, + SmlInfixOperationOperator.By -> { + val hasWrongType = operandType !is ClassType || + operandType.isNullable || + operandType.smlClass.qualifiedNameOrNull() !in setOf(StdlibClasses.Float, StdlibClasses.Int) + + if (hasWrongType) { + error( + "The ${operandPositionToString(feature)} operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'.", + feature, + ErrorCode.WrongType + ) + } + } + + SmlInfixOperationOperator.LessThan, + SmlInfixOperationOperator.LessThanOrEquals, + SmlInfixOperationOperator.GreaterThanOrEquals, + SmlInfixOperationOperator.GreaterThan -> { + val hasWrongType = operandType !is ClassType || + operandType.isNullable || + operandType.smlClass.qualifiedNameOrNull() !in setOf(StdlibClasses.Float, StdlibClasses.Int) + + if (hasWrongType) { + error( + "The ${operandPositionToString(feature)} operand of a comparison must be an instance of the class 'Float' or the class 'Int'.", + feature, + ErrorCode.WrongType + ) + } + } + + else -> {} + } + } + + private fun operand(smlInfixOperation: SmlInfixOperation, feature: EReference): SmlAbstractExpression { + return when (feature) { + Literals.SML_INFIX_OPERATION__LEFT_OPERAND -> smlInfixOperation.leftOperand + Literals.SML_INFIX_OPERATION__RIGHT_OPERAND -> smlInfixOperation.rightOperand + else -> throw IllegalArgumentException("Cannot handle feature '$feature'.") + } + } + + private fun operandPositionToString(feature: EReference): String { + return when (feature) { + Literals.SML_INFIX_OPERATION__LEFT_OPERAND -> "left" + Literals.SML_INFIX_OPERATION__RIGHT_OPERAND -> "right" + else -> throw IllegalArgumentException("Cannot handle feature '$feature'.") + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/PrefixOperationTypeChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/PrefixOperationTypeChecker.kt new file mode 100644 index 000000000..5dcffe851 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/PrefixOperationTypeChecker.kt @@ -0,0 +1,55 @@ +package de.unibonn.simpleml.validation.typeChecking + +import de.unibonn.simpleml.constant.SmlPrefixOperationOperator +import de.unibonn.simpleml.constant.operator +import de.unibonn.simpleml.naming.qualifiedNameOrNull +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlPrefixOperation +import de.unibonn.simpleml.staticAnalysis.typing.ClassType +import de.unibonn.simpleml.staticAnalysis.typing.UnresolvedType +import de.unibonn.simpleml.staticAnalysis.typing.type +import de.unibonn.simpleml.stdlibAccess.StdlibClasses +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check +import org.eclipse.xtext.validation.CheckType + +class PrefixOperationTypeChecker : AbstractSimpleMLChecker() { + + @Check(CheckType.NORMAL) + fun operand(smlPrefixOperation: SmlPrefixOperation) { + val operandType = smlPrefixOperation.operand.type() + if (operandType is UnresolvedType) { + return // Scoping error already shown + } + + when (smlPrefixOperation.operator()) { + SmlPrefixOperationOperator.Not -> { + val hasWrongType = operandType !is ClassType || + operandType.isNullable || + operandType.smlClass.qualifiedNameOrNull() != StdlibClasses.Boolean + + if (hasWrongType) { + error( + "The operand of a logical negation must be an instance of the class 'Boolean'.", + Literals.SML_PREFIX_OPERATION__OPERAND, + ErrorCode.WrongType + ) + } + } + SmlPrefixOperationOperator.Minus -> { + val hasWrongType = operandType !is ClassType || + operandType.isNullable || + operandType.smlClass.qualifiedNameOrNull() !in setOf(StdlibClasses.Float, StdlibClasses.Int) + + if (hasWrongType) { + error( + "The operand of an arithmetic negation must be an instance of the class 'Float' or the class 'Int'.", + Literals.SML_PREFIX_OPERATION__OPERAND, + ErrorCode.WrongType + ) + } + } + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/YieldTypeChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/YieldTypeChecker.kt new file mode 100644 index 000000000..e74eb2fe9 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/typeChecking/YieldTypeChecker.kt @@ -0,0 +1,42 @@ +package de.unibonn.simpleml.validation.typeChecking + +import de.unibonn.simpleml.simpleML.SmlYield +import de.unibonn.simpleml.staticAnalysis.assignedOrNull +import de.unibonn.simpleml.staticAnalysis.typing.UnresolvedType +import de.unibonn.simpleml.staticAnalysis.typing.isSubstitutableFor +import de.unibonn.simpleml.staticAnalysis.typing.type +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check +import org.eclipse.xtext.validation.CheckType + +class YieldTypeChecker : AbstractSimpleMLChecker() { + + @Check(CheckType.NORMAL) + fun value(smlYield: SmlYield) { + val yieldedValue = smlYield.assignedOrNull() ?: return + val yieldedValueType = yieldedValue.type() + if (yieldedValueType is UnresolvedType) { + return // Scoping error already shown + } + + val resultType = (smlYield.result ?: return).type() + + if (!yieldedValueType.isSubstitutableFor(resultType)) { + var yieldedValueTypeString = yieldedValueType.toSimpleString() + var resultTypeString = resultType.toSimpleString() + + if (yieldedValueTypeString == resultTypeString) { + yieldedValueTypeString = yieldedValueType.toString() + resultTypeString = resultType.toString() + } + + error( + "A value of type '$yieldedValueTypeString' cannot be assigned to a result of type '$resultTypeString'.", + yieldedValue, + null, + ErrorCode.WrongType + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/types/CallableTypeChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/types/CallableTypeChecker.kt new file mode 100644 index 000000000..037804499 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/types/CallableTypeChecker.kt @@ -0,0 +1,35 @@ +package de.unibonn.simpleml.validation.types + +import de.unibonn.simpleml.emf.isOptional +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.resultsOrEmpty +import de.unibonn.simpleml.simpleML.SimpleMLPackage.Literals +import de.unibonn.simpleml.simpleML.SmlCallableType +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check + +class CallableTypeChecker : AbstractSimpleMLChecker() { + + @Check + fun uniqueNames(smlCallableType: SmlCallableType) { + val declarations = smlCallableType.parametersOrEmpty() + smlCallableType.resultsOrEmpty() + declarations.reportDuplicateNames { + "A parameter or result with name '${it.name}' exists already in this callable type." + } + } + + @Check + fun noOptionalParameters(smlCallableType: SmlCallableType) { + smlCallableType.parametersOrEmpty().forEach { + if (it.isOptional()) { + error( + "Parameters in callable types must not be optional.", + it, + Literals.SML_PARAMETER__DEFAULT_VALUE, + ErrorCode.NoOptionalParametersInCallableType + ) + } + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/types/NamedTypeChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/types/NamedTypeChecker.kt new file mode 100644 index 000000000..2736815b3 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/types/NamedTypeChecker.kt @@ -0,0 +1,38 @@ +package de.unibonn.simpleml.validation.types + +import de.unibonn.simpleml.emf.typeParametersOrEmpty +import de.unibonn.simpleml.simpleML.SimpleMLPackage +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlNamedType +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import org.eclipse.xtext.validation.Check + +class NamedTypeChecker : AbstractSimpleMLChecker() { + + @Check + fun missingTypeArgumentList(smlNamedType: SmlNamedType) { + if (smlNamedType.typeArgumentList != null) { + return + } + + val declaration = smlNamedType.declaration + val typeParameters = when { + declaration.eIsProxy() -> return + declaration is SmlClass -> declaration.typeParametersOrEmpty() + declaration is SmlEnumVariant -> declaration.typeParametersOrEmpty() + declaration is SmlFunction -> declaration.typeParametersOrEmpty() + else -> return + } + + if (typeParameters.isNotEmpty()) { + error( + "Missing type argument list.", + SimpleMLPackage.Literals.SML_NAMED_TYPE__DECLARATION, + ErrorCode.MISSING_TYPE_ARGUMENT_LIST + ) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/types/UnionTypeChecker.kt b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/types/UnionTypeChecker.kt new file mode 100644 index 000000000..a226a83db --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation/types/UnionTypeChecker.kt @@ -0,0 +1,31 @@ +package de.unibonn.simpleml.validation.types + +import de.unibonn.simpleml.emf.typeArgumentsOrEmpty +import de.unibonn.simpleml.simpleML.SmlUnionType +import de.unibonn.simpleml.validation.AbstractSimpleMLChecker +import de.unibonn.simpleml.validation.codes.ErrorCode +import de.unibonn.simpleml.validation.codes.InfoCode +import org.eclipse.xtext.validation.Check + +class UnionTypeChecker : AbstractSimpleMLChecker() { + + @Check + fun numberOfTypeArguments(smlUnionType: SmlUnionType) { + when (smlUnionType.typeArgumentsOrEmpty().size) { + 0 -> { + error( + "A union type must have least one type argument.", + null, + ErrorCode.UNION_TYPE_WITHOUT_TYPE_ARGUMENTS + ) + } + 1 -> { + info( + "A union type with one type argument is equivalent to the the type argument itself.", + null, + InfoCode.UnnecessaryUnionType + ) + } + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/main/resources/stdlib/simpleml/lang/codeGeneration.smlstub b/DSL/de.unibonn.simpleml/src/main/resources/stdlib/simpleml/lang/codeGeneration.smlstub new file mode 100644 index 000000000..e669ba390 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/resources/stdlib/simpleml/lang/codeGeneration.smlstub @@ -0,0 +1,24 @@ +package simpleml.lang + +@Description("The qualified name of the corresponding Python module (default is the qualified name of the package).") +@Target(AnnotationTarget.CompilationUnit) +annotation PythonModule( + @Description("The qualified name of the corresponding Python module.") + qualifiedName: String +) + +@Description("The name of the corresponding API element in Python (default is the name of the declaration in the stubs).") +@Target( + AnnotationTarget.Attribute, + AnnotationTarget.Class, + AnnotationTarget.Enum, + AnnotationTarget.EnumVariant, + AnnotationTarget.Function, + AnnotationTarget.Parameter, + AnnotationTarget.Step, + AnnotationTarget.Workflow +) +annotation PythonName( + @Description("The name of the corresponding API element in Python.") + name: String +) diff --git a/DSL/de.unibonn.simpleml/src/main/resources/stdlib/simpleml/lang/coreAnnotations.smlstub b/DSL/de.unibonn.simpleml/src/main/resources/stdlib/simpleml/lang/coreAnnotations.smlstub new file mode 100644 index 000000000..a7ed91762 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/resources/stdlib/simpleml/lang/coreAnnotations.smlstub @@ -0,0 +1,90 @@ +package simpleml.lang + +@Description("The annotation can target these declaration types. If the @Target annotation is not used any declaration type can be targeted.") +@Target(AnnotationTarget.Annotation) +annotation Target( + @Description("The valid targets.") + vararg targets: AnnotationTarget +) + +@Description("The declaration types that can be targeted by annotations.") +enum AnnotationTarget { + @Description("The annotation can be called on annotations.") + Annotation + + @Description("The annotation can be called on attributes.") + Attribute + + @Description("The annotation can be called on classes.") + Class + + @Description("The annotation can be called on compilation units (i.e. files).") + CompilationUnit + + @Description("The annotation can be called on enums.") + Enum + + @Description("The annotation can be called on enum variants.") + EnumVariant + + @Description("The annotation can be called on functions.") + Function + + @Description("The annotation can be called on parameters.") + Parameter + + @Description("The annotation can be called on results.") + Result + + @Description("The annotation can be called on steps.") + Step + + @Description("The annotation can be called on type parameters.") + TypeParameter + + @Description("The annotation can be called on workflows.") + Workflow +} + +@Description("The annotation can be called multiple times for the same declaration.") +@Target(AnnotationTarget.Annotation) +annotation Repeatable + +@Description("The declaration should no longer be used.") +@Target( + AnnotationTarget.Annotation, + AnnotationTarget.Attribute, + AnnotationTarget.Class, + AnnotationTarget.Enum, + AnnotationTarget.EnumVariant, + AnnotationTarget.Function, + AnnotationTarget.Parameter, + AnnotationTarget.Result, + AnnotationTarget.Step, + AnnotationTarget.TypeParameter, +) +annotation Deprecated( + @Description("What to use instead.") + alternative: String? = null, + + @Description("Why the declaration was deprecated.") + reason: String? = null, + + @Description("When the declaration was deprecated.") + sinceVersion: String? = null, + + @Description("When the declaration will be removed.") + removalVersion: String? = null, +) + +@Description("The function has no side effects and returns the same results for the same arguments.") +@Target(AnnotationTarget.Function) +annotation Pure + +@Description("The function has no side effects.") +@Target(AnnotationTarget.Function) +annotation NoSideEffects + +@Description("Values assigned to this parameter must be constant.") +@Target(AnnotationTarget.Parameter) +annotation Constant diff --git a/DSL/de.unibonn.simpleml/src/main/resources/stdlib/simpleml/lang/coreClasses.smlstub b/DSL/de.unibonn.simpleml/src/main/resources/stdlib/simpleml/lang/coreClasses.smlstub new file mode 100644 index 000000000..a46e0a1e6 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/resources/stdlib/simpleml/lang/coreClasses.smlstub @@ -0,0 +1,22 @@ +package simpleml.lang + +@Description("The common superclass of all classes.") +class Any + +@Description("The common subclass of all classes.") +class Nothing + +@Description("A truth value.") +class Boolean + +@Description("A number.") +class Number + +@Description("An integer.") +class Int sub Number + +@Description("A floating-point number.") +class Float sub Number + +@Description("Some text.") +class String diff --git a/DSL/de.unibonn.simpleml/src/main/resources/stdlib/simpleml/lang/documentation.smlstub b/DSL/de.unibonn.simpleml/src/main/resources/stdlib/simpleml/lang/documentation.smlstub new file mode 100644 index 000000000..08a965e3c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/main/resources/stdlib/simpleml/lang/documentation.smlstub @@ -0,0 +1,17 @@ +package simpleml.lang + +@Description("The purpose of a declaration.") +annotation Description( + @Description("The purpose of a declaration.") + description: String +) + +@Description("The version in which a declaration was added.") +annotation Since( + @Description("The version in which a declaration was added.") + version: String +) + +@Description("This parameter should only be used by expert users.") +@Target(AnnotationTarget.Parameter) +annotation Expert diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/IssueFinderTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/IssueFinderTest.kt new file mode 100644 index 000000000..dc33a1b85 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/IssueFinderTest.kt @@ -0,0 +1,216 @@ +package de.unibonn.simpleml + +import com.google.inject.Inject +import de.unibonn.simpleml.emf.OriginalFilePath +import de.unibonn.simpleml.testing.CategorizedTest +import de.unibonn.simpleml.testing.FindTestRangesResult +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.assertions.ExpectedIssue +import de.unibonn.simpleml.testing.assertions.shouldHaveNoIssue +import de.unibonn.simpleml.testing.assertions.shouldHaveNoSemanticError +import de.unibonn.simpleml.testing.assertions.shouldHaveNoSemanticInfo +import de.unibonn.simpleml.testing.assertions.shouldHaveNoSemanticWarning +import de.unibonn.simpleml.testing.assertions.shouldHaveNoSyntaxError +import de.unibonn.simpleml.testing.assertions.shouldHaveSemanticError +import de.unibonn.simpleml.testing.assertions.shouldHaveSemanticInfo +import de.unibonn.simpleml.testing.assertions.shouldHaveSemanticWarning +import de.unibonn.simpleml.testing.assertions.shouldHaveSyntaxError +import de.unibonn.simpleml.testing.assertions.stringify +import de.unibonn.simpleml.testing.createDynamicTestsFromResourceFolder +import de.unibonn.simpleml.testing.findTestRanges +import de.unibonn.simpleml.testing.getResourcePath +import de.unibonn.simpleml.testing.testDisplayName +import de.unibonn.simpleml.utils.outerZipBy +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.eclipse.xtext.testing.validation.ValidationTestHelper +import org.eclipse.xtext.validation.Issue +import org.junit.jupiter.api.DynamicNode +import org.junit.jupiter.api.DynamicTest.dynamicTest +import org.junit.jupiter.api.TestFactory +import org.junit.jupiter.api.extension.ExtendWith +import java.nio.file.Path +import java.util.stream.Stream + +private const val SYNTAX_ERROR = "syntax_error" +private const val NO_SYNTAX_ERROR = "no_syntax_error" +private const val SEMANTIC_ERROR = "semantic_error" +private const val NO_SEMANTIC_ERROR = "no_semantic_error" +private const val SEMANTIC_WARNING = "semantic_warning" +private const val NO_SEMANTIC_WARNING = "no_semantic_warning" +private const val SEMANTIC_INFO = "semantic_info" +private const val NO_SEMANTIC_INFO = "no_semantic_info" +private const val NO_ISSUE = "no_issue" +private val validSeverities = setOf( + SYNTAX_ERROR, + NO_SYNTAX_ERROR, + SEMANTIC_ERROR, + NO_SEMANTIC_ERROR, + SEMANTIC_WARNING, + NO_SEMANTIC_WARNING, + SEMANTIC_INFO, + NO_SEMANTIC_INFO, + NO_ISSUE, +) +private val semanticSeverities = setOf( + SEMANTIC_ERROR, + NO_SEMANTIC_ERROR, + SEMANTIC_WARNING, + NO_SEMANTIC_WARNING, + SEMANTIC_INFO, + NO_SEMANTIC_INFO, +) + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class IssueFinderTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + @Inject + private lateinit var validationHelper: ValidationTestHelper + + @TestFactory + fun `should parse test files correctly`(): Stream { + return javaClass.classLoader + .getResourcePath("grammar") + ?.createDynamicTestsFromResourceFolder(::validateTestFile, ::createTest) + ?: Stream.empty() + } + + @TestFactory + fun `should validate test files correctly`(): Stream { + return javaClass.classLoader + .getResourcePath("validation") + ?.createDynamicTestsFromResourceFolder(::validateTestFile, ::createTest) + ?: Stream.empty() + } + + /** + * Checks if the given program is a valid test. If there are issues a description of the issue is returned, + * otherwise this returns `null`. + */ + private fun validateTestFile(resourcePath: Path, filePath: Path, program: String): String? { + val severities = severities(program) + + // Must contain at least one severity + if (severities.isEmpty()) { + return "No expected issue is specified." + } + + // Severities must be valid + severities.forEach { + if (it !in validSeverities) { + return "Severity '$it' is invalid." + } + } + + // Opening and closing test markers must match + val locations = when (val locationsResult = findTestRanges(program)) { + is FindTestRangesResult.Success -> locationsResult.ranges + is FindTestRangesResult.Failure -> return locationsResult.message + } + + // Must not contain more locations markers than severities + if (severities.size < locations.size) { + return "Test file contains more locations (»«) than severities." + } + + // Must be able to parse the test file + if (parseHelper.parseProgramText(program) == null) { + return "Could not parse test file." + } + + // Must not combine syntax errors with checks of semantic errors + if (severities.intersect(semanticSeverities).isNotEmpty()) { + if (severities.contains(SYNTAX_ERROR)) { + return "Cannot combine severity 'syntax_error' with check of semantic errors." + } + + val syntaxErrors = actualIssues(program, filePath).filter { it.isSyntaxError } + if (syntaxErrors.isNotEmpty()) { + return "File checks for semantic issues but has syntax errors${syntaxErrors.stringify()}" + } + } + + return null + } + + private fun createTest(resourcePath: Path, filePath: Path, program: String) = sequence { + expectedIssues(program) + .groupBy { it.severity to it.message } + .keys + .forEach { (severity, message) -> + yield( + CategorizedTest( + severity, + dynamicTest(testDisplayName(resourcePath, filePath, message), filePath.toUri()) { + parsingTest(program, filePath, severity, message) + } + ) + ) + } + } + + private fun parsingTest(program: String, filePath: Path, severity: String, message: String) { + val actualIssues = actualIssues(program, filePath) + expectedIssues(program) + .filter { it.severity == severity && it.message == message } + .forEach { + when (it.severity) { + SYNTAX_ERROR -> actualIssues.shouldHaveSyntaxError(it) + NO_SYNTAX_ERROR -> actualIssues.shouldHaveNoSyntaxError(it) + SEMANTIC_ERROR -> actualIssues.shouldHaveSemanticError(it) + NO_SEMANTIC_ERROR -> actualIssues.shouldHaveNoSemanticError(it) + SEMANTIC_WARNING -> actualIssues.shouldHaveSemanticWarning(it) + NO_SEMANTIC_WARNING -> actualIssues.shouldHaveNoSemanticWarning(it) + SEMANTIC_INFO -> actualIssues.shouldHaveSemanticInfo(it) + NO_SEMANTIC_INFO -> actualIssues.shouldHaveNoSemanticInfo(it) + NO_ISSUE -> actualIssues.shouldHaveNoIssue(it) + } + } + } + + private fun expectedIssues(program: String): List { + val locations = when (val locationsResult = findTestRanges(program)) { + is FindTestRangesResult.Success -> locationsResult.ranges + else -> return emptyList() + } + + return outerZipBy(severitiesAndMessages(program), locations) { severityAndMessage, location -> + ExpectedIssue( + severityAndMessage!!.severity, + severityAndMessage.message, + severityAndMessage.messageIsRegex, + location + ) + } + } + + private fun severities(program: String): List { + return severitiesAndMessages(program).map { it.severity } + } + + private fun severitiesAndMessages(program: String): List { + return """//\s*(?\S+)\s*(?:(?r)?"(?[^"]*)")?""" + .toRegex() + .findAll(program) + .map { + ExpectedIssue( + it.groupValues[1], + it.groupValues[3], + it.groupValues[2] == "r", + null + ) + } + .toList() + } + + private fun actualIssues(program: String, filePath: Path): List { + val parsingResult = parseHelper.parseProgramText(program) ?: return emptyList() + parsingResult.eResource().eAdapters().add(OriginalFilePath(filePath.toString())) + return validationHelper.validate(parsingResult) + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/StdlibTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/StdlibTest.kt new file mode 100644 index 000000000..6cbcfb8b4 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/StdlibTest.kt @@ -0,0 +1,51 @@ +package de.unibonn.simpleml + +import com.google.inject.Inject +import de.unibonn.simpleml.emf.OriginalFilePath +import de.unibonn.simpleml.stdlibAccess.listStdlibFiles +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.assertions.shouldHaveNoErrorsOrWarnings +import io.kotest.matchers.nulls.shouldNotBeNull +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.eclipse.xtext.testing.validation.ValidationTestHelper +import org.junit.jupiter.api.DynamicNode +import org.junit.jupiter.api.DynamicTest +import org.junit.jupiter.api.TestFactory +import org.junit.jupiter.api.extension.ExtendWith +import java.nio.file.Files +import java.util.stream.Stream + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class StdlibTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + @Inject + private lateinit var validationHelper: ValidationTestHelper + + @TestFactory + fun `should not have syntax or semantic errors`(): Stream { + val allStdlibFiles = listStdlibFiles().map { it.first.toString() }.toList() + + return listStdlibFiles() + .map { (filePath, _) -> + + // We must do this here and not in the callback of the dynamicTest so the JAR file system is still open + val otherStdlibFiles = allStdlibFiles - filePath.toString() + val program = Files.readString(filePath) + val parsingResult = parseHelper.parseProgramText(program, context = otherStdlibFiles, loadStdlib = false) + + DynamicTest.dynamicTest(filePath.toString(), filePath.toUri()) { + parsingResult.shouldNotBeNull() + parsingResult.eResource().eAdapters().add(OriginalFilePath(filePath.toString())) + validationHelper.validate(parsingResult).shouldHaveNoErrorsOrWarnings() + } + } + .toList() + .stream() + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLIDValueConverterTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLIDValueConverterTest.kt new file mode 100644 index 000000000..2af08b5f5 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLIDValueConverterTest.kt @@ -0,0 +1,88 @@ +@file:Suppress("ClassName") + +package de.unibonn.simpleml.conversion + +import com.google.inject.Inject +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.emf.createSmlClass +import de.unibonn.simpleml.emf.createSmlCompilationUnit +import de.unibonn.simpleml.emf.createSmlDummyResource +import de.unibonn.simpleml.serializer.SerializationResult +import de.unibonn.simpleml.serializer.serializeToFormattedString +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.assertions.findUniqueDeclarationOrFail +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf +import org.eclipse.xtext.conversion.impl.IDValueConverter +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class SimpleMLIDValueConverterTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + @Inject + private lateinit var idValueConverter: IDValueConverter + + @Nested + inner class toValue { + @Test + fun `should remove backticks (direct converter call)`() { + idValueConverter.toValue("`package`", null) shouldBe "package" + } + + @Test + fun `should remove backticks (file)`() { + val compilationUnit = + parseHelper.parseResource("conversion/idValueConverter.smltest") + compilationUnit.shouldNotBeNull() + + val `class` = compilationUnit.findUniqueDeclarationOrFail("class") + `class`.shouldNotBeNull() + } + } + + @Nested + inner class toString { + @Test + fun `should escape keywords (direct converter call)`() { + idValueConverter.toString("package") shouldBe "`package`" + } + + @Test + fun `should escape keywords (creator)`() { + val `class` = createSmlClass("class") + val compilationUnit = createSmlCompilationUnit(packageName = "test", members = listOf(`class`)) + createSmlDummyResource("test", SmlFileExtension.Test, compilationUnit) + + val result = `class`.serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "class `class`" + } + + @Test + fun `should not escape non-keywords (direct converter call)`() { + idValueConverter.toString("notAKeyword") shouldBe "notAKeyword" + } + + @Test + fun `should not escape non-keywords (creator)`() { + val `class` = createSmlClass("notAKeyword") + val compilationUnit = createSmlCompilationUnit(packageName = "notAKeyword", members = listOf(`class`)) + createSmlDummyResource("test", SmlFileExtension.Test, compilationUnit) + + val result = `class`.serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "class notAKeyword" + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLINTValueConverterTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLINTValueConverterTest.kt new file mode 100644 index 000000000..b88cc2da2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLINTValueConverterTest.kt @@ -0,0 +1,81 @@ +@file:Suppress("ClassName") + +package de.unibonn.simpleml.conversion + +import com.google.inject.Inject +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.emf.createSmlDummyResource +import de.unibonn.simpleml.emf.createSmlInt +import de.unibonn.simpleml.emf.descendants +import de.unibonn.simpleml.emf.smlExpressionStatement +import de.unibonn.simpleml.emf.smlWorkflow +import de.unibonn.simpleml.serializer.SerializationResult +import de.unibonn.simpleml.serializer.serializeToFormattedString +import de.unibonn.simpleml.simpleML.SmlInt +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf +import org.eclipse.xtext.conversion.impl.INTValueConverter +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class SimpleMLINTValueConverterTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + @Inject + private lateinit var intValueConverter: INTValueConverter + + @Nested + inner class toValue { + @Test + fun `should convert string to int (direct converter call)`() { + intValueConverter.toValue("1", null) shouldBe 1 + } + + @Test + fun `should convert string to int (file)`() { + val compilationUnit = parseHelper.parseResource( + "conversion/intValueConverter.smltest" + ) // readProgramTextFromResource(resourceName)?.let { parseHelper.parse(it) } + compilationUnit.shouldNotBeNull() + + val int = compilationUnit.descendants().toList() + int.shouldHaveSize(1) + + int[0].value shouldBe 1 + } + } + + @Nested + inner class toString { + @Test + fun `should convert int to string (direct converter call)`() { + intValueConverter.toString(1) shouldBe "1" + } + + @Test + fun `should convert int to string (creator)`() { + val int = createSmlInt(1) + + createSmlDummyResource(fileName = "test", SmlFileExtension.Test, packageName = "test") { + smlWorkflow("test") { + smlExpressionStatement(int) + } + } + + val result = int.serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "1" + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLQualifiedNameValueConverterTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLQualifiedNameValueConverterTest.kt new file mode 100644 index 000000000..5470ccb19 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLQualifiedNameValueConverterTest.kt @@ -0,0 +1,149 @@ +@file:Suppress("ClassName") + +package de.unibonn.simpleml.conversion + +import com.google.inject.Inject +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.emf.createSmlCompilationUnit +import de.unibonn.simpleml.emf.createSmlDummyResource +import de.unibonn.simpleml.emf.createSmlImport +import de.unibonn.simpleml.serializer.SerializationResult +import de.unibonn.simpleml.serializer.serializeToFormattedString +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf +import org.eclipse.xtext.conversion.impl.QualifiedNameValueConverter +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class SimpleMLQualifiedNameValueConverterTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + @Inject + private lateinit var qualifiedNameValueConverter: QualifiedNameValueConverter + + @Nested + inner class toValue { + @Test + fun `should remove backticks (direct converter call, no wildcard)`() { + qualifiedNameValueConverter.toValue("simpleml.`package`", null) shouldBe "simpleml.package" + } + + @Test + fun `should remove backticks (direct converter call, with wildcard)`() { + qualifiedNameValueConverter.toValue("simpleml.`package`.*", null) shouldBe "simpleml.package.*" + } + + @Test + fun `should remove backticks (file, no wildcard)`() { + val compilationUnit = + parseHelper.parseResource("conversion/qualifiedNameValueConverter.smltest") + compilationUnit.shouldNotBeNull() + + compilationUnit.name shouldBe "simpleml.package" + } + + @Test + fun `should remove backticks (file, with wildcard)`() { + val compilationUnit = + parseHelper.parseResource("conversion/qualifiedNameValueConverter.smltest") + compilationUnit.shouldNotBeNull() + + compilationUnit.name.shouldNotBeNull() + + val imports = compilationUnit.imports + imports.shouldHaveSize(1) + + imports[0].importedNamespace shouldBe "simpleml.package.*" + } + } + + @Nested + inner class toString { + @Test + fun `should escape keywords (direct converter call, no wildcard)`() { + qualifiedNameValueConverter.toString("simpleml.package") shouldBe "simpleml.`package`" + } + + @Test + fun `should escape keywords (direct converter call, with wildcard)`() { + qualifiedNameValueConverter.toString("simpleml.package.*") shouldBe "simpleml.`package`.*" + } + + @Test + fun `should escape keywords (creator, no wildcard)`() { + val compilationUnit = createSmlCompilationUnit(packageName = "simpleml.package") + createSmlDummyResource( + "test", + SmlFileExtension.Test, + compilationUnit + ) + + val result = compilationUnit.serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "package simpleml.`package`" + } + + @Test + fun `should escape keywords (creator, with wildcard)`() { + val import = createSmlImport("simpleml.package.*") + createSmlDummyResource( + fileName = "test", + SmlFileExtension.Test, + createSmlCompilationUnit(packageName = "test", imports = listOf(import)) + ) + + val result = import.serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "import simpleml.`package`.*" + } + + @Test + fun `should not escape non-keywords (direct converter call, no wildcard)`() { + qualifiedNameValueConverter.toString("simpleml.notAKeyword") shouldBe "simpleml.notAKeyword" + } + + @Test + fun `should not escape non-keywords (direct converter call, with wildcard)`() { + qualifiedNameValueConverter.toString("simpleml.notAKeyword.*") shouldBe "simpleml.notAKeyword.*" + } + + @Test + fun `should not escape non-keywords (creator, no wildcard)`() { + val compilationUnit = createSmlCompilationUnit(packageName = "simpleml.notAKeyword") + createSmlDummyResource( + "test", + SmlFileExtension.Test, + compilationUnit + ) + + val result = compilationUnit.serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "package simpleml.notAKeyword" + } + + @Test + fun `should not escape non-keywords (creator, with wildcard)`() { + val import = createSmlImport("simpleml.notAKeyword.*") + createSmlDummyResource( + fileName = "test", + SmlFileExtension.Test, + createSmlCompilationUnit(packageName = "test", imports = listOf(import)) + ) + + val result = import.serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "import simpleml.notAKeyword.*" + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLSTRINGValueConverterTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLSTRINGValueConverterTest.kt new file mode 100644 index 000000000..788b81029 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLSTRINGValueConverterTest.kt @@ -0,0 +1,169 @@ +@file:Suppress("ClassName") + +package de.unibonn.simpleml.conversion + +import com.google.inject.Inject +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.emf.createSmlDummyResource +import de.unibonn.simpleml.emf.createSmlString +import de.unibonn.simpleml.emf.descendants +import de.unibonn.simpleml.emf.smlExpressionStatement +import de.unibonn.simpleml.emf.smlWorkflow +import de.unibonn.simpleml.serializer.SerializationResult +import de.unibonn.simpleml.serializer.serializeToFormattedString +import de.unibonn.simpleml.simpleML.SmlString +import de.unibonn.simpleml.simpleML.SmlWorkflow +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.assertions.findUniqueDeclarationOrFail +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf +import org.eclipse.xtext.conversion.impl.STRINGValueConverter +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class SimpleMLSTRINGValueConverterTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + @Inject + private lateinit var stringValueConverter: STRINGValueConverter + + @Nested + inner class toValue { + @Test + fun `should unescape opening curly brace (direct converter call)`() { + stringValueConverter.toValue("\"\\{\"", null) shouldBe "{" + } + + @Test + fun `should unescape opening curly brace (file)`() { + val compilationUnit = + parseHelper.parseResource("conversion/stringValueConverter.smltest") + compilationUnit.shouldNotBeNull() + + val workflow = compilationUnit.findUniqueDeclarationOrFail("escapedOpeningBrace") + + val strings = workflow.descendants().toList() + strings.shouldHaveSize(1) + strings[0].value shouldBe "{" + } + + @Test + fun `should unescape single quote (direct converter call)`() { + stringValueConverter.toValue("\"\\'\"", null) shouldBe "'" + } + + @Test + fun `should unescape single quote (file)`() { + val compilationUnit = + parseHelper.parseResource("conversion/stringValueConverter.smltest") + compilationUnit.shouldNotBeNull() + + val workflow = compilationUnit.findUniqueDeclarationOrFail("escapedSingleQuote") + + val strings = workflow.descendants().toList() + strings.shouldHaveSize(1) + strings[0].value shouldBe "'" + } + } + + @Nested + inner class toString { + @Test + fun `should escape opening curly brace (direct converter call)`() { + stringValueConverter.toString("{") shouldBe "\"\\{\"" + } + + @Test + fun `should keep escaped opening curly brace (file)`() { + val compilationUnit = + parseHelper.parseResource("conversion/stringValueConverter.smltest") + compilationUnit.shouldNotBeNull() + + val workflow = compilationUnit.findUniqueDeclarationOrFail("escapedOpeningBrace") + + val strings = workflow.descendants().toList() + strings.shouldHaveSize(1) + + val result = strings[0].serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "\"\\{\"" + } + + @Test + fun `should keep unescaped opening curly brace (file)`() { + val compilationUnit = + parseHelper.parseResource("conversion/stringValueConverter.smltest") + compilationUnit.shouldNotBeNull() + + val workflow = compilationUnit.findUniqueDeclarationOrFail("unescapedOpeningBrace") + + val strings = workflow.descendants().toList() + strings.shouldHaveSize(1) + + val result = strings[0].serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "\"{\"" + } + + @Test + fun `should always escape opening curly brace (creator)`() { + val string = createSmlString("{") + + createSmlDummyResource(fileName = "test", SmlFileExtension.Test, packageName = "test") { + smlWorkflow("test") { + smlExpressionStatement(string) + } + } + + val result = string.serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "\"\\{\"" + } + + @Test + fun `should not escape single quote (direct converter call)`() { + stringValueConverter.toString("'") shouldBe "\"'\"" + } + + @Test + fun `should not escape single quote (file)`() { + val compilationUnit = + parseHelper.parseResource("conversion/stringValueConverter.smltest") + compilationUnit.shouldNotBeNull() + + val workflow = compilationUnit.findUniqueDeclarationOrFail("unescapedSingleQuote") + + val strings = workflow.descendants().toList() + strings.shouldHaveSize(1) + + val result = strings[0].serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "\"'\"" + } + + @Test + fun `should not escape single quote (creator)`() { + val string = createSmlString("'") + + createSmlDummyResource(fileName = "test", SmlFileExtension.Test, packageName = "test") { + smlWorkflow("test") { + smlExpressionStatement(string) + } + } + + val result = string.serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "\"'\"" + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_ENDValueConverterTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_ENDValueConverterTest.kt new file mode 100644 index 000000000..7450dc03e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_ENDValueConverterTest.kt @@ -0,0 +1,87 @@ +@file:Suppress("ClassName") + +package de.unibonn.simpleml.conversion + +import com.google.inject.Inject +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.emf.createSmlDummyResource +import de.unibonn.simpleml.emf.createSmlNull +import de.unibonn.simpleml.emf.createSmlTemplateString +import de.unibonn.simpleml.emf.descendants +import de.unibonn.simpleml.emf.smlExpressionStatement +import de.unibonn.simpleml.emf.smlWorkflow +import de.unibonn.simpleml.serializer.SerializationResult +import de.unibonn.simpleml.serializer.serializeToFormattedString +import de.unibonn.simpleml.simpleML.SmlTemplateStringEnd +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class SimpleMLTEMPLATE_STRING_ENDValueConverterTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + @Inject + private lateinit var templateStringEndValueConverter: SimpleMLTEMPLATE_STRING_ENDValueConverter + + @Nested + inner class toValue { + @Test + fun `should remove delimiters (direct converter call)`() { + templateStringEndValueConverter.toValue("}}end\"", null) shouldBe "end" + } + + @Test + fun `should remove delimiters (file)`() { + val compilationUnit = parseHelper.parseResource( + "conversion/templateStringPartValueConverter.smltest" + ) // readProgramTextFromResource(resourceName)?.let { parseHelper.parse(it) } + compilationUnit.shouldNotBeNull() + + val stringTemplateParts = compilationUnit.descendants().toList() + stringTemplateParts.shouldHaveSize(1) + + stringTemplateParts[0].value shouldBe "end" + } + } + + @Nested + inner class toString { + @Test + fun `should add delimiters (direct converter call)`() { + templateStringEndValueConverter.toString("end") shouldBe "}}end\"" + } + + @Test + fun `should add delimiters (creator)`() { + val stringTemplate = createSmlTemplateString( + listOf("start", "end"), + listOf(createSmlNull()) + ) + + createSmlDummyResource(fileName = "test", SmlFileExtension.Test, packageName = "test") { + smlWorkflow("test") { + smlExpressionStatement(stringTemplate) + } + } + + val expressions = stringTemplate.expressions + expressions.shouldHaveSize(3) + + val result = expressions[2].serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "}}end\"" + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_INNERValueConverterTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_INNERValueConverterTest.kt new file mode 100644 index 000000000..b6a2f2fcc --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_INNERValueConverterTest.kt @@ -0,0 +1,87 @@ +@file:Suppress("ClassName") + +package de.unibonn.simpleml.conversion + +import com.google.inject.Inject +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.emf.createSmlDummyResource +import de.unibonn.simpleml.emf.createSmlNull +import de.unibonn.simpleml.emf.createSmlTemplateString +import de.unibonn.simpleml.emf.descendants +import de.unibonn.simpleml.emf.smlExpressionStatement +import de.unibonn.simpleml.emf.smlWorkflow +import de.unibonn.simpleml.serializer.SerializationResult +import de.unibonn.simpleml.serializer.serializeToFormattedString +import de.unibonn.simpleml.simpleML.SmlTemplateStringInner +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class SimpleMLTEMPLATE_STRING_INNERValueConverterTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + @Inject + private lateinit var templateStringInnerValueConverter: SimpleMLTEMPLATE_STRING_INNERValueConverter + + @Nested + inner class toValue { + @Test + fun `should remove delimiters (direct converter call)`() { + templateStringInnerValueConverter.toValue("}}inner{{", null) shouldBe "inner" + } + + @Test + fun `should remove delimiters (file)`() { + val compilationUnit = parseHelper.parseResource( + "conversion/templateStringPartValueConverter.smltest" + ) // readProgramTextFromResource(resourceName)?.let { parseHelper.parse(it) } + compilationUnit.shouldNotBeNull() + + val stringTemplateParts = compilationUnit.descendants().toList() + stringTemplateParts.shouldHaveSize(1) + + stringTemplateParts[0].value shouldBe "inner" + } + } + + @Nested + inner class toString { + @Test + fun `should add delimiters (direct converter call)`() { + templateStringInnerValueConverter.toString("inner") shouldBe "}}inner{{" + } + + @Test + fun `should add delimiters (creator)`() { + val stringTemplate = createSmlTemplateString( + listOf("start", "inner", "end"), + listOf(createSmlNull(), createSmlNull()) + ) + + createSmlDummyResource(fileName = "test", SmlFileExtension.Test, packageName = "test") { + smlWorkflow("test") { + smlExpressionStatement(stringTemplate) + } + } + + val expressions = stringTemplate.expressions + expressions.shouldHaveSize(5) + + val result = expressions[2].serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "}}inner{{" + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_STARTValueConverterTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_STARTValueConverterTest.kt new file mode 100644 index 000000000..33dc58300 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion/SimpleMLTEMPLATE_STRING_STARTValueConverterTest.kt @@ -0,0 +1,87 @@ +@file:Suppress("ClassName") + +package de.unibonn.simpleml.conversion + +import com.google.inject.Inject +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.emf.createSmlDummyResource +import de.unibonn.simpleml.emf.createSmlNull +import de.unibonn.simpleml.emf.createSmlTemplateString +import de.unibonn.simpleml.emf.descendants +import de.unibonn.simpleml.emf.smlExpressionStatement +import de.unibonn.simpleml.emf.smlWorkflow +import de.unibonn.simpleml.serializer.SerializationResult +import de.unibonn.simpleml.serializer.serializeToFormattedString +import de.unibonn.simpleml.simpleML.SmlTemplateStringStart +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class SimpleMLTEMPLATE_STRING_STARTValueConverterTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + @Inject + private lateinit var templateStringStartValueConverter: SimpleMLTEMPLATE_STRING_STARTValueConverter + + @Nested + inner class toValue { + @Test + fun `should remove delimiters (direct converter call)`() { + templateStringStartValueConverter.toValue("\"start{{", null) shouldBe "start" + } + + @Test + fun `should remove delimiters (file)`() { + val compilationUnit = parseHelper.parseResource( + "conversion/templateStringPartValueConverter.smltest" + ) // readProgramTextFromResource(resourceName)?.let { parseHelper.parse(it) } + compilationUnit.shouldNotBeNull() + + val stringTemplateParts = compilationUnit.descendants().toList() + stringTemplateParts.shouldHaveSize(1) + + stringTemplateParts[0].value shouldBe "start" + } + } + + @Nested + inner class toString { + @Test + fun `should add delimiters (direct converter call)`() { + templateStringStartValueConverter.toString("start") shouldBe "\"start{{" + } + + @Test + fun `should add delimiters (creator)`() { + val stringTemplate = createSmlTemplateString( + listOf("start", "end"), + listOf(createSmlNull()) + ) + + createSmlDummyResource(fileName = "test", SmlFileExtension.Test, packageName = "test") { + smlWorkflow("test") { + smlExpressionStatement(stringTemplate) + } + } + + val expressions = stringTemplate.expressions + expressions.shouldHaveSize(3) + + val result = expressions[0].serializeToFormattedString() + result.shouldBeInstanceOf() + result.code shouldBe "\"start{{" + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/emf/CreatorsTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/emf/CreatorsTest.kt new file mode 100644 index 000000000..aa8989e5a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/emf/CreatorsTest.kt @@ -0,0 +1,820 @@ +package de.unibonn.simpleml.emf + +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.constant.SmlPrefixOperationOperator +import de.unibonn.simpleml.constant.SmlProtocolTokenClassValue +import de.unibonn.simpleml.constant.SmlTypeParameterConstraintOperator +import de.unibonn.simpleml.serializer.SerializationResult +import de.unibonn.simpleml.serializer.serializeToFormattedString +import de.unibonn.simpleml.simpleML.SmlFloat +import de.unibonn.simpleml.simpleML.SmlInt +import de.unibonn.simpleml.simpleML.SmlLambdaParameterList +import de.unibonn.simpleml.simpleML.SmlParameterList +import de.unibonn.simpleml.simpleML.SmlPrefixOperation +import de.unibonn.simpleml.simpleML.SmlProtocol +import de.unibonn.simpleml.simpleML.SmlTemplateStringEnd +import de.unibonn.simpleml.simpleML.SmlTemplateStringInner +import de.unibonn.simpleml.simpleML.SmlTemplateStringStart +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.assertions.shouldBeCloseTo +import io.kotest.assertions.asClue +import io.kotest.assertions.throwables.shouldNotThrow +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +/** + * Includes tests for the (extension) functions in Creators.kt. Since most of the functions are straightforward, not + * everything is being tested. These are the guidelines for what should be tested: + * + * - Handling of annotations (features annotationCallList vs. annotations) + * - Extension functions should add created object to receiver + * - Creators for objects with cross-references that take a name instead of the referenced object + * - Should not create unnecessary syntax (like empty class bodies) + * + * There are also some special tests: + * - Dummy resource should be serializable + * - Assignments requires at least one assignee + * - Template string creator should check structure of template string + * - Union type requires at least one type argument + */ +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class CreatorsTest { + + @Test + fun `createSmlDummyResource should create serializable dummy resource`() { + val result = createSmlDummyResource(fileName = "test", SmlFileExtension.Test, packageName = "test") + + result.contents.shouldHaveSize(1) + result.contents[0].serializeToFormattedString().shouldBeInstanceOf() + } + + @Test + fun `createSmlAnnotation should store annotation uses in annotationCallList`() { + val annotation = createSmlAnnotation( + "Test", + listOf(createSmlAnnotationCall("Test")) + ) + + annotation.annotationCalls.shouldHaveSize(0) + + val annotationCallList = annotation.annotationCallList + annotationCallList.shouldNotBeNull() + annotationCallList.annotationCalls.shouldHaveSize(1) + } + + @Test + fun `createSmlAnnotation should omit empty parameter lists`() { + val annotation = createSmlAnnotation( + "Test", + parameters = emptyList() + ) + + annotation.parameterList.shouldBeNull() + } + + @Test + fun `smlAnnotation should add the created annotation to the receiving compilation unit`() { + val compilationUnit = createSmlCompilationUnit(packageName = "test") { + smlAnnotation("Test") + } + + compilationUnit.members.shouldHaveSize(1) + } + + @Test + fun `createSmlAnnotationUse should omit empty argument lists`() { + val annotationUse = createSmlAnnotationCall( + "Test", + arguments = emptyList() + ) + annotationUse.argumentList.shouldBeNull() + } + + @Test + fun `createSmlAnnotationUse should create an SmlAnnotation when only a name is passed`() { + val annotationUse = createSmlAnnotationCall("Test") + val annotation = annotationUse.annotation + annotation.shouldNotBeNull() + annotation.name shouldBe "Test" + } + + @Test + fun `createSmlArgument should create an SmlParameter when only a name is passed`() { + val argument = createSmlArgument(createSmlInt(1), "Test") + val parameter = argument.parameter + parameter.shouldNotBeNull() + parameter.name shouldBe "Test" + } + + @Test + fun `smlAssignment should throw if no type arguments are passed`() { + shouldThrow { + createSmlAssignment(listOf(), createSmlInt(1)) + } + } + + @Test + fun `smlAssignment should add the created assignment to the receiving lambda`() { + val lambda = createSmlBlockLambda { + smlAssignment( + listOf(createSmlWildcard()), + createSmlInt(1) + ) + } + + val body = lambda.body + body.shouldNotBeNull() + body.statements.shouldHaveSize(1) + } + + @Test + fun `smlAssignment should add the created assignment to the receiving workflow`() { + val workflow = createSmlWorkflow("Test") { + smlAssignment( + listOf(createSmlWildcard()), + createSmlInt(1) + ) + } + + val body = workflow.body + body.shouldNotBeNull() + body.statements.shouldHaveSize(1) + } + + @Test + fun `smlAssignment should add the created assignment to the receiving step`() { + val step = createSmlStep("Test") { + smlAssignment( + listOf(createSmlWildcard()), + createSmlInt(1) + ) + } + + val body = step.body + body.shouldNotBeNull() + body.statements.shouldHaveSize(1) + } + + @Test + fun `createSmlAttribute should store annotation uses in annotationCallList`() { + val attribute = createSmlAttribute( + "Test", + listOf(createSmlAnnotationCall("Test")) + ) + + attribute.annotationCalls.shouldHaveSize(0) + + val annotationCallList = attribute.annotationCallList + annotationCallList.shouldNotBeNull() + annotationCallList.annotationCalls.shouldHaveSize(1) + } + + @Test + fun `smlAttribute should add the created attribute to the receiver`() { + val `class` = createSmlClass("Test") { + smlAttribute("Test") + } + + val body = `class`.body + body.shouldNotBeNull() + body.members.shouldHaveSize(1) + } + + @Test + fun `createSmlBlockLambda should not omit empty parameter lists`() { + val lambda = createSmlBlockLambda(parameters = emptyList()) + lambda.parameterList.shouldBeInstanceOf() + } + + @Test + fun `createSmlBlockLambda should use a lambda parameter list for parameters`() { + val lambda = createSmlBlockLambda() + lambda.parameterList.shouldBeInstanceOf() + } + + @Test + fun `createSmlBlockLambda should create a serializable block lambda`() { + val lambda = createSmlBlockLambda() + + createSmlDummyResource(fileName = "test", SmlFileExtension.Test, packageName = "test") { + smlWorkflow(name = "test") { + smlExpressionStatement(lambda) + } + } + + lambda.serializeToFormattedString().shouldBeInstanceOf() + } + + @Test + fun `createSmlCall should omit empty type argument lists`() { + val call = createSmlCall( + createSmlNull(), + typeArguments = emptyList() + ) + call.typeArgumentList.shouldBeNull() + } + + @Test + fun `createSmlClass should store annotation uses in annotationCallList`() { + val `class` = createSmlClass( + "Test", + listOf(createSmlAnnotationCall("Test")) + ) + + `class`.annotationCalls.shouldHaveSize(0) + + val annotationCallList = `class`.annotationCallList + annotationCallList.shouldNotBeNull() + annotationCallList.annotationCalls.shouldHaveSize(1) + } + + @Test + fun `createSmlClass should omit empty body`() { + val `class` = createSmlClass( + "Test", + members = emptyList() + ) + `class`.body.shouldBeNull() + } + + @Test + fun `createSmlClass should not omit empty parameter lists`() { + val `class` = createSmlClass( + "Test", + parameters = emptyList() + ) + `class`.parameterList.shouldBeInstanceOf() + } + + @Test + fun `createSmlClass should omit empty parent type list`() { + val `class` = createSmlClass( + "Test", + parentTypes = emptyList() + ) + `class`.parentTypeList.shouldBeNull() + } + + @Test + fun `createSmlClass should omit empty type parameter list`() { + val `class` = createSmlClass( + "Test", + typeParameters = emptyList() + ) + `class`.typeParameterList.shouldBeNull() + } + + @Test + fun `createSmlClass should omit empty constraint list`() { + val `class` = createSmlClass( + "Test", + constraints = emptyList() + ) + `class`.constraintList.shouldBeNull() + } + + @Test + fun `smlClass should add the created class to the receiving class`() { + val `class` = createSmlClass("Test") { + smlClass("Test") + } + + val body = `class`.body + body.shouldNotBeNull() + body.members.shouldHaveSize(1) + } + + @Test + fun `smlClass should add the created class to the receiving compilation unit`() { + val compilationUnit = createSmlCompilationUnit(packageName = "test") { + smlClass("Test") + } + + compilationUnit.members.shouldHaveSize(1) + } + + @Test + fun `createSmlCompilationUnit should store annotation uses in annotationCalls`() { + val compilationUnit = createSmlCompilationUnit( + packageName = "test", + listOf(createSmlAnnotationCall("Test")) + ) + + compilationUnit.annotationCalls.shouldHaveSize(1) + compilationUnit.annotationCallList.shouldBeNull() + } + + @Test + fun `createSmlEnum should store annotation uses in annotationCallList`() { + val `enum` = createSmlEnum( + "Test", + listOf(createSmlAnnotationCall("Test")) + ) + + `enum`.annotationCalls.shouldHaveSize(0) + + val annotationCallList = `enum`.annotationCallList + annotationCallList.shouldNotBeNull() + annotationCallList.annotationCalls.shouldHaveSize(1) + } + + @Test + fun `createSmlEnum should omit empty body`() { + val enum = createSmlEnum( + "Test", + variants = emptyList() + ) + enum.body.shouldBeNull() + } + + @Test + fun `smlEnum should add the created enum to the receiving class`() { + val `class` = createSmlClass("Test") { + smlEnum("Test") + } + + val body = `class`.body + body.shouldNotBeNull() + body.members.shouldHaveSize(1) + } + + @Test + fun `smlEnum should add the created enum to the receiving compilation unit`() { + val compilationUnit = createSmlCompilationUnit(packageName = "test") { + smlEnum("Test") + } + + compilationUnit.members.shouldHaveSize(1) + } + + @Test + fun `createSmlEnumVariant should store annotation uses in annotations`() { + val variant = createSmlEnumVariant( + "Test", + listOf(createSmlAnnotationCall("Test")) + ) + + variant.annotationCalls.shouldHaveSize(1) + variant.annotationCallList.shouldBeNull() + } + + @Test + fun `createSmlEnumVariant should omit empty type parameter list`() { + val enum = createSmlEnumVariant( + "Test", + typeParameters = emptyList() + ) + enum.typeParameterList.shouldBeNull() + } + + @Test + fun `createSmlEnumVariant should omit empty parameter list`() { + val enum = createSmlEnumVariant( + "Test", + parameters = emptyList() + ) + enum.parameterList.shouldBeNull() + } + + @Test + fun `createSmlEnumVariant should omit empty constraint list`() { + val enum = createSmlEnumVariant( + "Test", + constraints = emptyList() + ) + enum.constraintList.shouldBeNull() + } + + @Test + fun `smlEnumVariant should add the created variant to the receiver`() { + val enum = createSmlEnum("Test") { + smlEnumVariant("Test") + } + + val body = enum.body + body.shouldNotBeNull() + body.variants.shouldHaveSize(1) + } + + @Test + fun `createSmlExpressionLambda should use a lambda parameter list for parameters`() { + val lambda = createSmlExpressionLambda(result = createSmlNull()) + lambda.parameterList.shouldBeInstanceOf() + } + + @Test + fun `createSmlExpressionLambda should create a serializable expression lambda`() { + val lambda = createSmlExpressionLambda(result = createSmlNull()) + + createSmlDummyResource(fileName = "test", SmlFileExtension.Test, packageName = "test") { + smlWorkflow(name = "test") { + smlExpressionStatement(lambda) + } + } + + lambda.serializeToFormattedString().shouldBeInstanceOf() + } + + @Test + fun `smlExpressionStatement should add the created expression statement to the receiving lambda`() { + val lambda = createSmlBlockLambda { + smlExpressionStatement(createSmlInt(1)) + } + + val body = lambda.body + body.shouldNotBeNull() + body.statements.shouldHaveSize(1) + } + + @Test + fun `smlExpressionStatement should add the created expression statement to the receiving workflow`() { + val workflow = createSmlWorkflow("Test") { + smlExpressionStatement(createSmlInt(1)) + } + + val body = workflow.body + body.shouldNotBeNull() + body.statements.shouldHaveSize(1) + } + + @Test + fun `smlExpressionStatement should add the created expression statement to the receiving step`() { + val step = createSmlStep("Test") { + smlExpressionStatement(createSmlInt(1)) + } + + val body = step.body + body.shouldNotBeNull() + body.statements.shouldHaveSize(1) + } + + @Test + fun `createSmlFloat should wrap negative numbers in a prefix operation (-)`() { + val float = createSmlFloat(-1.0) + + float.shouldBeInstanceOf() + float.operator shouldBe SmlPrefixOperationOperator.Minus.operator + + val operand = float.operand + operand.shouldBeInstanceOf() + operand.value shouldBeCloseTo 1.0 + } + + @Test + fun `createSmlFunction should store annotation uses in annotationCallList`() { + val function = createSmlFunction( + "test", + listOf(createSmlAnnotationCall("Test")) + ) + + function.annotationCalls.shouldHaveSize(0) + + val annotationCallList = function.annotationCallList + annotationCallList.shouldNotBeNull() + annotationCallList.annotationCalls.shouldHaveSize(1) + } + + @Test + fun `createSmlFunction should omit empty result list`() { + val function = createSmlFunction( + "test", + results = emptyList() + ) + function.resultList.shouldBeNull() + } + + @Test + fun `createSmlFunction should omit empty type parameter list`() { + val function = createSmlFunction( + "test", + typeParameters = emptyList() + ) + function.typeParameterList.shouldBeNull() + } + + @Test + fun `createSmlFunction should omit empty constraint list`() { + val function = createSmlFunction( + "test", + constraints = emptyList() + ) + function.constraintList.shouldBeNull() + } + + @Test + fun `smlFunction should add the created function to the receiving class`() { + val `class` = createSmlClass("Test") { + smlFunction("test") + } + + val body = `class`.body + body.shouldNotBeNull() + body.members.shouldHaveSize(1) + } + + @Test + fun `smlFunction should add the created function to the receiving compilation unit`() { + val compilationUnit = createSmlCompilationUnit(packageName = "test") { + smlFunction("test") + } + + compilationUnit.members.shouldHaveSize(1) + } + + @Test + fun `createSmlInt should wrap negative numbers in a prefix operation (-)`() { + val int = createSmlInt(-1) + + int.shouldBeInstanceOf() + int.operator shouldBe SmlPrefixOperationOperator.Minus.operator + + val operand = int.operand + operand.shouldBeInstanceOf() + operand.value shouldBe 1 + } + + @Test + fun `createSmlNamedType should omit empty type argument lists`() { + val namedType = createSmlNamedType( + createSmlClass("Int"), + typeArguments = emptyList() + ) + namedType.typeArgumentList.shouldBeNull() + } + + @Test + fun `createSmlParameter should store annotation uses in annotations`() { + val parameter = createSmlParameter( + "test", + listOf(createSmlAnnotationCall("Test")) + ) + + parameter.annotationCalls.shouldHaveSize(1) + parameter.annotationCallList.shouldBeNull() + } + + @Test + fun `createSmlProtocol should omit empty subterm list`() { + val protocol = createSmlProtocol(emptyList()) + protocol.body.shouldNotBeNull() + protocol.body.subtermList.shouldBeNull() + } + + @Test + fun `smlProtocol should add the created protocol to the receiving class`() { + val `class` = createSmlClass("Test") { + smlProtocol() + } + + `class`.body.shouldNotBeNull() + `class`.body.members.filterIsInstance().shouldHaveSize(1) + } + + @Test + fun `createSmlProtocolAlternative should throw if fewer than two terms are passed`() { + shouldThrow { + createSmlProtocolAlternative(listOf()) + } + + shouldThrow { + createSmlProtocolAlternative( + listOf( + createSmlProtocolTokenClass(SmlProtocolTokenClassValue.Anything) + ) + ) + } + + shouldNotThrow { + createSmlProtocolAlternative( + listOf( + createSmlProtocolTokenClass(SmlProtocolTokenClassValue.Anything), + createSmlProtocolTokenClass(SmlProtocolTokenClassValue.Anything), + ) + ) + } + } + + @Test + fun `createSmlProtocolComplement should omit empty reference list`() { + val complement = createSmlProtocolComplement() + complement.referenceList.shouldBeNull() + } + + @Test + fun `createSmlProtocolSequence should throw if fewer than two terms are passed`() { + shouldThrow { + createSmlProtocolSequence(listOf()) + } + + shouldThrow { + createSmlProtocolSequence( + listOf( + createSmlProtocolTokenClass(SmlProtocolTokenClassValue.Anything) + ) + ) + } + + shouldNotThrow { + createSmlProtocolSequence( + listOf( + createSmlProtocolTokenClass(SmlProtocolTokenClassValue.Anything), + createSmlProtocolTokenClass(SmlProtocolTokenClassValue.Anything), + ) + ) + } + } + + @Test + fun `smlProtocolSubterm should add the created subterm to the receiving protocol`() { + val protocol = createSmlProtocol { + smlProtocolSubterm("test", createSmlProtocolTokenClass(SmlProtocolTokenClassValue.Anything)) + } + + protocol.body.shouldNotBeNull() + protocol.body.subtermList.shouldNotBeNull() + protocol.body.subtermList.subterms.shouldHaveSize(1) + } + + @Test + fun `createSmlResult should store annotation uses in annotations`() { + val result = createSmlResult( + "Test", + listOf(createSmlAnnotationCall("Test")) + ) + + result.annotationCalls.shouldHaveSize(1) + result.annotationCallList.shouldBeNull() + } + + @Test + fun `createSmlTemplate should throw if there are fewer than 2 string parts`() { + shouldThrow { + createSmlTemplateString( + listOf("Test"), + listOf(createSmlInt(1)) + ) + } + } + + @Test + fun `createSmlTemplate should throw if there is no template expression`() { + shouldThrow { + createSmlTemplateString( + listOf("Test", "Test"), + listOf() + ) + } + } + + @Test + fun `createSmlTemplate should throw if numbers of string parts and template expressions don't match`() { + shouldThrow { + createSmlTemplateString( + listOf("Test", "Test", "Test"), + listOf(createSmlInt(1)) + ) + } + } + + @Test + fun `createSmlTemplate should interleave string parts and template expressions`() { + val templateString = createSmlTemplateString( + listOf("Start", "Inner", "Inner", "End"), + listOf(createSmlInt(1), createSmlInt(1), createSmlInt(1)) + ) + + templateString.expressions.asClue { + it.shouldHaveSize(7) + it[0].shouldBeInstanceOf() + it[1].shouldBeInstanceOf() + it[2].shouldBeInstanceOf() + it[3].shouldBeInstanceOf() + it[4].shouldBeInstanceOf() + it[5].shouldBeInstanceOf() + it[6].shouldBeInstanceOf() + } + } + + @Test + fun `createSmlTypeArgument should create an SmlTypeParameter when only a name is passed`() { + val typeArgument = createSmlTypeArgument( + createSmlStarProjection(), + "Test" + ) + val typeParameter = typeArgument.typeParameter + typeParameter.shouldNotBeNull() + typeParameter.name shouldBe "Test" + } + + @Test + fun `createSmlTypeParameter should store annotation uses in annotations`() { + val result = createSmlTypeParameter( + "Test", + listOf(createSmlAnnotationCall("Test")) + ) + + result.annotationCalls.shouldHaveSize(1) + result.annotationCallList.shouldBeNull() + } + + @Test + fun `createTypeParameterConstraint should create an SmlTypeParameter when only a name is passed`() { + val constraint = createSmlTypeParameterConstraint( + "Test", + SmlTypeParameterConstraintOperator.SubclassOf, + createSmlNamedType(createSmlClass("Test")) + ) + val leftOperand = constraint.leftOperand + leftOperand.shouldNotBeNull() + leftOperand.name shouldBe "Test" + } + + @Test + fun `createSmlUnionType should throw if no type arguments are passed`() { + shouldThrow { + createSmlUnionType(listOf()) + } + + shouldNotThrow { + createSmlUnionType( + listOf( + createSmlTypeArgument( + createSmlStarProjection() + ) + ) + ) + } + } + + @Test + fun `createSmlYield should create an SmlResult when only a name is passed`() { + val yield = createSmlYield("test") + val result = `yield`.result + result.shouldNotBeNull() + result.name shouldBe "test" + } + + @Test + fun `createSmlStep should store annotation uses in annotationCallList`() { + val step = createSmlStep( + "test", + listOf(createSmlAnnotationCall("Test")) + ) + + step.annotationCalls.shouldHaveSize(0) + + val annotationCallList = step.annotationCallList + annotationCallList.shouldNotBeNull() + annotationCallList.annotationCalls.shouldHaveSize(1) + } + + @Test + fun `createSmlStep should omit empty result list`() { + val function = createSmlStep( + "test", + results = emptyList() + ) + function.resultList.shouldBeNull() + } + + @Test + fun `smlStep should add the created step to the receiving compilation unit`() { + val compilationUnit = createSmlCompilationUnit(packageName = "test") { + smlStep("test") + } + + compilationUnit.members.shouldHaveSize(1) + } + + @Test + fun `createSmlWorkflow should store annotation uses in annotationCallList`() { + val workflow = createSmlWorkflow( + "test", + listOf(createSmlAnnotationCall("Test")) + ) + + workflow.annotationCalls.shouldHaveSize(0) + + val annotationCallList = workflow.annotationCallList + annotationCallList.shouldNotBeNull() + annotationCallList.annotationCalls.shouldHaveSize(1) + } + + @Test + fun `smlWorkflow should add the created workflow to the receiving compilation unit`() { + val compilationUnit = createSmlCompilationUnit(packageName = "test") { + smlWorkflow("test") + } + + compilationUnit.members.shouldHaveSize(1) + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/formatting2/FormatterTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/formatting2/FormatterTest.kt new file mode 100644 index 000000000..b0508b2ed --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/formatting2/FormatterTest.kt @@ -0,0 +1,78 @@ +package de.unibonn.simpleml.formatting2 + +import com.google.inject.Inject +import de.unibonn.simpleml.testing.CategorizedTest +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.createDynamicTestsFromResourceFolder +import de.unibonn.simpleml.testing.getResourcePath +import de.unibonn.simpleml.testing.testDisplayName +import de.unibonn.simpleml.testing.withSystemLineBreaks +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.eclipse.xtext.testing.formatter.FormatterTestHelper +import org.junit.jupiter.api.DynamicNode +import org.junit.jupiter.api.DynamicTest +import org.junit.jupiter.api.TestFactory +import org.junit.jupiter.api.extension.ExtendWith +import java.nio.file.Path +import java.util.stream.Stream + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class FormatterTest { + + @Inject + lateinit var formatter: FormatterTestHelper + + private val separator = Regex("// -*") + + @TestFactory + fun `should be formatted properly`(): Stream { + return javaClass.classLoader + .getResourcePath("formatting") + ?.createDynamicTestsFromResourceFolder(::validateTestFile, ::createTest) + ?: Stream.empty() + } + + /** + * Checks if the given program is a valid test. If there are issues a description of the issue is returned, otherwise + * this returns null. + */ + @Suppress("UNUSED_PARAMETER") + private fun validateTestFile(resourcePath: Path, filePath: Path, program: String): String? { + if (separator !in program) { + return "Did not find a separator between the original and the formatted code." + } + + return null + } + + private fun createTest(resourcePath: Path, filePath: Path, program: String) = sequence { + yield( + CategorizedTest( + "formatter tests", + DynamicTest.dynamicTest(testDisplayName(resourcePath, filePath), filePath.toUri()) { + assertFormatted(toBeFormatted(program), expectedResult(program)) + } + ) + ) + } + + private fun toBeFormatted(program: String): String { + return program.split(separator)[0].trim() + } + + private fun expectedResult(program: String): String { + return program + .split(separator)[1] + .trim() + .withSystemLineBreaks() + } + + private fun assertFormatted(toBeFormatted: String, expectedResult: String) { + formatter.assertFormatted { + it.toBeFormatted = toBeFormatted + it.expectation = expectedResult + System.lineSeparator() + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/generator/GeneratorUtilsTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/generator/GeneratorUtilsTest.kt new file mode 100644 index 000000000..59513bc0f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/generator/GeneratorUtilsTest.kt @@ -0,0 +1,183 @@ +@file:Suppress("ClassName") + +package de.unibonn.simpleml.generator + +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.emf.createSmlCompilationUnit +import de.unibonn.simpleml.emf.createSmlDummyResource +import de.unibonn.simpleml.simpleML.SimpleMLFactory +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldStartWith +import org.eclipse.emf.common.util.URI +import org.eclipse.xtext.resource.XtextResource +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class GeneratorUtilsTest { + + @Nested + inner class baseFileName { + + @Test + fun `should keep only last segment`() { + val resource = createSmlDummyResource( + "dir/file", + SmlFileExtension.Flow, + createSmlCompilationUnit(packageName = "test") + ) + + resource.baseFileNameOrNull().shouldStartWith("file") + } + + @Test + fun `should remove all characters that are not legal in Simple-ML identifiers except spaces`() { + val resource = createSmlDummyResource( + "MyöáúName1", + SmlFileExtension.Flow, + createSmlCompilationUnit(packageName = "MyName1") + ) + + resource.baseFileNameOrNull() shouldBe "MyName1" + } + + @Test + fun `should replace spaces with underscores`() { + val resource = createSmlDummyResource( + "file with spaces", + SmlFileExtension.Flow, + createSmlCompilationUnit(packageName = "test") + ) + + resource.baseFileNameOrNull() shouldBe "file_with_spaces" + } + + @Test + fun `should replace twice URL encoded spaces with underscores`() { + val resource = createSmlDummyResource( + "_skip_%2520context%2520same%2520package", + SmlFileExtension.Flow, + createSmlCompilationUnit(packageName = "test") + ) + + resource.baseFileNameOrNull() shouldBe "_skip__context_same_package" + } + + @Test + fun `should replace dots with underscores`() { + val resource = createSmlDummyResource( + "file.with.dots", + SmlFileExtension.Flow, + createSmlCompilationUnit(packageName = "test") + ) + + resource.baseFileNameOrNull() shouldBe "file_with_dots" + } + + @Test + fun `should replace dashes with underscores`() { + val resource = createSmlDummyResource( + "file-with-dashes", + SmlFileExtension.Flow, + createSmlCompilationUnit(packageName = "test") + ) + + resource.baseFileNameOrNull() shouldBe "file_with_dashes" + } + + @Test + fun `should remove 'smlflow' extension`() { + val resource = createSmlDummyResource( + "file", + SmlFileExtension.Flow, + createSmlCompilationUnit(packageName = "test") + ) + + resource.baseFileNameOrNull() shouldBe "file" + } + + @Test + fun `should remove 'smlstub' extension`() { + val resource = createSmlDummyResource( + "file", + SmlFileExtension.Stub, + createSmlCompilationUnit(packageName = "test") + ) + + resource.baseFileNameOrNull() shouldBe "file" + } + + @Test + fun `should remove 'smltest' extension`() { + val resource = createSmlDummyResource( + "file", + SmlFileExtension.Test, + createSmlCompilationUnit(packageName = "test") + ) + + resource.baseFileNameOrNull() shouldBe "file" + } + + @Test + fun `should not remove other extension`() { + val uri = URI.createURI("dummy:/test.other") + val resource = XtextResource(uri) + + resource.baseFileNameOrNull() shouldBe "test_other" + } + + @Test + fun `should return null if the resource has no URI`() { + val resource = XtextResource(null) + + resource.baseFileNameOrNull().shouldBeNull() + } + } + + @Nested + inner class baseGeneratedFilePath { + + @Test + fun `should return the base path for generated files if possible`() { + val resource = createSmlDummyResource( + "file", + SmlFileExtension.Test, + createSmlCompilationUnit(packageName = "test") + ) + + resource.baseGeneratedFilePathOrNull() shouldBe "test/gen_file" + } + + @Test + fun `should return null if the resource has no compilation unit`() { + val uri = URI.createURI("dummy:/test.other") + val resource = XtextResource(uri) + + resource.baseGeneratedFilePathOrNull().shouldBeNull() + } + + @Test + fun `should return null if the compilation unit has no package`() { + val resource = createSmlDummyResource( + "file", + SmlFileExtension.Test, + SimpleMLFactory.eINSTANCE.createSmlCompilationUnit() + ) + + resource.baseGeneratedFilePathOrNull().shouldBeNull() + } + + @Test + fun `should return null if the resource has no URI`() { + val resource = XtextResource(null) + + resource.baseGeneratedFilePathOrNull() + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/generator/SimpleMLGeneratorTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/generator/SimpleMLGeneratorTest.kt new file mode 100644 index 000000000..dd2ef7198 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/generator/SimpleMLGeneratorTest.kt @@ -0,0 +1,180 @@ +package de.unibonn.simpleml.generator + +import com.google.inject.Inject +import de.unibonn.simpleml.constant.SmlFileExtension.Flow +import de.unibonn.simpleml.constant.SmlFileExtension.Stub +import de.unibonn.simpleml.constant.SmlFileExtension.Test +import de.unibonn.simpleml.emf.OriginalFilePath +import de.unibonn.simpleml.emf.resourceSetOrNull +import de.unibonn.simpleml.testing.CategorizedTest +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.ResourceName +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.assertions.stringify +import de.unibonn.simpleml.testing.createDynamicTestsFromResourceFolder +import de.unibonn.simpleml.testing.getResourcePath +import de.unibonn.simpleml.testing.testDisplayName +import de.unibonn.simpleml.testing.withSystemLineBreaks +import io.kotest.assertions.forEachAsClue +import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import org.eclipse.emf.ecore.resource.ResourceSet +import org.eclipse.xtext.diagnostics.Severity +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.eclipse.xtext.testing.validation.ValidationTestHelper +import org.eclipse.xtext.validation.Issue +import org.eclipse.xtext.xbase.testing.CompilationTestHelper +import org.junit.jupiter.api.DynamicNode +import org.junit.jupiter.api.DynamicTest +import org.junit.jupiter.api.TestFactory +import org.junit.jupiter.api.extension.ExtendWith +import java.nio.file.Files +import java.nio.file.Path +import java.util.stream.Stream +import kotlin.io.path.extension +import kotlin.io.path.name +import kotlin.io.path.readText +import kotlin.streams.asSequence + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class SimpleMLGeneratorTest { + + @Inject + private lateinit var compilationTestHelper: CompilationTestHelper + + @Inject + private lateinit var parseHelper: ParseHelper + + @Inject + private lateinit var validationHelper: ValidationTestHelper + + @TestFactory + fun `should compile test files correctly`(): Stream { + return javaClass.classLoader + .getResourcePath("generator") + ?.createDynamicTestsFromResourceFolder(::validateTestFile, ::createTest) + ?: Stream.empty() + } + + /** + * Checks if the given program is a valid test. If there are issues a description of the issue is returned, + * otherwise this returns `null`. + */ + @Suppress("UNUSED_PARAMETER") + private fun validateTestFile(resourcePath: Path, filePath: Path, program: String): String? { + + // Must be able to parse the test file + if (parseHelper.parseProgramText(program) == null) { + return "Could not parse test file." + } + + // Must not have errors + val errors = actualIssues(resourcePath, filePath, program).filter { it.severity == Severity.ERROR } + if (errors.isNotEmpty()) { + return "Program has errors:${errors.stringify()}" + } + + return null + } + + private fun actualIssues(resourcePath: Path, filePath: Path, program: String): List { + val context = context(resourcePath, filePath) + val parsingResult = parseHelper.parseProgramText(program, context) ?: return emptyList() + parsingResult.eResource().eAdapters().add(OriginalFilePath(filePath.toString())) + return validationHelper.validate(parsingResult) + } + + @Suppress("UNUSED_PARAMETER") + private fun createTest(resourcePath: Path, filePath: Path, program: String) = sequence { + yield( + CategorizedTest( + "valid test file", + DynamicTest.dynamicTest(testDisplayName(resourcePath, filePath), filePath.toUri()) { + generatorTest(resourcePath, filePath) + } + ) + ) + } + + private fun generatorTest(resourcePath: Path, filePath: Path) { + val expectedOutputs = expectedOutputs(filePath) + val actualOutputs = actualOutputs(resourcePath, filePath) + + // File paths should match exactly + actualOutputs.map { it.filePath }.shouldContainExactlyInAnyOrder(expectedOutputs.map { it.filePath }) + + // Contents should match + actualOutputs.forEachAsClue { actualOutput -> + val expectedOutput = expectedOutputs.first { it.filePath == actualOutput.filePath } + actualOutput.content.withSystemLineBreaks() shouldBe expectedOutput.content.withSystemLineBreaks() + } + } + + private data class OutputFile(val filePath: String, val content: String) + + private fun context(resourcePath: Path, filePath: Path): List { + val root = filePath.parent + + return Files.walk(root) + .asSequence() + .filter { it.extension in setOf(Flow.extension, Stub.extension, Test.extension) } + .filter { it.name.startsWith("_skip_") } + .map { resourceName(resourcePath, it) } + .toList() + } + + private fun expectedOutputs(filePath: Path): List { + val root = filePath.parent + + return Files.walk(root) + .asSequence() + .filter { it.extension == "py" } + .map { + OutputFile( + root.relativize(it).toUnixString(), + it.readText() + ) + } + .toList() + } + + private fun actualOutputs(resourcePath: Path, filePath: Path): List { + var actualOutput: List = emptyList() + + compilationTestHelper.compile(resourceSet(resourcePath, filePath)) { result -> + actualOutput = result.allGeneratedResources.map { + OutputFile(it.key.normalizePathPrefix(), it.value.toString()) + } + } + + return actualOutput + } + + private fun resourceSet(resourcePath: Path, filePath: Path): ResourceSet { + val context = context(resourcePath, filePath) + return parseHelper + .parseResource(resourceName(resourcePath, filePath), context) + ?.resourceSetOrNull() + .shouldNotBeNull() + } + + private fun resourceName(resourcePath: Path, filePath: Path): ResourceName { + return resourcePath + .parent + .relativize(filePath) + .toUnixString() + } + + private fun Path.toUnixString(): String { + return this.toString().replace("\\", "/") + } + + private fun String.normalizePathPrefix(): String { + return this + .removePrefix("/myProject/./") + .replace("src-gen", "output") + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/naming/QualifiedNameProviderTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/naming/QualifiedNameProviderTest.kt new file mode 100644 index 000000000..8d9e8d0fa --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/naming/QualifiedNameProviderTest.kt @@ -0,0 +1,51 @@ +@file:Suppress("ClassName") + +package de.unibonn.simpleml.naming + +import de.unibonn.simpleml.emf.createSmlClass +import de.unibonn.simpleml.emf.createSmlCompilationUnit +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import io.kotest.matchers.shouldBe +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class QualifiedNameProviderTest { + + @Nested + inner class QualifiedNameOrNull { + + @Test + fun `should handle declarations with simple names`() { + val myClass = createSmlClass(name = "MyClass") + createSmlCompilationUnit(packageName = "tests", members = listOf(myClass)) + + myClass.qualifiedNameOrNull() shouldBe "tests.MyClass".toQualifiedName() + } + + @Test + fun `should handle declarations with escaped names`() { + val myClass = createSmlClass(name = "`MyClass`") + createSmlCompilationUnit(packageName = "`tests`", members = listOf(myClass)) + + myClass.qualifiedNameOrNull() shouldBe "`tests`.`MyClass`".toQualifiedName() + } + } + + @Nested + inner class toQualifiedName { + + @Test + fun `should convert string to qualified name`() { + val qualifiedName = "tests.MyClass".toQualifiedName() + + qualifiedName.segmentCount shouldBe 2 + qualifiedName.getSegment(0) shouldBe "tests" + qualifiedName.getSegment(1) shouldBe "MyClass" + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/prologBridge/AstToPrologFactbaseTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/prologBridge/AstToPrologFactbaseTest.kt new file mode 100644 index 000000000..24c618613 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/prologBridge/AstToPrologFactbaseTest.kt @@ -0,0 +1,1943 @@ +package de.unibonn.simpleml.prologBridge + +import de.unibonn.simpleml.SimpleMLStandaloneSetup +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.prologBridge.model.facts.AnnotationCallT +import de.unibonn.simpleml.prologBridge.model.facts.AnnotationT +import de.unibonn.simpleml.prologBridge.model.facts.ArgumentT +import de.unibonn.simpleml.prologBridge.model.facts.AssignmentT +import de.unibonn.simpleml.prologBridge.model.facts.AttributeT +import de.unibonn.simpleml.prologBridge.model.facts.BlockLambdaResultT +import de.unibonn.simpleml.prologBridge.model.facts.BlockLambdaT +import de.unibonn.simpleml.prologBridge.model.facts.BooleanT +import de.unibonn.simpleml.prologBridge.model.facts.CallT +import de.unibonn.simpleml.prologBridge.model.facts.CallableTypeT +import de.unibonn.simpleml.prologBridge.model.facts.ClassT +import de.unibonn.simpleml.prologBridge.model.facts.CompilationUnitT +import de.unibonn.simpleml.prologBridge.model.facts.DeclarationT +import de.unibonn.simpleml.prologBridge.model.facts.EnumT +import de.unibonn.simpleml.prologBridge.model.facts.EnumVariantT +import de.unibonn.simpleml.prologBridge.model.facts.ExpressionLambdaT +import de.unibonn.simpleml.prologBridge.model.facts.ExpressionStatementT +import de.unibonn.simpleml.prologBridge.model.facts.ExpressionT +import de.unibonn.simpleml.prologBridge.model.facts.FloatT +import de.unibonn.simpleml.prologBridge.model.facts.FunctionT +import de.unibonn.simpleml.prologBridge.model.facts.ImportT +import de.unibonn.simpleml.prologBridge.model.facts.IndexedAccessT +import de.unibonn.simpleml.prologBridge.model.facts.InfixOperationT +import de.unibonn.simpleml.prologBridge.model.facts.IntT +import de.unibonn.simpleml.prologBridge.model.facts.MemberAccessT +import de.unibonn.simpleml.prologBridge.model.facts.MemberTypeT +import de.unibonn.simpleml.prologBridge.model.facts.NamedTypeT +import de.unibonn.simpleml.prologBridge.model.facts.NodeWithParent +import de.unibonn.simpleml.prologBridge.model.facts.NullT +import de.unibonn.simpleml.prologBridge.model.facts.ParameterT +import de.unibonn.simpleml.prologBridge.model.facts.ParenthesizedExpressionT +import de.unibonn.simpleml.prologBridge.model.facts.ParenthesizedTypeT +import de.unibonn.simpleml.prologBridge.model.facts.PlFactbase +import de.unibonn.simpleml.prologBridge.model.facts.PlaceholderT +import de.unibonn.simpleml.prologBridge.model.facts.PrefixOperationT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolAlternativeT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolComplementT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolParenthesizedTermT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolQuantifiedTermT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolReferenceT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolSequenceT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolSubtermT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolTermT +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolTokenClassT +import de.unibonn.simpleml.prologBridge.model.facts.ReferenceT +import de.unibonn.simpleml.prologBridge.model.facts.ResourceS +import de.unibonn.simpleml.prologBridge.model.facts.ResultT +import de.unibonn.simpleml.prologBridge.model.facts.SourceLocationS +import de.unibonn.simpleml.prologBridge.model.facts.StarProjectionT +import de.unibonn.simpleml.prologBridge.model.facts.StatementT +import de.unibonn.simpleml.prologBridge.model.facts.StepT +import de.unibonn.simpleml.prologBridge.model.facts.StringT +import de.unibonn.simpleml.prologBridge.model.facts.TemplateStringEndT +import de.unibonn.simpleml.prologBridge.model.facts.TemplateStringInnerT +import de.unibonn.simpleml.prologBridge.model.facts.TemplateStringStartT +import de.unibonn.simpleml.prologBridge.model.facts.TemplateStringT +import de.unibonn.simpleml.prologBridge.model.facts.TypeArgumentT +import de.unibonn.simpleml.prologBridge.model.facts.TypeParameterConstraintT +import de.unibonn.simpleml.prologBridge.model.facts.TypeParameterT +import de.unibonn.simpleml.prologBridge.model.facts.TypeProjectionT +import de.unibonn.simpleml.prologBridge.model.facts.TypeT +import de.unibonn.simpleml.prologBridge.model.facts.UnionTypeT +import de.unibonn.simpleml.prologBridge.model.facts.UnresolvedT +import de.unibonn.simpleml.prologBridge.model.facts.WildcardT +import de.unibonn.simpleml.prologBridge.model.facts.WorkflowT +import de.unibonn.simpleml.prologBridge.model.facts.YieldT +import de.unibonn.simpleml.testing.assertions.findUniqueFactOrFail +import de.unibonn.simpleml.testing.assertions.shouldBeChildExpressionOf +import de.unibonn.simpleml.testing.assertions.shouldBeChildOf +import de.unibonn.simpleml.testing.assertions.shouldBeChildProtocolTermOf +import de.unibonn.simpleml.testing.assertions.shouldBeCloseTo +import de.unibonn.simpleml.testing.assertions.shouldBeNChildExpressionsOf +import de.unibonn.simpleml.testing.assertions.shouldBeNChildProtocolTermsOf +import de.unibonn.simpleml.testing.assertions.shouldBeNChildrenOf +import de.unibonn.simpleml.testing.assertions.shouldHaveNAnnotationCalls +import de.unibonn.simpleml.testing.getResourcePath +import io.kotest.assertions.asClue +import io.kotest.matchers.collections.shouldBeEmpty +import io.kotest.matchers.collections.shouldBeOneOf +import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldEndWith +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +/** + * ## Testing procedure for a fact: + * - Check its own primitive arguments + * - Check size of lists of children + * - Ensure IDs of children are correct + */ +class AstToPrologFactbaseTest { + + private val main = SimpleMLStandaloneSetup() + .createInjectorAndDoEMFRegistration() + .getInstance(Main::class.java) + + private val testRoot = javaClass.classLoader.getResourcePath("astToPrologFactbase").toString() + + // ***************************************************************************************************************** + // Declarations + // ****************************************************************************************************************/ + + @Nested + inner class Declarations { + + @Nested + inner class CompilationUnit { + @Test + fun `should handle empty compilation units`() = withFactbaseFromFile("empty") { + val compilationUnitT = findUniqueFactOrFail() + compilationUnitT.asClue { + it.members.shouldBeEmpty() + } + } + + @Test + fun `should store package name`() = withFactbaseFromFile("declarations") { + val compilationUnitT = findUniqueFactOrFail() + compilationUnitT.asClue { + compilationUnitT.packageName shouldBe "tests.astToPrologFactbase.declarations" + } + } + + @Test + fun `should reference imports`() = withFactbaseFromFile("declarations") { + val compilationUnitT = findUniqueFactOrFail() + shouldBeNChildrenOf(compilationUnitT.imports, compilationUnitT, 3) + } + + @Test + fun `should reference members`() = withFactbaseFromFile("declarations") { + val compilationUnitT = findUniqueFactOrFail() + shouldBeNChildrenOf(compilationUnitT.members, compilationUnitT, 12) + } + + @Test + fun `should store resource URI in separate relation`() = withFactbaseFromFile("empty") { + val compilationUnitT = findUniqueFactOrFail() + val resourceS = findUniqueFactOrFail() + resourceS.asClue { + resourceS.target shouldBe compilationUnitT.id + resourceS.uri shouldEndWith "astToPrologFactbase/empty.${SmlFileExtension.Test}" + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("empty") { + val compilationUnitT = findUniqueFactOrFail() + findUniqueFactOrFail { it.target == compilationUnitT.id } + } + } + + @Nested + inner class Annotation { + @Test + fun `should handle simple annotations`() = withFactbaseFromFile("declarations") { + val annotationT = findUniqueFactOrFail { it.name == "MySimpleAnnotation" } + annotationT.asClue { + annotationT.parameters.shouldBeNull() + annotationT.constraints.shouldBeNull() + } + + shouldHaveNAnnotationCalls(annotationT, 0) + } + + @Test + fun `should reference parameters`() = withFactbaseFromFile("declarations") { + val annotationT = findUniqueFactOrFail { it.name == "MyComplexAnnotation" } + shouldBeNChildrenOf(annotationT.parameters, annotationT, 2) + } + + @Test + fun `should reference constraints`() = withFactbaseFromFile("declarations") { + val annotationT = findUniqueFactOrFail { it.name == "MyComplexAnnotation" } + shouldBeNChildrenOf(annotationT.constraints, annotationT, 2) + } + + @Test + fun `should store annotation uses`() = withFactbaseFromFile("declarations") { + val annotationT = findUniqueFactOrFail { it.name == "MyComplexAnnotation" } + shouldHaveNAnnotationCalls(annotationT, 1) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("declarations") { + val annotationT = findUniqueFactOrFail { it.name == "MySimpleAnnotation" } + findUniqueFactOrFail { it.target == annotationT.id } + } + } + + @Nested + inner class Attribute { + @Test + fun `should handle simple attributes`() = withFactbaseFromFile("declarations") { + val attributeT = findUniqueFactOrFail { it.name == "mySimpleAttribute" } + attributeT.asClue { + attributeT.isStatic shouldBe false + attributeT.type.shouldBeNull() + } + + shouldHaveNAnnotationCalls(attributeT, 0) + } + + @Test + fun `should store isStatic`() = withFactbaseFromFile("declarations") { + val attributeT = findUniqueFactOrFail { it.name == "myComplexAttribute" } + attributeT.asClue { + attributeT.isStatic shouldBe true + } + } + + @Test + fun `should reference type`() = withFactbaseFromFile("declarations") { + val attributeT = findUniqueFactOrFail { it.name == "myComplexAttribute" } + shouldBeChildOf(attributeT.type, attributeT) + } + + @Test + fun `should store annotation uses`() = withFactbaseFromFile("declarations") { + val attributeT = findUniqueFactOrFail { it.name == "myComplexAttribute" } + shouldHaveNAnnotationCalls(attributeT, 1) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("declarations") { + val attributeT = findUniqueFactOrFail { it.name == "mySimpleAttribute" } + findUniqueFactOrFail { it.target == attributeT.id } + } + } + + @Nested + inner class Class { + @Test + fun `should handle simple classes`() = withFactbaseFromFile("declarations") { + val classT = findUniqueFactOrFail { it.name == "MySimpleClass" } + classT.asClue { + classT.typeParameters.shouldBeNull() + classT.parameters.shouldBeNull() + classT.parentTypes.shouldBeNull() + classT.constraints.shouldBeNull() + classT.members.shouldBeNull() + } + + shouldHaveNAnnotationCalls(classT, 0) + } + + @Test + fun `should reference type parameters`() = withFactbaseFromFile("declarations") { + val classT = findUniqueFactOrFail { it.name == "MyComplexClass" } + shouldBeNChildrenOf(classT.typeParameters, classT, 2) + } + + @Test + fun `should reference parameters`() = withFactbaseFromFile("declarations") { + val classT = findUniqueFactOrFail { it.name == "MyComplexClass" } + shouldBeNChildrenOf(classT.parameters, classT, 2) + } + + @Test + fun `should reference parent types`() = withFactbaseFromFile("declarations") { + val classT = findUniqueFactOrFail { it.name == "MyComplexClass" } + shouldBeNChildrenOf(classT.parentTypes, classT, 2) + } + + @Test + fun `should reference constraints`() = withFactbaseFromFile("declarations") { + val classT = findUniqueFactOrFail { it.name == "MyComplexClass" } + shouldBeNChildrenOf(classT.constraints, classT, 2) + } + + @Test + fun `should reference members`() = withFactbaseFromFile("declarations") { + val classT = findUniqueFactOrFail { it.name == "MyComplexClass" } + shouldBeNChildrenOf(classT.members, classT, 5) + } + + @Test + fun `should store annotation uses`() = withFactbaseFromFile("declarations") { + val classT = findUniqueFactOrFail { it.name == "MyComplexClass" } + shouldHaveNAnnotationCalls(classT, 1) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("declarations") { + val classT = findUniqueFactOrFail { it.name == "MySimpleClass" } + findUniqueFactOrFail { it.target == classT.id } + } + } + + @Nested + inner class Enum { + @Test + fun `should handle simple enums`() = withFactbaseFromFile("declarations") { + val enumT = findUniqueFactOrFail { it.name == "MySimpleEnum" } + enumT.asClue { + enumT.variants.shouldBeNull() + } + + shouldHaveNAnnotationCalls(enumT, 0) + } + + @Test + fun `should reference instances`() = withFactbaseFromFile("declarations") { + val enumT = findUniqueFactOrFail { it.name == "MyComplexEnum" } + shouldBeNChildrenOf(enumT.variants, enumT, 2) + } + + @Test + fun `should store annotation uses`() = withFactbaseFromFile("declarations") { + val enumT = findUniqueFactOrFail { it.name == "MyComplexEnum" } + shouldHaveNAnnotationCalls(enumT, 1) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("declarations") { + val enumT = findUniqueFactOrFail { it.name == "MySimpleEnum" } + findUniqueFactOrFail { it.target == enumT.id } + } + } + + @Nested + inner class EnumInstance { + @Test + fun `should handle simple enum instances`() = withFactbaseFromFile("declarations") { + val enumVariantT = findUniqueFactOrFail { it.name == "MySimpleVariant" } + shouldHaveNAnnotationCalls(enumVariantT, 0) + } + + @Test + fun `should store annotation uses`() = withFactbaseFromFile("declarations") { + val enumVariantT = findUniqueFactOrFail { it.name == "MyComplexVariant" } + shouldHaveNAnnotationCalls(enumVariantT, 1) + } + + @Test + fun `should reference type parameters`() = withFactbaseFromFile("declarations") { + val enumVariantT = findUniqueFactOrFail { it.name == "MyComplexVariant" } + shouldBeNChildrenOf(enumVariantT.typeParameters, enumVariantT, 2) + } + + @Test + fun `should reference parameters`() = withFactbaseFromFile("declarations") { + val enumVariantT = findUniqueFactOrFail { it.name == "MyComplexVariant" } + shouldBeNChildrenOf(enumVariantT.parameters, enumVariantT, 2) + } + + @Test + fun `should reference constraints`() = withFactbaseFromFile("declarations") { + val enumVariantT = findUniqueFactOrFail { it.name == "MyComplexVariant" } + shouldBeNChildrenOf(enumVariantT.constraints, enumVariantT, 2) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("declarations") { + val enumVariantT = findUniqueFactOrFail { it.name == "MySimpleVariant" } + findUniqueFactOrFail { it.target == enumVariantT.id } + } + } + + @Nested + inner class Function { + @Test + fun `should handle simple functions`() = withFactbaseFromFile("declarations") { + val functionT = findUniqueFactOrFail { it.name == "mySimpleFunction" } + functionT.asClue { + functionT.isStatic shouldBe false + functionT.typeParameters.shouldBeNull() + functionT.parameters.shouldBeEmpty() + functionT.results.shouldBeNull() + functionT.constraints.shouldBeNull() + } + + shouldHaveNAnnotationCalls(functionT, 0) + } + + @Test + fun `should store isStatic`() = withFactbaseFromFile("declarations") { + val functionT = findUniqueFactOrFail { it.name == "myStaticMethod" } + functionT.asClue { + functionT.isStatic shouldBe true + } + } + + @Test + fun `should reference type parameters`() = withFactbaseFromFile("declarations") { + val functionT = findUniqueFactOrFail { it.name == "myComplexFunction" } + shouldBeNChildrenOf(functionT.typeParameters, functionT, 2) + } + + @Test + fun `should reference parameters`() = withFactbaseFromFile("declarations") { + val functionT = findUniqueFactOrFail { it.name == "myComplexFunction" } + shouldBeNChildrenOf(functionT.parameters, functionT, 2) + } + + @Test + fun `should reference results`() = withFactbaseFromFile("declarations") { + val functionT = findUniqueFactOrFail { it.name == "myComplexFunction" } + shouldBeNChildrenOf(functionT.results, functionT, 2) + } + + @Test + fun `should reference constraints`() = withFactbaseFromFile("declarations") { + val functionT = findUniqueFactOrFail { it.name == "myComplexFunction" } + shouldBeNChildrenOf(functionT.constraints, functionT, 2) + } + + @Test + fun `should store annotation uses`() = withFactbaseFromFile("declarations") { + val functionT = findUniqueFactOrFail { it.name == "myComplexFunction" } + shouldHaveNAnnotationCalls(functionT, 1) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("declarations") { + val functionT = findUniqueFactOrFail { it.name == "mySimpleFunction" } + findUniqueFactOrFail { it.target == functionT.id } + } + } + + @Nested + inner class Import { + @Test + fun `should handle normal imports`() = withFactbaseFromFile("declarations") { + val importT = findUniqueFactOrFail { it.importedNamespace == "myPackage.MyClass" } + importT.asClue { + importT.alias.shouldBeNull() + } + } + + @Test + fun `should handle imports with alias`() = withFactbaseFromFile("declarations") { + val importT = findUniqueFactOrFail { it.importedNamespace == "myPackage.MyOtherClass" } + importT.asClue { + importT.alias shouldBe "Class" + } + } + + @Test + fun `should handle imports with wildcard`() = withFactbaseFromFile("declarations") { + val importT = findUniqueFactOrFail { it.importedNamespace == "myPackage.*" } + importT.asClue { + importT.alias.shouldBeNull() + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("declarations") { + val importT = findUniqueFactOrFail { it.importedNamespace == "myPackage.MyClass" } + findUniqueFactOrFail { it.target == importT.id } + } + } + + @Nested + inner class Parameter { + @Test + fun `should handle simple parameters`() = withFactbaseFromFile("declarations") { + val parameterT = findUniqueFactOrFail { it.name == "mySimpleParameter" } + parameterT.asClue { + parameterT.isVariadic shouldBe false + parameterT.type.shouldBeNull() + parameterT.defaultValue.shouldBeNull() + } + + shouldHaveNAnnotationCalls(parameterT, 0) + } + + @Test + fun `should store isVariadic`() = withFactbaseFromFile("declarations") { + val parameterT = findUniqueFactOrFail { it.name == "myComplexParameter" } + parameterT.asClue { + parameterT.isVariadic shouldBe true + } + } + + @Test + fun `should reference type`() = withFactbaseFromFile("declarations") { + val parameterT = findUniqueFactOrFail { it.name == "myComplexParameter" } + shouldBeChildOf(parameterT.type, parameterT) + } + + @Test + fun `should reference default value`() = withFactbaseFromFile("declarations") { + val parameterT = findUniqueFactOrFail { it.name == "myComplexParameter" } + shouldBeChildExpressionOf(parameterT.defaultValue, parameterT) + } + + @Test + fun `should store annotation uses`() = withFactbaseFromFile("declarations") { + val parameterT = findUniqueFactOrFail { it.name == "myComplexParameter" } + shouldHaveNAnnotationCalls(parameterT, 1) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("declarations") { + val parameterT = findUniqueFactOrFail { it.name == "mySimpleParameter" } + findUniqueFactOrFail { it.target == parameterT.id } + } + } + + @Nested + inner class Result { + @Test + fun `should handle simple parameters`() = withFactbaseFromFile("declarations") { + val resultT = findUniqueFactOrFail { it.name == "mySimpleResult" } + resultT.asClue { + resultT.type.shouldBeNull() + } + + shouldHaveNAnnotationCalls(resultT, 0) + } + + @Test + fun `should reference type`() = withFactbaseFromFile("declarations") { + val resultT = findUniqueFactOrFail { it.name == "myComplexResult" } + shouldBeChildOf(resultT.type, resultT) + } + + @Test + fun `should store annotation uses`() = withFactbaseFromFile("declarations") { + val resultT = findUniqueFactOrFail { it.name == "myComplexResult" } + shouldHaveNAnnotationCalls(resultT, 1) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("declarations") { + val resultT = findUniqueFactOrFail { it.name == "mySimpleResult" } + findUniqueFactOrFail { it.target == resultT.id } + } + } + + @Nested + inner class TypeParameter { + @Test + fun `should handle simple parameters`() = withFactbaseFromFile("declarations") { + val typeParameterT = findUniqueFactOrFail { it.name == "MY_SIMPLE_TYPE_PARAMETER" } + typeParameterT.asClue { + typeParameterT.variance.shouldBeNull() + } + + shouldHaveNAnnotationCalls(typeParameterT, 0) + } + + @Test + fun `should store variance`() = withFactbaseFromFile("declarations") { + val typeParameterT = findUniqueFactOrFail { it.name == "MY_COMPLEX_TYPE_PARAMETER" } + typeParameterT.asClue { + typeParameterT.variance shouldBe "out" + } + } + + @Test + fun `should store annotation uses`() = withFactbaseFromFile("declarations") { + val typeParameterT = findUniqueFactOrFail { it.name == "MY_COMPLEX_TYPE_PARAMETER" } + shouldHaveNAnnotationCalls(typeParameterT, 1) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("declarations") { + val typeParameterT = findUniqueFactOrFail { it.name == "MY_SIMPLE_TYPE_PARAMETER" } + findUniqueFactOrFail { it.target == typeParameterT.id } + } + } + + @Nested + inner class Steps { + @Test + fun `should handle simple steps`() = withFactbaseFromFile("declarations") { + val stepT = findUniqueFactOrFail { it.name == "mySimpleStep" } + stepT.asClue { + stepT.visibility.shouldBeNull() + stepT.parameters.shouldBeEmpty() + stepT.results.shouldBeNull() + stepT.statements.shouldBeEmpty() + } + + shouldHaveNAnnotationCalls(stepT, 0) + } + + @Test + fun `should store visibility`() = withFactbaseFromFile("declarations") { + val stepT = findUniqueFactOrFail { it.name == "myComplexStep" } + stepT.visibility shouldBe "private" + } + + @Test + fun `should reference parameters`() = withFactbaseFromFile("declarations") { + val stepT = findUniqueFactOrFail { it.name == "myComplexStep" } + shouldBeNChildrenOf(stepT.parameters, stepT, 2) + } + + @Test + fun `should reference results`() = withFactbaseFromFile("declarations") { + val stepT = findUniqueFactOrFail { it.name == "myComplexStep" } + shouldBeNChildrenOf(stepT.results, stepT, 2) + } + + @Test + fun `should reference statements`() = withFactbaseFromFile("declarations") { + val stepT = findUniqueFactOrFail { it.name == "myComplexStep" } + shouldBeNChildrenOf(stepT.statements, stepT, 1) + } + + @Test + fun `should store annotation uses`() = withFactbaseFromFile("declarations") { + val stepT = findUniqueFactOrFail { it.name == "myComplexStep" } + shouldHaveNAnnotationCalls(stepT, 1) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("declarations") { + val stepT = findUniqueFactOrFail { it.name == "mySimpleStep" } + findUniqueFactOrFail { it.target == stepT.id } + } + } + + @Nested + inner class Workflow { + @Test + fun `should handle simple workflows`() = withFactbaseFromFile("declarations") { + val workflowT = findUniqueFactOrFail { it.name == "mySimpleWorkflow" } + workflowT.asClue { + workflowT.statements.shouldBeEmpty() + } + + shouldHaveNAnnotationCalls(workflowT, 0) + } + + @Test + fun `should reference statements`() = withFactbaseFromFile("declarations") { + val workflowT = findUniqueFactOrFail { it.name == "myComplexWorkflow" } + shouldBeNChildrenOf(workflowT.statements, workflowT, 1) + } + + @Test + fun `should store annotation uses`() = withFactbaseFromFile("declarations") { + val workflowT = findUniqueFactOrFail { it.name == "myComplexWorkflow" } + shouldHaveNAnnotationCalls(workflowT, 1) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("declarations") { + val workflowT = findUniqueFactOrFail { it.name == "mySimpleWorkflow" } + findUniqueFactOrFail { it.target == workflowT.id } + } + } + } + + // ***************************************************************************************************************** + // Statements + // ****************************************************************************************************************/ + + @Nested + inner class Statements { + + @Nested + inner class Assignment { + @Test + fun `should reference assignees`() = withFactbaseFromFile("statements") { + val stepT = findUniqueFactOrFail { it.name == "myFunctionalStep" } + val assignmentT = findUniqueFactOrFail { it.parent == stepT.id } + shouldBeNChildrenOf(assignmentT.assignees, assignmentT, 4) + } + + @Test + fun `should reference expression`() = withFactbaseFromFile("statements") { + val stepT = findUniqueFactOrFail { it.name == "myFunctionalStep" } + val assignmentT = findUniqueFactOrFail { it.parent == stepT.id } + shouldBeChildExpressionOf(assignmentT.expression, assignmentT) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("statements") { + val stepT = findUniqueFactOrFail { it.name == "myFunctionalStep" } + val assignmentT = findUniqueFactOrFail { it.parent == stepT.id } + findUniqueFactOrFail { it.target == assignmentT.id } + } + } + + @Nested + inner class LambdaResult { + @Test + fun `should handle lambda results`() = withFactbaseFromFile("statements") { + findUniqueFactOrFail { it.name == "mySimpleLambdaResult" } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("statements") { + val blockLambdaResultT = findUniqueFactOrFail { it.name == "mySimpleLambdaResult" } + findUniqueFactOrFail { it.target == blockLambdaResultT.id } + } + } + + @Nested + inner class Placeholder { + @Test + fun `should handle placeholders`() = withFactbaseFromFile("statements") { + findUniqueFactOrFail { it.name == "mySimplePlaceholder" } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("statements") { + val placeholderT = findUniqueFactOrFail { it.name == "mySimplePlaceholder" } + findUniqueFactOrFail { it.target == placeholderT.id } + } + } + + @Nested + inner class Wildcard { + @Test + fun `should handle wildcards`() = withFactbaseFromFile("statements") { + val stepT = findUniqueFactOrFail { it.name == "myFunctionalStep" } + val assignmentT = findUniqueFactOrFail { it.parent == stepT.id } + findUniqueFactOrFail { it.parent == assignmentT.id } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("statements") { + val stepT = findUniqueFactOrFail { it.name == "myFunctionalStep" } + val assignmentT = findUniqueFactOrFail { it.parent == stepT.id } + val wildcardT = findUniqueFactOrFail { it.parent == assignmentT.id } + findUniqueFactOrFail { it.target == wildcardT.id } + } + } + + @Nested + inner class Yield { + @Test + fun `should reference result if possible`() = withFactbaseFromFile("statements") { + val stepT = findUniqueFactOrFail { it.name == "myFunctionalStep" } + val assignmentT = findUniqueFactOrFail { it.parent == stepT.id } + val yieldT = findUniqueFactOrFail { it.parent == assignmentT.id } + val resultT = findUniqueFactOrFail { it.id == yieldT.result } + resultT.asClue { + resultT.parent shouldBe stepT.id + resultT.name shouldBe "a" + } + } + + @Test + fun `should store name for unresolvable results`() = withFactbaseFromFile("statements") { + val stepT = findUniqueFactOrFail { it.name == "myStepWithUnresolvedYield" } + val assignmentT = findUniqueFactOrFail { it.parent == stepT.id } + val yieldT = findUniqueFactOrFail { it.parent == assignmentT.id } + val unresolvedT = findUniqueFactOrFail { it.id == yieldT.result } + unresolvedT.asClue { + unresolvedT.name shouldBe "myUnresolvedResult" + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("statements") { + val stepT = findUniqueFactOrFail { it.name == "myFunctionalStep" } + val assignmentT = findUniqueFactOrFail { it.parent == stepT.id } + val yieldT = findUniqueFactOrFail { it.parent == assignmentT.id } + findUniqueFactOrFail { it.target == yieldT.id } + } + } + + @Nested + inner class ExpressionStatement { + @Test + fun `should reference expression`() = withFactbaseFromFile("statements") { + val expressionStatementT = findUniqueFactOrFail() + shouldBeChildExpressionOf(expressionStatementT.expression, expressionStatementT) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("statements") { + val expressionStatementT = findUniqueFactOrFail() + findUniqueFactOrFail { it.target == expressionStatementT.id } + } + } + } + + // ***************************************************************************************************************** + // Expressions + // ****************************************************************************************************************/ + + @Nested + inner class Expressions { + + @Nested + inner class Argument { + @Test + fun `should handle positional arguments`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithPositionalArgument" } + val argumentT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + argumentT.asClue { + argumentT.parameter.shouldBeNull() + } + } + + @Test + fun `should reference parameter if possible`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithResolvableNamedArgument" } + val argumentT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + val parameterT = findUniqueFactOrFail { it.id == argumentT.parameter } + parameterT.asClue { + parameterT.name shouldBe "a" + } + } + + @Test + fun `should reference value`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithResolvableNamedArgument" } + val argumentT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeChildExpressionOf(argumentT.value, argumentT) + } + + @Test + fun `should store name for unresolvable arguments`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithUnresolvedArgument" } + val argumentT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + val unresolvedT = findUniqueFactOrFail { it.id == argumentT.parameter } + unresolvedT.asClue { + unresolvedT.name shouldBe "myUnresolvedParameter" + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithPositionalArgument" } + val argumentT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == argumentT.id } + } + } + + @Nested + inner class Boolean { + @Test + fun `should store value`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithLiterals" } + val booleanT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + booleanT.asClue { + booleanT.value shouldBe true + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithLiterals" } + val booleanT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == booleanT.id } + } + } + + @Nested + inner class BlockLambda { + @Test + fun `should handle simple block lambdas`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithSimpleBlockLambda" } + val blockLambdaT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + blockLambdaT.asClue { + blockLambdaT.parameters.shouldBeEmpty() + blockLambdaT.statements.shouldBeEmpty() + } + } + + @Test + fun `should reference parameters`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithComplexBlockLambda" } + val blockLambdaT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeNChildrenOf(blockLambdaT.parameters, blockLambdaT, 2) + } + + @Test + fun `should reference statements`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithComplexBlockLambda" } + val blockLambdaT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeNChildrenOf(blockLambdaT.statements, blockLambdaT, 2) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithSimpleBlockLambda" } + val blockLambdaT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == blockLambdaT.id } + } + } + + @Nested + inner class Call { + @Test + fun `should handle simple calls`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithSimpleCall" } + val callT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + callT.asClue { + callT.typeArguments.shouldBeNull() + callT.arguments.shouldBeEmpty() + } + } + + @Test + fun `should reference receiver`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithComplexCall" } + val callT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeChildExpressionOf(callT.receiver, callT) + } + + @Test + fun `should reference type arguments`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithComplexCall" } + val callT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeNChildrenOf(callT.typeArguments, callT, 2) + } + + @Test + fun `should reference arguments`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithComplexCall" } + val callT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeNChildExpressionsOf(callT.arguments, callT, 2) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithSimpleCall" } + val callT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == callT.id } + } + } + + @Nested + inner class ExpressionLambda { + @Test + fun `should handle simple expression lambdas`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithSimpleExpressionLambda" } + val expressionLambdaT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + expressionLambdaT.asClue { + expressionLambdaT.parameters.shouldBeEmpty() + } + } + + @Test + fun `should reference parameters`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithComplexExpressionLambda" } + val expressionLambdaT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeNChildrenOf(expressionLambdaT.parameters, expressionLambdaT, 2) + } + + @Test + fun `should reference result`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithComplexExpressionLambda" } + val expressionLambdaT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeChildOf(expressionLambdaT.result, expressionLambdaT) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithSimpleExpressionLambda" } + val expressionLambdaT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == expressionLambdaT.id } + } + } + + @Nested + inner class Float { + @Test + fun `should store value`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithLiterals" } + val floatT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + floatT.asClue { + floatT.value.shouldBeCloseTo(1.0) + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithLiterals" } + val floatT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == floatT.id } + } + } + + @Nested + inner class IndexedAccess { + @Test + fun `should reference receiver`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithIndexedAccess" } + val indexedAccessT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeChildExpressionOf(indexedAccessT.receiver, indexedAccessT) + } + + @Test + fun `should reference index`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithIndexedAccess" } + val indexedAccessT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeChildExpressionOf(indexedAccessT.index, indexedAccessT) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithIndexedAccess" } + val indexedAccessT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == indexedAccessT.id } + } + } + + @Nested + inner class InfixOperation { + @Test + fun `should reference left operand`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithOperations" } + val infixOperationT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeChildExpressionOf(infixOperationT.leftOperand, infixOperationT) + } + + @Test + fun `should store operator`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithOperations" } + val infixOperationT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + infixOperationT.asClue { + infixOperationT.operator shouldBe "+" + } + } + + @Test + fun `should reference right operand`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithOperations" } + val infixOperationT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeChildExpressionOf(infixOperationT.rightOperand, infixOperationT) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithOperations" } + val infixOperationT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == infixOperationT.id } + } + } + + @Nested + inner class Int { + @Test + fun `should store value`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithLiterals" } + val intT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + intT.asClue { + intT.value shouldBe 42 + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithLiterals" } + val intT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == intT.id } + } + } + + @Nested + inner class MemberAccess { + @Test + fun `should reference receiver`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithMemberAccess" } + val memberAccessT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeChildExpressionOf(memberAccessT.receiver, memberAccessT) + } + + @Test + fun `should store null safety`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithMemberAccess" } + val memberAccessT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + memberAccessT.asClue { + memberAccessT.isNullSafe shouldBe true + } + } + + @Test + fun `should reference member`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithMemberAccess" } + val memberAccessT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeChildExpressionOf(memberAccessT.member, memberAccessT) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithMemberAccess" } + val memberAccessT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == memberAccessT.id } + } + } + + @Nested + inner class Null { + @Test + fun `should handle nulls`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithLiterals" } + findUniqueFactOrFail { isContainedIn(it, workflowT) } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithLiterals" } + val nullT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == nullT.id } + } + } + + @Nested + inner class ParenthesizedExpression { + @Test + fun `should reference expression`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithParenthesizedExpression" } + val parenthesizedExpressionT = + findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeChildExpressionOf(parenthesizedExpressionT.expression, parenthesizedExpressionT) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithParenthesizedExpression" } + val parenthesizedExpressionT = + findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == parenthesizedExpressionT.id } + } + } + + @Nested + inner class PrefixOperation { + @Test + fun `should store operator`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithOperations" } + val prefixOperationT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + prefixOperationT.asClue { + prefixOperationT.operator shouldBe "-" + } + } + + @Test + fun `should reference operand`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithOperations" } + val prefixOperationT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeChildExpressionOf(prefixOperationT.operand, prefixOperationT) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithOperations" } + val prefixOperationT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == prefixOperationT.id } + } + } + + @Nested + inner class Reference { + @Test + fun `should reference declaration if possible`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithResolvableReference" } + val referenceT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + val placeholderT = findUniqueFactOrFail { it.id == referenceT.declaration } + placeholderT.asClue { + placeholderT.name shouldBe "a" + } + } + + @Test + fun `should store name for unresolvable references`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithUnresolvableReference" } + val referenceT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + val unresolvedT = findUniqueFactOrFail { it.id == referenceT.declaration } + unresolvedT.asClue { + unresolvedT.name shouldBe "myUnresolvedDeclaration" + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithResolvableReference" } + val referenceT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == referenceT.id } + } + } + + @Nested + inner class String { + @Test + fun `should store value`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithLiterals" } + val stringT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + stringT.asClue { + stringT.value shouldBe "bla" + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithLiterals" } + val stringT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == stringT.id } + } + } + + @Nested + inner class TemplateString { + @Test + fun `should store expressions`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithTemplateString" } + val templateStringT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeNChildExpressionsOf(templateStringT.expressions, templateStringT, 5) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithTemplateString" } + val templateStringT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == templateStringT.id } + } + } + + @Nested + inner class TemplateStringStart { + @Test + fun `should store value`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithTemplateString" } + val templateStringStartT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + templateStringStartT.asClue { + templateStringStartT.value shouldBe "start " + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithTemplateString" } + val templateStringStartT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + templateStringStartT.asClue { + findUniqueFactOrFail { it.target == templateStringStartT.id } + } + } + } + + @Nested + inner class TemplateStringInner { + @Test + fun `should store value`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithTemplateString" } + val templateStringInnerT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + templateStringInnerT.asClue { + templateStringInnerT.value shouldBe " inner " + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithTemplateString" } + val templateStringInnerT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + templateStringInnerT.asClue { + findUniqueFactOrFail { it.target == templateStringInnerT.id } + } + } + } + + @Nested + inner class TemplateStringEnd { + @Test + fun `should store value`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithTemplateString" } + val templateStringEndT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + templateStringEndT.asClue { + templateStringEndT.value shouldBe " end" + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("expressions") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithTemplateString" } + val templateStringEndT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + templateStringEndT.asClue { + findUniqueFactOrFail { it.target == templateStringEndT.id } + } + } + } + } + + // ***************************************************************************************************************** + // Protocols + // ****************************************************************************************************************/ + + @Nested + inner class Protocols { + + @Nested + inner class Protocols { + @Test + fun `should handle simple protocols`() = withFactbaseFromFile("protocols") { + val classT = findUniqueFactOrFail { it.name == "MyClassWithSimpleProtocol" } + val protocolT = findUniqueFactOrFail { it.parent == classT.id } + protocolT.asClue { + protocolT.subterms.shouldBeNull() + protocolT.term.shouldBeNull() + } + } + + @Test + fun `should reference subterms`() = withFactbaseFromFile("protocols") { + val classT = findUniqueFactOrFail { it.name == "MyClassWithComplexProtocol" } + val protocolT = findUniqueFactOrFail { it.parent == classT.id } + shouldBeNChildrenOf(protocolT.subterms, protocolT, 9) + } + + @Test + fun `should reference term`() = withFactbaseFromFile("protocols") { + val classT = findUniqueFactOrFail { it.name == "MyClassWithComplexProtocol" } + val protocolT = findUniqueFactOrFail { it.parent == classT.id } + shouldBeChildProtocolTermOf(protocolT.term, protocolT) + } + + @Test + fun `should store source location in separate relation`() = + withFactbaseFromFile("protocols") { + val classT = findUniqueFactOrFail { it.name == "MyClassWithSimpleProtocol" } + val protocolT = findUniqueFactOrFail { it.parent == classT.id } + findUniqueFactOrFail { it.target == protocolT.id } + } + } + + @Nested + inner class ProtocolAlternatives { + @Test + fun `should reference term`() = withFactbaseFromFile("protocols") { + val alternativeT = findUniqueFactOrFail() + shouldBeNChildProtocolTermsOf(alternativeT.terms, alternativeT, 2) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("protocols") { + val alternativeT = findUniqueFactOrFail() + findUniqueFactOrFail { it.target == alternativeT.id } + } + } + + @Nested + inner class ProtocolComplements { + @Test + fun `should handle simple complements`() = withFactbaseFromFile("protocols") { + val subtermT = findUniqueFactOrFail { it.name == "simpleComplement" } + val complementT = findUniqueFactOrFail { isContainedIn(it, subtermT) } + complementT.asClue { + complementT.universe.shouldBeNull() + complementT.references.shouldBeNull() + } + } + + @Test + fun `should reference universe`() = withFactbaseFromFile("protocols") { + val subtermT = findUniqueFactOrFail { it.name == "complexComplement" } + val complementT = findUniqueFactOrFail { isContainedIn(it, subtermT) } + shouldBeChildProtocolTermOf(complementT.universe, complementT) + } + + @Test + fun `should reference references`() = withFactbaseFromFile("protocols") { + val subtermT = findUniqueFactOrFail { it.name == "complexComplement" } + val complementT = findUniqueFactOrFail { isContainedIn(it, subtermT) } + shouldBeNChildProtocolTermsOf(complementT.references, complementT, 2) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("protocols") { + val subtermT = findUniqueFactOrFail { it.name == "simpleComplement" } + val complementT = findUniqueFactOrFail { isContainedIn(it, subtermT) } + findUniqueFactOrFail { it.target == complementT.id } + } + } + + @Nested + inner class ProtocolParenthesizedTerms { + @Test + fun `should reference term`() = withFactbaseFromFile("protocols") { + val parenthesizedTermT = findUniqueFactOrFail() + shouldBeChildProtocolTermOf(parenthesizedTermT.term, parenthesizedTermT) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("protocols") { + val parenthesizedTermT = findUniqueFactOrFail() + findUniqueFactOrFail { it.target == parenthesizedTermT.id } + } + } + + @Nested + inner class ProtocolQuantifiedTerms { + @Test + fun `should reference term`() = withFactbaseFromFile("protocols") { + val quantifiedTermT = findUniqueFactOrFail() + shouldBeChildProtocolTermOf(quantifiedTermT.term, quantifiedTermT) + } + + @Test + fun `should store quantified`() = withFactbaseFromFile("protocols") { + val quantifiedTermT = findUniqueFactOrFail() + quantifiedTermT.asClue { + quantifiedTermT.quantifier shouldBe "?" + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("protocols") { + val quantifiedTermT = findUniqueFactOrFail() + findUniqueFactOrFail { it.target == quantifiedTermT.id } + } + } + + @Nested + inner class ProtocolReferences { + @Test + fun `should reference declaration if possible`() = withFactbaseFromFile("protocols") { + val subtermT = findUniqueFactOrFail { it.name == "reference" } + val referenceT = findUniqueFactOrFail { isContainedIn(it, subtermT) } + val attributeT = findUniqueFactOrFail { it.id == referenceT.token } + attributeT.asClue { + attributeT.name shouldBe "member" + } + } + + @Test + fun `should store name for unresolvable references`() = withFactbaseFromFile("protocols") { + val subtermT = findUniqueFactOrFail { it.name == "unresolvedReference" } + val referenceT = findUniqueFactOrFail { isContainedIn(it, subtermT) } + val unresolvedT = findUniqueFactOrFail { it.id == referenceT.token } + unresolvedT.asClue { + unresolvedT.name shouldBe "unresolved" + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("protocols") { + val subtermT = findUniqueFactOrFail { it.name == "reference" } + val referenceT = findUniqueFactOrFail { isContainedIn(it, subtermT) } + findUniqueFactOrFail { it.target == referenceT.id } + } + } + + @Nested + inner class ProtocolSequences { + @Test + fun `should reference terms`() = withFactbaseFromFile("protocols") { + val sequenceT = findUniqueFactOrFail() + shouldBeNChildProtocolTermsOf(sequenceT.terms, sequenceT, 2) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("protocols") { + val sequenceT = findUniqueFactOrFail() + findUniqueFactOrFail { it.target == sequenceT.id } + } + } + + @Nested + inner class ProtocolSubterms { + @Test + fun `should store name`() = withFactbaseFromFile("protocols") { + findUniqueFactOrFail { it.name == "alternative" } + } + + @Test + fun `should reference term`() = withFactbaseFromFile("protocols") { + val subtermT = findUniqueFactOrFail { it.name == "alternative" } + shouldBeChildProtocolTermOf(subtermT.term, subtermT) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("protocols") { + val subtermT = findUniqueFactOrFail { it.name == "alternative" } + findUniqueFactOrFail { it.target == subtermT.id } + } + } + + @Nested + inner class ProtocolTokenClasses { + @Test + fun `should store value`() = withFactbaseFromFile("protocols") { + val subtermT = findUniqueFactOrFail { it.name == "tokenClass" } + val tokenClassT = findUniqueFactOrFail { isContainedIn(it, subtermT) } + tokenClassT.asClue { + it.value shouldBe "\\a" + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("protocols") { + val subtermT = findUniqueFactOrFail { it.name == "tokenClass" } + val tokenClassT = findUniqueFactOrFail { isContainedIn(it, subtermT) } + findUniqueFactOrFail { it.target == tokenClassT.id } + } + } + } + + // ***************************************************************************************************************** + // Types + // ****************************************************************************************************************/ + + @Nested + inner class Types { + + @Nested + inner class CallableType { + @Test + fun `should handle simple callable types`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowStepWithSimpleCallableType" } + val callableTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + callableTypeT.asClue { + callableTypeT.parameters.shouldBeEmpty() + callableTypeT.results.shouldBeEmpty() + } + } + + @Test + fun `should reference parameters`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowStepWithComplexCallableType" } + val callableTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + shouldBeNChildrenOf(callableTypeT.parameters, callableTypeT, 2) + } + + @Test + fun `should reference results`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowStepWithComplexCallableType" } + val callableTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + shouldBeNChildrenOf(callableTypeT.results, callableTypeT, 2) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowStepWithSimpleCallableType" } + val callableTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + findUniqueFactOrFail { it.target == callableTypeT.id } + } + } + + @Nested + inner class MemberType { + @Test + fun `should reference receiver`() = withFactbaseFromFile("types") { + val stepT = findUniqueFactOrFail { it.name == "myWorkflowStepWithMemberType" } + val memberTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + shouldBeChildOf(memberTypeT.receiver, memberTypeT) + } + + @Test + fun `should reference member`() = withFactbaseFromFile("types") { + val stepT = findUniqueFactOrFail { it.name == "myWorkflowStepWithMemberType" } + val memberTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + shouldBeChildOf(memberTypeT.member, memberTypeT) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("types") { + val stepT = findUniqueFactOrFail { it.name == "myWorkflowStepWithMemberType" } + val memberTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + findUniqueFactOrFail { it.target == memberTypeT.id } + } + } + + @Nested + inner class NamedType { + @Test + fun `should handle simple named types`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowStepWithSimpleResolvableNamedType" } + val namedTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + namedTypeT.asClue { + namedTypeT.typeArguments.shouldBeNull() + namedTypeT.isNullable shouldBe false + } + } + + @Test + fun `should reference declaration if possible`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowStepWithComplexResolvableNamedType" } + val namedTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + val declarationT = findUniqueFactOrFail { it.id == namedTypeT.declaration } + declarationT.asClue { + declarationT.name shouldBe "C" + } + } + + @Test + fun `should reference type arguments`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowStepWithComplexResolvableNamedType" } + val namedTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + shouldBeNChildrenOf(namedTypeT.typeArguments, namedTypeT, 1) + } + + @Test + fun `should store nullability`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowStepWithComplexResolvableNamedType" } + val namedTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + namedTypeT.asClue { + namedTypeT.isNullable shouldBe true + } + } + + @Test + fun `should store name for unresolvable named types`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowWithUnresolvableNamedType" } + val namedTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + val unresolvedT = findUniqueFactOrFail { it.id == namedTypeT.declaration } + unresolvedT.asClue { + unresolvedT.name shouldBe "MyUnresolvedDeclaration" + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowStepWithSimpleResolvableNamedType" } + val namedTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + findUniqueFactOrFail { it.target == namedTypeT.id } + } + } + + @Nested + inner class ParenthesizedType { + @Test + fun `should reference type`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowStepWithParenthesizedType" } + val parenthesizedTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + shouldBeChildOf(parenthesizedTypeT.type, parenthesizedTypeT) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowStepWithParenthesizedType" } + val parenthesizedTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + findUniqueFactOrFail { it.target == parenthesizedTypeT.id } + } + } + + @Nested + inner class StarProjection { + @Test + fun `should handle star projections`() = withFactbaseFromFile("types") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithStarProjection" } + findUniqueFactOrFail { isContainedIn(it, workflowT) } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("types") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithStarProjection" } + val starProjectionT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == starProjectionT.id } + } + } + + @Nested + inner class TypeArgument { + @Test + fun `should handle positional type arguments`() = withFactbaseFromFile("types") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithPositionalTypeArgument" } + val typeArgumentT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + typeArgumentT.asClue { + typeArgumentT.typeParameter.shouldBeNull() + } + } + + @Test + fun `should reference type parameter if possible`() = withFactbaseFromFile("types") { + val workflowT = + findUniqueFactOrFail { it.name == "myWorkflowWithResolvableNamedTypeArgument" } + val typeArgumentT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + val typeParameterT = findUniqueFactOrFail { it.id == typeArgumentT.typeParameter } + typeParameterT.asClue { + typeParameterT.name shouldBe "T" + } + } + + @Test + fun `should reference value`() = withFactbaseFromFile("types") { + val workflowT = + findUniqueFactOrFail { it.name == "myWorkflowWithResolvableNamedTypeArgument" } + val typeArgumentT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeChildOf(typeArgumentT.value, typeArgumentT) + } + + @Test + fun `should store name for unresolvable type arguments`() = withFactbaseFromFile("types") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithUnresolvedTypeArgument" } + val typeArgumentT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + val unresolvedT = findUniqueFactOrFail { it.id == typeArgumentT.typeParameter } + unresolvedT.asClue { + unresolvedT.name shouldBe "MY_UNRESOLVED_TYPE_PARAMETER" + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("types") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithPositionalTypeArgument" } + val typeArgumentT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == typeArgumentT.id } + } + } + + @Nested + inner class TypeParameterConstraint { + @Test + fun `should reference left operand if possible`() = withFactbaseFromFile("types") { + val functionT = + findUniqueFactOrFail { it.name == "myFunctionWithResolvableTypeParameterConstraint" } + val typeParameterConstraintT = + findUniqueFactOrFail { isContainedIn(it, functionT) } + val typeParameterT = + findUniqueFactOrFail { it.id == typeParameterConstraintT.leftOperand } + typeParameterT.asClue { + typeParameterT.name shouldBe "T" + } + } + + @Test + fun `should store operator`() = withFactbaseFromFile("types") { + val functionT = + findUniqueFactOrFail { it.name == "myFunctionWithResolvableTypeParameterConstraint" } + val typeParameterConstraintT = + findUniqueFactOrFail { isContainedIn(it, functionT) } + typeParameterConstraintT.asClue { + typeParameterConstraintT.operator shouldBe "sub" + } + } + + @Test + fun `should reference right operand`() = withFactbaseFromFile("types") { + val functionT = + findUniqueFactOrFail { it.name == "myFunctionWithResolvableTypeParameterConstraint" } + val typeParameterConstraintT = + findUniqueFactOrFail { isContainedIn(it, functionT) } + shouldBeChildOf(typeParameterConstraintT.rightOperand, typeParameterConstraintT) + } + + @Test + fun `should store name for unresolvable type parameters`() = withFactbaseFromFile("types") { + val functionT = + findUniqueFactOrFail { it.name == "myFunctionWithUnresolvableTypeParameterConstraint" } + val typeParameterConstraintT = + findUniqueFactOrFail { isContainedIn(it, functionT) } + val unresolvedT = findUniqueFactOrFail { it.id == typeParameterConstraintT.leftOperand } + unresolvedT.asClue { + unresolvedT.name shouldBe "MY_UNRESOLVED_TYPE_PARAMETER" + } + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("types") { + val functionT = + findUniqueFactOrFail { it.name == "myFunctionWithResolvableTypeParameterConstraint" } + val typeParameterConstraintT = + findUniqueFactOrFail { isContainedIn(it, functionT) } + findUniqueFactOrFail { it.target == typeParameterConstraintT.id } + } + } + + @Nested + inner class TypeProjection { + @Test + fun `should handle simple type projections`() = withFactbaseFromFile("types") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithSimpleTypeProjection" } + val typeProjectionT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + typeProjectionT.asClue { + typeProjectionT.variance.shouldBeNull() + } + } + + @Test + fun `should store variance`() = withFactbaseFromFile("types") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithComplexTypeProjection" } + val typeProjectionT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + typeProjectionT.asClue { + typeProjectionT.variance shouldBe "out" + } + } + + @Test + fun `should reference type`() = withFactbaseFromFile("types") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithComplexTypeProjection" } + val typeProjectionT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + shouldBeChildOf(typeProjectionT.type, typeProjectionT) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("types") { + val workflowT = findUniqueFactOrFail { it.name == "myWorkflowWithSimpleTypeProjection" } + val typeProjectionT = findUniqueFactOrFail { isContainedIn(it, workflowT) } + findUniqueFactOrFail { it.target == typeProjectionT.id } + } + } + + @Nested + inner class UnionType { + @Test + fun `should handle simple union types`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowStepWithSimpleUnionType" } + val unionTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + unionTypeT.asClue { + unionTypeT.typeArguments.shouldBeEmpty() + } + } + + @Test + fun `should store type arguments`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowStepWithComplexUnionType" } + val unionTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + shouldBeNChildrenOf(unionTypeT.typeArguments, unionTypeT, 2) + } + + @Test + fun `should store source location in separate relation`() = withFactbaseFromFile("types") { + val stepT = + findUniqueFactOrFail { it.name == "myWorkflowStepWithSimpleUnionType" } + val unionTypeT = findUniqueFactOrFail { isContainedIn(it, stepT) } + findUniqueFactOrFail { it.target == unionTypeT.id } + } + } + } + + // ***************************************************************************************************************** + // Other + // ****************************************************************************************************************/ + + @Nested + inner class Other { + + @Nested + inner class AnnotationCalls { + @Test + fun `should handle simple annotation uses`() = withFactbaseFromFile("annotationCalls") { + val classT = findUniqueFactOrFail { it.name == "MyClassWithSimpleAnnotationCall" } + val annotationCallT = findUniqueFactOrFail { it.parent == classT.id } + annotationCallT.asClue { + annotationCallT.arguments.shouldBeNull() + } + } + + @Test + fun `should reference annotation if possible`() = withFactbaseFromFile("annotationCalls") { + val classT = findUniqueFactOrFail { it.name == "MyClassWithSimpleAnnotationCall" } + val annotationCallT = findUniqueFactOrFail { it.parent == classT.id } + val annotationT = findUniqueFactOrFail { it.id == annotationCallT.annotation } + annotationT.asClue { + annotationT.name shouldBe "MyAnnotation" + } + } + + @Test + fun `should reference arguments`() = withFactbaseFromFile("annotationCalls") { + val classT = findUniqueFactOrFail { it.name == "MyClassWithComplexAnnotationCall" } + val annotationCallT = findUniqueFactOrFail { it.parent == classT.id } + shouldBeNChildExpressionsOf(annotationCallT.arguments, annotationCallT, 2) + } + + @Test + fun `should store name for unresolvable annotations`() = withFactbaseFromFile("annotationCalls") { + val classT = findUniqueFactOrFail { it.name == "MyClassWithUnresolvedAnnotationCall" } + val annotationCallT = findUniqueFactOrFail { it.parent == classT.id } + val unresolvedT = findUniqueFactOrFail { it.id == annotationCallT.annotation } + unresolvedT.asClue { + unresolvedT.name shouldBe "MyUnresolvedAnnotation" + } + } + + @Test + fun `should store source location in separate relation`() = + withFactbaseFromFile("annotationCalls") { + val classT = findUniqueFactOrFail { it.name == "MyClassWithSimpleAnnotationCall" } + val annotationCallT = findUniqueFactOrFail { it.parent == classT.id } + findUniqueFactOrFail { it.target == annotationCallT.id } + } + } + + @Nested + inner class SourceLocation { + + @Test + fun `should store uri hash`() = withFactbaseFromFile("declarations") { + val importT = findUniqueFactOrFail { it.importedNamespace == "myPackage.MyOtherClass" } + val sourceLocationS = findUniqueFactOrFail { it.target == importT.id } + sourceLocationS.asClue { + sourceLocationS.uriHash shouldBe "//@imports.1" + } + } + + @Test + fun `should store offset`() = withFactbaseFromFile("declarations") { + val importT = findUniqueFactOrFail { it.importedNamespace == "myPackage.MyClass" } + val sourceLocationS = findUniqueFactOrFail { it.target == importT.id } + sourceLocationS.asClue { + // Actual offset depends on the new line characters (48 for just \n or \r and 50 for \r\n) + sourceLocationS.offset shouldBeOneOf listOf(48, 50) + } + } + + @Test + fun `should store line`() = withFactbaseFromFile("declarations") { + val importT = findUniqueFactOrFail { it.importedNamespace == "myPackage.MyClass" } + val sourceLocationS = findUniqueFactOrFail { it.target == importT.id } + sourceLocationS.asClue { + sourceLocationS.line shouldBe 3 + } + } + + @Test + fun `should store column`() = withFactbaseFromFile("declarations") { + val importT = findUniqueFactOrFail { it.importedNamespace == "myPackage.MyClass" } + val sourceLocationS = findUniqueFactOrFail { it.target == importT.id } + sourceLocationS.asClue { + sourceLocationS.column shouldBe 1 + } + } + + @Test + fun `should store length`() = withFactbaseFromFile("declarations") { + val importT = findUniqueFactOrFail { it.importedNamespace == "myPackage.MyClass" } + val sourceLocationS = findUniqueFactOrFail { it.target == importT.id } + sourceLocationS.asClue { + sourceLocationS.length shouldBe 24 + } + } + } + } + + // ***************************************************************************************************************** + // Helpers + // ****************************************************************************************************************/ + + private fun withFactbaseFromFile(file: String, lambda: PlFactbase.() -> Unit) { + main.createFactbase("$testRoot/$file.${SmlFileExtension.Test}").apply(lambda) + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/scoping/ScopingTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/scoping/ScopingTest.kt new file mode 100644 index 000000000..0af0647e2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/scoping/ScopingTest.kt @@ -0,0 +1,2439 @@ +package de.unibonn.simpleml.scoping + +import com.google.inject.Inject +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.emf.annotationCallsOrEmpty +import de.unibonn.simpleml.emf.descendants +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.simpleML.SmlAnnotationCall +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.simpleML.SmlBlockLambdaResult +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlMemberType +import de.unibonn.simpleml.simpleML.SmlNamedType +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlProtocolReference +import de.unibonn.simpleml.simpleML.SmlProtocolSubterm +import de.unibonn.simpleml.simpleML.SmlReference +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlTypeArgument +import de.unibonn.simpleml.simpleML.SmlTypeParameter +import de.unibonn.simpleml.simpleML.SmlTypeParameterConstraint +import de.unibonn.simpleml.simpleML.SmlYield +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.ResourceName +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.assertions.findUniqueDeclarationOrFail +import de.unibonn.simpleml.testing.assertions.shouldBeResolved +import de.unibonn.simpleml.testing.assertions.shouldNotBeResolved +import io.kotest.assertions.forEachAsClue +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +private const val ANNOTATION_CALL = "annotationCall" +private const val ARGUMENT = "argument" +private const val IMPORT_WITH_ALIAS = "importWithAlias" +private const val NAMED_TYPE = "namedType" +private const val PROTOCOL_REFERENCE = "protocolReference" +private const val REFERENCE = "reference" +private const val TYPE_ARGUMENT = "typeArgument" +private const val TYPE_PARAMETER_CONSTRAINT = "typeParameterConstraint" +private const val YIELD = "yield" + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class ScopingTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + @Nested + inner class AnnotationCall { + + @Test + fun `should resolve annotations in same file`() = withResource(ANNOTATION_CALL) { + val annotationCalls = this.descendants().toList() + annotationCalls.shouldHaveSize(6) + + val annotationInSameFile = findUniqueDeclarationOrFail("AnnotationInSameFile") + + val referencedAnnotation = annotationCalls[0].annotation + referencedAnnotation.shouldBeResolved() + referencedAnnotation.shouldBe(annotationInSameFile) + } + + @Test + fun `should resolve annotations in same package`() = withResource(ANNOTATION_CALL) { + val annotationCalls = this.descendants().toList() + annotationCalls.shouldHaveSize(6) + + val annotation = annotationCalls[1].annotation + annotation.shouldBeResolved() + annotation.name.shouldBe("AnnotationInSamePackage") + } + + @Test + fun `should resolve annotations in another package if imported`() = withResource(ANNOTATION_CALL) { + val annotationCalls = this.descendants().toList() + annotationCalls.shouldHaveSize(6) + + val annotation = annotationCalls[2].annotation + annotation.shouldBeResolved() + annotation.name.shouldBe("AnnotationInOtherPackage1") + } + + @Test + fun `should not resolve annotations in another package if not imported`() = withResource(ANNOTATION_CALL) { + val annotationCalls = this.descendants().toList() + annotationCalls.shouldHaveSize(6) + annotationCalls[3].annotation.shouldNotBeResolved() + } + + @Test + fun `should not resolve unknown declaration`() = withResource(ANNOTATION_CALL) { + val annotationCalls = this.descendants().toList() + annotationCalls.shouldHaveSize(6) + annotationCalls[4].annotation.shouldNotBeResolved() + } + + @Test + fun `should not resolve something that is not an annotation`() = withResource(ANNOTATION_CALL) { + val annotationCalls = this.descendants().toList() + annotationCalls.shouldHaveSize(6) + annotationCalls[5].annotation.shouldNotBeResolved() + } + } + + @Nested + inner class Argument { + + @Test + fun `should resolve parameter in use annotation in same file`() = + withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + + val parameterInAnnotationInSameFile = + findUniqueDeclarationOrFail("parameterInAnnotationInSameFile") + + val referencedParameter = arguments[0].parameter + referencedParameter.shouldBeResolved() + referencedParameter.shouldBe(parameterInAnnotationInSameFile) + } + + @Test + fun `should resolve parameter in called block lambda in same step`() = + withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + + val parameterInLambdaInSameStep = + findUniqueDeclarationOrFail("parameterInBlockLambdaInSameStep") + + val referencedParameter = arguments[1].parameter + referencedParameter.shouldBeResolved() + referencedParameter.shouldBe(parameterInLambdaInSameStep) + } + + @Test + fun `should resolve parameter in called callable in same step`() = + withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + + val parameterInCallableInSameStep = + findUniqueDeclarationOrFail("parameterInCallableInSameStep") + + val referencedParameter = arguments[2].parameter + referencedParameter.shouldBeResolved() + referencedParameter.shouldBe(parameterInCallableInSameStep) + } + + @Test + fun `should resolve parameter in called class in same file`() = + withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + + val parameterInClassInSameFile = findUniqueDeclarationOrFail("parameterInClassInSameFile") + + val referencedParameter = arguments[3].parameter + referencedParameter.shouldBeResolved() + referencedParameter.shouldBe(parameterInClassInSameFile) + } + + @Test + fun `should resolve parameter in called enum variant in same file`() = + withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + + val parameterInEnumVariantInSameFile = + findUniqueDeclarationOrFail("parameterInEnumVariantInSameFile") + + val referencedParameter = arguments[4].parameter + referencedParameter.shouldBeResolved() + referencedParameter.shouldBe(parameterInEnumVariantInSameFile) + } + + @Test + fun `should resolve parameter in called expression lambda in same step`() = + withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + + val parameterInLambdaInSameStep = + findUniqueDeclarationOrFail("parameterInExpressionLambdaInSameStep") + + val referencedParameter = arguments[5].parameter + referencedParameter.shouldBeResolved() + referencedParameter.shouldBe(parameterInLambdaInSameStep) + } + + @Test + fun `should resolve parameter in called function in same file`() = + withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + + val parameterInFunctionSameFile = + findUniqueDeclarationOrFail("parameterInFunctionSameFile") + + val referencedParameter = arguments[6].parameter + referencedParameter.shouldBeResolved() + referencedParameter.shouldBe(parameterInFunctionSameFile) + } + + @Test + fun `should resolve parameter in called step in same file`() = + withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + + val parameterInStepInSameFile = findUniqueDeclarationOrFail("parameterInStepInSameFile") + + val referencedParameter = arguments[7].parameter + referencedParameter.shouldBeResolved() + referencedParameter.shouldBe(parameterInStepInSameFile) + } + + @Test + fun `should resolve parameter in called function in same package`() = + withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + + val referencedParameter = arguments[8].parameter + referencedParameter.shouldBeResolved() + referencedParameter.name.shouldBe("parameterInSamePackage") + } + + @Test + fun `should resolve parameter in called function that is imported and in another package`() = + withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + + val referencedParameter = arguments[9].parameter + referencedParameter.shouldBeResolved() + referencedParameter.name.shouldBe("parameterInOtherPackage1") + } + + @Test + fun `should not resolve parameter in called function that is not imported and in another package`() = + withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + arguments[10].parameter.shouldNotBeResolved() + } + + @Test + fun `should not resolve parameter in function other than called one in same package`() = + withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + arguments[11].parameter.shouldNotBeResolved() + } + + @Test + fun `should not resolve parameter in function other than called one that is imported and in another package`() = + withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + arguments[12].parameter.shouldNotBeResolved() + } + + @Test + fun `should not resolve parameter in function other than called one that is not imported and in another package`() = + withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + arguments[13].parameter.shouldNotBeResolved() + } + + @Test + fun `should not resolve unknown declaration`() = withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + arguments[14].parameter.shouldNotBeResolved() + } + + @Test + fun `should not resolve something that is not a parameter`() = withResource(ARGUMENT) { + val arguments = this.descendants().toList() + arguments.shouldHaveSize(16) + arguments[15].parameter.shouldNotBeResolved() + } + } + + @Nested + inner class ImportWithAlias { + + @Test + fun `should resolve alias name of declaration in same file`() = withResource(IMPORT_WITH_ALIAS) { + val aliasNameInSameFile = findUniqueDeclarationOrFail("aliasNameInSameFile") + val classInSameFile = findUniqueDeclarationOrFail("ClassInSameFile") + + val type = aliasNameInSameFile.type + type.shouldBeInstanceOf() + + val declaration = type.declaration + declaration.shouldBeResolved() + declaration.shouldBe(classInSameFile) + } + + @Test + fun `should resolve original name of declaration in same file`() = withResource(IMPORT_WITH_ALIAS) { + val originalNameInSameFile = findUniqueDeclarationOrFail("originalNameInSameFile") + + val type = originalNameInSameFile.type + type.shouldBeInstanceOf() + + val declaration = type.declaration + declaration.shouldBeResolved() + declaration.name.shouldBe("ClassInSameFile") + } + + @Test + fun `should resolve alias name of declaration in same package`() = withResource(IMPORT_WITH_ALIAS) { + val aliasNameInSamePackage = findUniqueDeclarationOrFail("aliasNameInSamePackage") + + val type = aliasNameInSamePackage.type + type.shouldBeInstanceOf() + + val declaration = type.declaration + declaration.shouldBeResolved() + declaration.name.shouldBe("ClassInSamePackage") + } + + @Test + fun `should resolve original name of declaration in same package`() = withResource(IMPORT_WITH_ALIAS) { + val originalNameInSamePackage = findUniqueDeclarationOrFail("originalNameInSamePackage") + + val type = originalNameInSamePackage.type + type.shouldBeInstanceOf() + + val declaration = type.declaration + declaration.shouldBeResolved() + declaration.name.shouldBe("ClassInSamePackage") + } + + @Test + fun `should resolve alias name of declaration in other package`() = withResource(IMPORT_WITH_ALIAS) { + val aliasNameInOtherPackage = findUniqueDeclarationOrFail("aliasNameInOtherPackage") + + val type = aliasNameInOtherPackage.type + type.shouldBeInstanceOf() + + val declaration = type.declaration + declaration.shouldBeResolved() + declaration.name.shouldBe("ClassInOtherPackage") + } + + @Test + fun `should not resolve original name of declaration in other package`() = withResource(IMPORT_WITH_ALIAS) { + val originalNameInOtherPackage = findUniqueDeclarationOrFail("originalNameInOtherPackage") + + val type = originalNameInOtherPackage.type + type.shouldBeInstanceOf() + type.declaration.shouldNotBeResolved() + } + } + + @Nested + inner class NamedType { + + @Test + fun `should resolve class in same file`() = withResource(NAMED_TYPE) { + val paramClassInSameFile = findUniqueDeclarationOrFail("paramClassInSameFile") + val classInSameFile = findUniqueDeclarationOrFail("ClassInSameFile") + + val parameterType = paramClassInSameFile.type + parameterType.shouldBeInstanceOf() + + val referencedClass = parameterType.declaration + referencedClass.shouldBeResolved() + referencedClass.shouldBe(classInSameFile) + } + + @Test + fun `should resolve enum in same file`() = withResource(NAMED_TYPE) { + val paramEnumInSameFile = findUniqueDeclarationOrFail("paramEnumInSameFile") + val enumInSameFile = findUniqueDeclarationOrFail("EnumInSameFile") + + val parameterType = paramEnumInSameFile.type + parameterType.shouldBeInstanceOf() + + val referencedEnum = parameterType.declaration + referencedEnum.shouldBeResolved() + referencedEnum.shouldBe(enumInSameFile) + } + + @Test + fun `should resolve class in same package`() = withResource(NAMED_TYPE) { + val paramClassInSamePackage = findUniqueDeclarationOrFail("paramClassInSamePackage") + + val parameterType = paramClassInSamePackage.type + parameterType.shouldBeInstanceOf() + + val referencedClass = parameterType.declaration + referencedClass.shouldBeResolved() + referencedClass.name.shouldBe("ClassInSamePackage") + } + + @Test + fun `should resolve enum in same package`() = withResource(NAMED_TYPE) { + val paramEnumInSamePackage = findUniqueDeclarationOrFail("paramEnumInSamePackage") + + val parameterType = paramEnumInSamePackage.type + parameterType.shouldBeInstanceOf() + + val referencedEnum = parameterType.declaration + referencedEnum.shouldBeResolved() + referencedEnum.name.shouldBe("EnumInSamePackage") + } + + @Test + fun `should resolve class in another package if imported`() = withResource(NAMED_TYPE) { + val paramClassInOtherPackage1 = findUniqueDeclarationOrFail("paramClassInOtherPackage1") + + val parameterType = paramClassInOtherPackage1.type + parameterType.shouldBeInstanceOf() + + val referencedClass = parameterType.declaration + referencedClass.shouldBeResolved() + referencedClass.name.shouldBe("ClassInOtherPackage1") + } + + @Test + fun `should resolve enum in another package if imported`() = withResource(NAMED_TYPE) { + val paramEnumInOtherPackage1 = findUniqueDeclarationOrFail("paramEnumInOtherPackage1") + + val parameterType = paramEnumInOtherPackage1.type + parameterType.shouldBeInstanceOf() + + val referencedEnum = parameterType.declaration + referencedEnum.shouldBeResolved() + referencedEnum.name.shouldBe("EnumInOtherPackage1") + } + + @Test + fun `should not resolve class in another package if not imported`() = withResource(NAMED_TYPE) { + val paramClassInOtherPackage2 = findUniqueDeclarationOrFail("paramClassInOtherPackage2") + + val parameterType = paramClassInOtherPackage2.type + parameterType.shouldBeInstanceOf() + + val referencedClass = parameterType.declaration + referencedClass.shouldNotBeResolved() + } + + @Test + fun `should not resolve enum in another package if not imported`() = withResource(NAMED_TYPE) { + val paramEnumInOtherPackage2 = findUniqueDeclarationOrFail("paramEnumInOtherPackage2") + + val parameterType = paramEnumInOtherPackage2.type + parameterType.shouldBeInstanceOf() + + val referencedEnum = parameterType.declaration + referencedEnum.shouldNotBeResolved() + } + + @Test + fun `should resolve type parameters in same function`() = withResource(NAMED_TYPE) { + val paramTypeParameterInSameFunction = + findUniqueDeclarationOrFail("paramTypeParameterInSameFunction") + val typeParameter = findUniqueDeclarationOrFail("TYPE_PARAMETER_IN_SAME_FUNCTION") + + val parameterType = paramTypeParameterInSameFunction.type + parameterType.shouldBeInstanceOf() + + val referencedTypeParameter = parameterType.declaration + referencedTypeParameter.shouldBeResolved() + referencedTypeParameter.shouldBe(typeParameter) + } + + @Test + fun `should not resolve type parameters in another declaration in same file`() = withResource(NAMED_TYPE) { + val paramTypeParameterInSameFile = findUniqueDeclarationOrFail("paramTypeParameterInSameFile") + + val parameterType = paramTypeParameterInSameFile.type + parameterType.shouldBeInstanceOf() + + val referencedInterface = parameterType.declaration + referencedInterface.shouldNotBeResolved() + } + + @Test + fun `should not resolve type parameters in another declaration in same package`() = withResource(NAMED_TYPE) { + val paramTypeParameterInSamePackage = + findUniqueDeclarationOrFail("paramTypeParameterInSamePackage") + + val parameterType = paramTypeParameterInSamePackage.type + parameterType.shouldBeInstanceOf() + + val referencedInterface = parameterType.declaration + referencedInterface.shouldNotBeResolved() + } + + @Test + fun `should not resolve type parameters in another declaration in another package`() = + withResource(NAMED_TYPE) { + val paramTypeParameterInOtherPackage = + findUniqueDeclarationOrFail("paramTypeParameterInOtherPackage") + + val parameterType = paramTypeParameterInOtherPackage.type + parameterType.shouldBeInstanceOf() + + val referencedInterface = parameterType.declaration + referencedInterface.shouldNotBeResolved() + } + + @Test + fun `should not resolve unknown declaration`() = withResource(NAMED_TYPE) { + val paramUnresolvedNamedTypeDeclaration = + findUniqueDeclarationOrFail("paramUnresolvedNamedTypeDeclaration") + + val parameterType = paramUnresolvedNamedTypeDeclaration.type + parameterType.shouldBeInstanceOf() + + val referencedInterface = parameterType.declaration + referencedInterface.shouldNotBeResolved() + } + + @Test + fun `should not resolve something that is not a named type declaration`() = withResource(NAMED_TYPE) { + val paramNotANamedTypeDeclaration = + findUniqueDeclarationOrFail("paramNotANamedTypeDeclaration") + + val parameterType = paramNotANamedTypeDeclaration.type + parameterType.shouldBeInstanceOf() + + val referencedInterface = parameterType.declaration + referencedInterface.shouldNotBeResolved() + } + + @Test + fun `should resolve type parameters in containing class from attribute`() = withResource(NAMED_TYPE) { + val attributeInClassWithTypeParameter = + findUniqueDeclarationOrFail("attributeInClassWithTypeParameter") + val typeParameter = findUniqueDeclarationOrFail("TYPE_PARAMETER_IN_OUTER_CLASS") + + val attributeType = attributeInClassWithTypeParameter.type + attributeType.shouldBeInstanceOf() + + val referencedTypeParameter = attributeType.declaration + referencedTypeParameter.shouldBeResolved() + referencedTypeParameter.shouldBe(typeParameter) + } + + @Test + fun `should resolve type parameters in containing class from nested class`() = withResource(NAMED_TYPE) { + val paramClassInClassWithTypeParameter = + findUniqueDeclarationOrFail("paramClassInClassWithTypeParameter") + val typeParameter = findUniqueDeclarationOrFail("TYPE_PARAMETER_IN_OUTER_CLASS") + + val parameterType = paramClassInClassWithTypeParameter.type + parameterType.shouldBeInstanceOf() + + val referencedTypeParameter = parameterType.declaration + referencedTypeParameter.shouldBeResolved() + referencedTypeParameter.shouldBe(typeParameter) + } + + @Test + fun `should resolve type parameters in containing class from nested enum variant`() = withResource(NAMED_TYPE) { + val paramEnumInClassWithTypeParameter = + findUniqueDeclarationOrFail("paramEnumInClassWithTypeParameter") + val typeParameter = findUniqueDeclarationOrFail("TYPE_PARAMETER_IN_OUTER_CLASS") + + val parameterType = paramEnumInClassWithTypeParameter.type + parameterType.shouldBeInstanceOf() + + val referencedTypeParameter = parameterType.declaration + referencedTypeParameter.shouldBeResolved() + referencedTypeParameter.shouldBe(typeParameter) + } + + @Test + fun `should resolve type parameters in containing class from method`() = withResource(NAMED_TYPE) { + val paramMethodInClassWithTypeParameter = + findUniqueDeclarationOrFail("paramMethodInClassWithTypeParameter") + val typeParameter = findUniqueDeclarationOrFail("TYPE_PARAMETER_IN_OUTER_CLASS") + + val parameterType = paramMethodInClassWithTypeParameter.type + parameterType.shouldBeInstanceOf() + + val referencedTypeParameter = parameterType.declaration + referencedTypeParameter.shouldBeResolved() + referencedTypeParameter.shouldBe(typeParameter) + } + + @Nested + inner class MemberType { + @Test + fun `should resolve class within class with qualified access`() = withResource(NAMED_TYPE) { + val paramClassInClassInSameFile = + findUniqueDeclarationOrFail("paramClassInClassInSameFile") + val classInSameFile = findUniqueDeclarationOrFail("ClassInClassInSameFile") + + val parameterType = paramClassInClassInSameFile.type + parameterType.shouldBeInstanceOf() + + val referencedClass = parameterType.member.declaration + referencedClass.shouldBeResolved() + referencedClass.shouldBe(classInSameFile) + } + + @Test + fun `should resolve enum within class with qualified access`() = withResource(NAMED_TYPE) { + val paramEnumInClassInSameFile = findUniqueDeclarationOrFail("paramEnumInClassInSameFile") + val enumInSameFile = findUniqueDeclarationOrFail("EnumInClassInSameFile") + + val parameterType = paramEnumInClassInSameFile.type + parameterType.shouldBeInstanceOf() + + val referencedEnum = parameterType.member.declaration + referencedEnum.shouldBeResolved() + referencedEnum.shouldBe(enumInSameFile) + } + + @Test + fun `should resolve enum variant with qualified access`() = withResource(NAMED_TYPE) { + val paramEnumVariantInSameFile = findUniqueDeclarationOrFail("paramEnumVariantInSameFile") + val enumVariantInSameFile = findUniqueDeclarationOrFail("EnumVariantInSameFile") + + val parameterType = paramEnumVariantInSameFile.type + parameterType.shouldBeInstanceOf() + + val referencedEnumVariant = parameterType.member.declaration + referencedEnumVariant.shouldBeResolved() + referencedEnumVariant.shouldBe(enumVariantInSameFile) + } + + @Test + fun `should not resolve class within class with unqualified access`() = withResource(NAMED_TYPE) { + val paramUnqualifiedClassInClassInSameFile = + findUniqueDeclarationOrFail("paramUnqualifiedClassInClassInSameFile") + + val parameterType = paramUnqualifiedClassInClassInSameFile.type + parameterType.shouldBeInstanceOf() + parameterType.declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve enum within class with unqualified access`() = withResource(NAMED_TYPE) { + val paramUnqualifiedEnumInClassInSameFile = + findUniqueDeclarationOrFail("paramUnqualifiedEnumInClassInSameFile") + + val parameterType = paramUnqualifiedEnumInClassInSameFile.type + parameterType.shouldBeInstanceOf() + parameterType.declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve enum variant with unqualified access`() = withResource(NAMED_TYPE) { + val paramUnqualifiedEnumVariantInSameFile = + findUniqueDeclarationOrFail("paramUnqualifiedEnumVariantInSameFile") + + val parameterType = paramUnqualifiedEnumVariantInSameFile.type + parameterType.shouldBeInstanceOf() + parameterType.declaration.shouldNotBeResolved() + } + + @Test + fun `should resolve inherited class within class with qualified access`() = withResource(NAMED_TYPE) { + val paramClassInSuperClass = findUniqueDeclarationOrFail("paramClassInSuperClass") + val classInSameFile = findUniqueDeclarationOrFail("ClassInSuperClass") + + val parameterType = paramClassInSuperClass.type + parameterType.shouldBeInstanceOf() + + val referencedClass = parameterType.member.declaration + referencedClass.shouldBeResolved() + referencedClass.shouldBe(classInSameFile) + } + + @Test + fun `should resolve inherited enum within class with qualified access`() = withResource(NAMED_TYPE) { + val paramEnumInSuperClass = findUniqueDeclarationOrFail("paramEnumInSuperClass") + val enumInSameFile = findUniqueDeclarationOrFail("EnumInSuperClass") + + val parameterType = paramEnumInSuperClass.type + parameterType.shouldBeInstanceOf() + + val referencedEnum = parameterType.member.declaration + referencedEnum.shouldBeResolved() + referencedEnum.shouldBe(enumInSameFile) + } + } + } + + @Nested + inner class ProtocolReference { + + @Test + fun `should resolve static attribute in super class`() = withResource(PROTOCOL_REFERENCE) { + val superClassStaticAttributeReference = + findUniqueDeclarationOrFail("superClassStaticAttributeReference") + + val superClassStaticAttribute = findUniqueDeclarationOrFail("superClassStaticAttribute") + + val term = superClassStaticAttributeReference.term + term.shouldBeInstanceOf() + term.token.shouldBeResolved() + term.token shouldBe superClassStaticAttribute + } + + @Test + fun `should resolve instance attribute in super class`() = withResource(PROTOCOL_REFERENCE) { + val superClassInstanceAttributeReference = + findUniqueDeclarationOrFail("superClassInstanceAttributeReference") + + val superClassInstanceAttribute = findUniqueDeclarationOrFail("superClassInstanceAttribute") + + val term = superClassInstanceAttributeReference.term + term.shouldBeInstanceOf() + term.token.shouldBeResolved() + term.token shouldBe superClassInstanceAttribute + } + + @Test + fun `should resolve static method in super class`() = withResource(PROTOCOL_REFERENCE) { + val superClassStaticMethodReference = + findUniqueDeclarationOrFail("superClassStaticMethodReference") + + val superClassStaticMethod = findUniqueDeclarationOrFail("superClassStaticMethod") + + val term = superClassStaticMethodReference.term + term.shouldBeInstanceOf() + term.token.shouldBeResolved() + term.token shouldBe superClassStaticMethod + } + + @Test + fun `should resolve instance method in super class`() = withResource(PROTOCOL_REFERENCE) { + val superClassInstanceMethodReference = + findUniqueDeclarationOrFail("superClassInstanceMethodReference") + + val superClassInstanceMethod = findUniqueDeclarationOrFail("superClassInstanceMethod") + + val term = superClassInstanceMethodReference.term + term.shouldBeInstanceOf() + term.token.shouldBeResolved() + term.token shouldBe superClassInstanceMethod + } + + @Test + fun `should not resolve static attribute in container class`() = withResource(PROTOCOL_REFERENCE) { + val containerClassStaticAttributeReference = + findUniqueDeclarationOrFail("containerClassStaticAttributeReference") + + val term = containerClassStaticAttributeReference.term + term.shouldBeInstanceOf() + term.token.shouldNotBeResolved() + } + + @Test + fun `should not resolve instance attribute in container class`() = withResource(PROTOCOL_REFERENCE) { + val containerClassInstanceAttributeReference = + findUniqueDeclarationOrFail("containerClassInstanceAttributeReference") + + val term = containerClassInstanceAttributeReference.term + term.shouldBeInstanceOf() + term.token.shouldNotBeResolved() + } + + @Test + fun `should not resolve static method in container class`() = withResource(PROTOCOL_REFERENCE) { + val containerClassStaticMethodReference = + findUniqueDeclarationOrFail("containerClassStaticMethodReference") + + val term = containerClassStaticMethodReference.term + term.shouldBeInstanceOf() + term.token.shouldNotBeResolved() + } + + @Test + fun `should not resolve instance method in container class`() = withResource(PROTOCOL_REFERENCE) { + val containerClassInstanceMethodReference = + findUniqueDeclarationOrFail("containerClassInstanceMethodReference") + + val term = containerClassInstanceMethodReference.term + term.shouldBeInstanceOf() + term.token.shouldNotBeResolved() + } + + @Test + fun `should resolve static attribute in own class`() = withResource(PROTOCOL_REFERENCE) { + val subClassStaticAttributeReference = + findUniqueDeclarationOrFail("subClassStaticAttributeReference") + + val subClassStaticAttribute = findUniqueDeclarationOrFail("subClassStaticAttribute") + + val term = subClassStaticAttributeReference.term + term.shouldBeInstanceOf() + term.token.shouldBeResolved() + term.token shouldBe subClassStaticAttribute + } + + @Test + fun `should resolve instance attribute in own class`() = withResource(PROTOCOL_REFERENCE) { + val subClassInstanceAttributeReference = + findUniqueDeclarationOrFail("subClassInstanceAttributeReference") + + val subClassInstanceAttribute = findUniqueDeclarationOrFail("subClassInstanceAttribute") + + val term = subClassInstanceAttributeReference.term + term.shouldBeInstanceOf() + term.token.shouldBeResolved() + term.token shouldBe subClassInstanceAttribute + } + + @Test + fun `should resolve static method in own class`() = withResource(PROTOCOL_REFERENCE) { + val subClassStaticMethodReference = + findUniqueDeclarationOrFail("subClassStaticMethodReference") + + val subClassStaticMethod = findUniqueDeclarationOrFail("subClassStaticMethod") + + val term = subClassStaticMethodReference.term + term.shouldBeInstanceOf() + term.token.shouldBeResolved() + term.token shouldBe subClassStaticMethod + } + + @Test + fun `should resolve instance method in own class`() = withResource(PROTOCOL_REFERENCE) { + val subClassInstanceMethodReference = + findUniqueDeclarationOrFail("subClassInstanceMethodReference") + + val subClassInstanceMethod = findUniqueDeclarationOrFail("subClassInstanceMethod") + + val term = subClassInstanceMethodReference.term + term.shouldBeInstanceOf() + term.token.shouldBeResolved() + term.token shouldBe subClassInstanceMethod + } + + @Test + fun `should resolve overriding declaration`() = withResource(PROTOCOL_REFERENCE) { + val overriddenReference = findUniqueDeclarationOrFail("overriddenReference") + + val subClass = findUniqueDeclarationOrFail("SubClass") + val overridden = subClass.findUniqueDeclarationOrFail("overridden") + + val term = overriddenReference.term + term.shouldBeInstanceOf() + term.token.shouldBeResolved() + term.token shouldBe overridden + } + + @Test + fun `should resolve other subterms`() = withResource(PROTOCOL_REFERENCE) { + val subtermReference = findUniqueDeclarationOrFail("subtermReference") + val forwardReference = findUniqueDeclarationOrFail("forwardReference") + + val term = subtermReference.term + term.shouldBeInstanceOf() + term.token.shouldBeResolved() + term.token shouldBe forwardReference + } + + @Test + fun `should resolve shadowing subterm`() = withResource(PROTOCOL_REFERENCE) { + val shadowedReference = findUniqueDeclarationOrFail("shadowedReference") + val shadowed = findUniqueDeclarationOrFail("shadowed") + + val term = shadowedReference.term + term.shouldBeInstanceOf() + term.token.shouldBeResolved() + term.token shouldBe shadowed + } + + @Test + fun `should not resolve forward reference to subterm`() = withResource(PROTOCOL_REFERENCE) { + val forwardReference = findUniqueDeclarationOrFail("forwardReference") + + val term = forwardReference.term + term.shouldBeInstanceOf() + term.token.shouldNotBeResolved() + } + + @Test + fun `should not resolve unknown declaration`() = withResource(PROTOCOL_REFERENCE) { + val unresolvedReference = findUniqueDeclarationOrFail("unresolvedReference") + + val term = unresolvedReference.term + term.shouldBeInstanceOf() + term.token.shouldNotBeResolved() + } + + @Test + fun `should not resolve something that is not a protocol token`() = withResource(PROTOCOL_REFERENCE) { + val notAProtocolTokenReference = + findUniqueDeclarationOrFail("notAProtocolTokenReference") + + val term = notAProtocolTokenReference.term + term.shouldBeInstanceOf() + term.token.shouldNotBeResolved() + } + } + + @Nested + inner class Reference { + + @Test + fun `should not resolve annotation in same file`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToAnnotations") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + references[0].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve annotation in same package`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToAnnotations") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + references[1].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve annotation in another package if imported`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToAnnotations") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + references[2].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve annotation in another package if not imported`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToAnnotations") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + references[3].declaration.shouldNotBeResolved() + } + + @Test + fun `should resolve class in same file`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToClasses") + val classInSameFile = findUniqueDeclarationOrFail("ClassInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + + val declaration = references[0].declaration + declaration.shouldBeResolved() + declaration.shouldBe(classInSameFile) + } + + @Test + fun `should resolve class in same package`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToClasses") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.name.shouldBe("ClassInSamePackage") + } + + @Test + fun `should resolve class in another package if imported`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToClasses") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + + val declaration = references[2].declaration + declaration.shouldBeResolved() + declaration.name.shouldBe("ClassInOtherPackage1") + } + + @Test + fun `should not resolve class in another package if not imported`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToClasses") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + references[3].declaration.shouldNotBeResolved() + } + + @Test + fun `should resolve enum in same file`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToEnums") + val enumInSameFile = findUniqueDeclarationOrFail("EnumInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + + val declaration = references[0].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumInSameFile) + } + + @Test + fun `should resolve enum in same package`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToEnums") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.name.shouldBe("EnumInSamePackage") + } + + @Test + fun `should resolve enum in another package if imported`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToEnums") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + + val declaration = references[2].declaration + declaration.shouldBeResolved() + declaration.name.shouldBe("EnumInOtherPackage1") + } + + @Test + fun `should not resolve enum in another package if not imported`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToEnums") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + references[3].declaration.shouldNotBeResolved() + } + + @Test + fun `should resolve global function in same file`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToGlobalFunctions") + val globalFunctionInSameFile = findUniqueDeclarationOrFail("globalFunctionInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + + val declaration = references[0].declaration + declaration.shouldBeResolved() + declaration.shouldBe(globalFunctionInSameFile) + } + + @Test + fun `should resolve global function in same package`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToGlobalFunctions") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.name.shouldBe("globalFunctionInSamePackage") + } + + @Test + fun `should resolve global function in another package if imported`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToGlobalFunctions") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + + val declaration = references[2].declaration + declaration.shouldBeResolved() + declaration.name.shouldBe("globalFunctionInOtherPackage1") + } + + @Test + fun `should not resolve global function in another package if not imported`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToGlobalFunctions") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + references[3].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve lambda result`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToLambdaResults") + + val reference = step.descendants().firstOrNull() + reference.shouldNotBeNull() + reference.declaration.shouldNotBeResolved() + } + + @Test + fun `should resolve parameter of step in same step`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToParameters") + val parameterInStep = step.findUniqueDeclarationOrFail("parameterInStep") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[0].declaration + declaration.shouldBeResolved() + declaration.shouldBe(parameterInStep) + } + + @Test + fun `should resolve parameter of step in block lambda in same step`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToParameters") + val parameterInStep = step.findUniqueDeclarationOrFail("parameterInStep") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(parameterInStep) + } + + @Test + fun `should resolve parameter of block lambda in same block lambda`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToParameters") + val parameterInLambda = step.findUniqueDeclarationOrFail("parameterInBlockLambda") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[2].declaration + declaration.shouldBeResolved() + declaration.shouldBe(parameterInLambda) + } + + @Test + fun `should resolve parameter of step in block lambda within block lambda in same step`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToParameters") + val parameterInStep = step.findUniqueDeclarationOrFail("parameterInStep") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(parameterInStep) + } + + @Test + fun `should resolve parameter of block lambda in nested block lambda`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToParameters") + val parameterInLambda = step.findUniqueDeclarationOrFail("parameterInBlockLambda") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[4].declaration + declaration.shouldBeResolved() + declaration.shouldBe(parameterInLambda) + } + + @Test + fun `should resolve parameter of expression lambda in same expression lambda`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToParameters") + val parameterInLambda = step.findUniqueDeclarationOrFail("parameterInExpressionLambda") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(parameterInLambda) + } + + @Test + fun `should resolve placeholder of step in same step`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToPlaceholders") + val placeholderInStep = step.findUniqueDeclarationOrFail("placeholderInStep") + + val references = step.descendants().toList() + references.shouldHaveSize(5) + + val declaration = references[0].declaration + declaration.shouldBeResolved() + declaration.shouldBe(placeholderInStep) + } + + @Test + fun `should resolve placeholder of step in lambda in same step`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToPlaceholders") + val placeholderInStep = step.findUniqueDeclarationOrFail("placeholderInStep") + + val references = step.descendants().toList() + references.shouldHaveSize(5) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(placeholderInStep) + } + + @Test + fun `should resolve placeholder of lambda in same lambda`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToPlaceholders") + val placeholderInLambda = step.findUniqueDeclarationOrFail("placeholderInLambda") + + val references = step.descendants().toList() + references.shouldHaveSize(5) + + val declaration = references[2].declaration + declaration.shouldBeResolved() + declaration.shouldBe(placeholderInLambda) + } + + @Test + fun `should resolve placeholder of step in lambda within lambda in same step`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToPlaceholders") + val placeholderInStep = step.findUniqueDeclarationOrFail("placeholderInStep") + + val references = step.descendants().toList() + references.shouldHaveSize(5) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(placeholderInStep) + } + + @Test + fun `should resolve placeholder of lambda in nested lambda`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToPlaceholders") + val placeholderInLambda = step.findUniqueDeclarationOrFail("placeholderInLambda") + + val references = step.descendants().toList() + references.shouldHaveSize(5) + + val declaration = references[4].declaration + declaration.shouldBeResolved() + declaration.shouldBe(placeholderInLambda) + } + + @Test + fun `should not resolve type parameters`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToTypeParameters") + + val reference = step.descendants().firstOrNull() + reference.shouldNotBeNull() + reference.declaration.shouldNotBeResolved() + } + + @Test + fun `should resolve step in same file`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToSteps") + val stepInSameFile = findUniqueDeclarationOrFail("stepInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(10) + + val declaration = references[0].declaration + declaration.shouldBeResolved() + declaration.shouldBe(stepInSameFile) + } + + @Test + fun `should resolve step in same package`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToSteps") + + val references = step.descendants().toList() + references.shouldHaveSize(10) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.name.shouldBe("stepInSamePackage") + } + + @Test + fun `should resolve step in another package if imported`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToSteps") + + val references = step.descendants().toList() + references.shouldHaveSize(10) + + val declaration = references[2].declaration + declaration.shouldBeResolved() + declaration.name.shouldBe("stepInOtherPackage1") + } + + @Test + fun `should not resolve step in another package if not imported`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToSteps") + + val references = step.descendants().toList() + references.shouldHaveSize(10) + references[3].declaration.shouldNotBeResolved() + } + + @Test + fun `should resolve internal step in same file`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToSteps") + val stepInSameFile = findUniqueDeclarationOrFail("internalStepInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(10) + + val declaration = references[4].declaration + declaration.shouldBeResolved() + declaration.shouldBe(stepInSameFile) + } + + @Test + fun `should resolve private step in same file`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToSteps") + val stepInSameFile = findUniqueDeclarationOrFail("privateStepInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(10) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(stepInSameFile) + } + + @Test + fun `should resolve internal step in same package`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToSteps") + + val references = step.descendants().toList() + references.shouldHaveSize(10) + + val declaration = references[6].declaration + declaration.shouldBeResolved() + declaration.name.shouldBe("internalStepInSamePackage") + } + + @Test + fun `should not resolve private step in same package`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToSteps") + + val references = step.descendants().toList() + references.shouldHaveSize(10) + references[7].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve internal step in another package`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToSteps") + + val references = step.descendants().toList() + references.shouldHaveSize(10) + references[8].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve private step in another package`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToSteps") + + val references = step.descendants().toList() + references.shouldHaveSize(10) + references[9].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve workflow in same file`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToWorkflows") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + references[0].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve workflow in same package`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToWorkflows") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + references[1].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve workflow in another package if imported`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToWorkflows") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + references[2].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve workflow in another package if not imported`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("directReferencesToWorkflows") + + val references = step.descendants().toList() + references.shouldHaveSize(4) + references[3].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve placeholder declared later in same step`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("forwardReferences") + + val references = step.descendants().toList() + references.shouldHaveSize(3) + references[0].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve placeholder declared later from nested lambda`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("forwardReferences") + + val references = step.descendants().toList() + references.shouldHaveSize(3) + references[1].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve placeholder that lambda is assigned to from body of lambda`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("forwardReferences") + + val references = step.descendants().toList() + references.shouldHaveSize(3) + references[2].declaration.shouldNotBeResolved() + } + + @Test + fun `should resolve declaration shadowed by parameter of step`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("shadowedReferences") + + val parameters = step.parametersOrEmpty() + parameters.shouldHaveSize(1) + + val references = step.descendants().toList() + references.shouldHaveSize(4) + + val declaration = references[0].declaration + declaration.shouldBeResolved() + declaration.shouldBe(parameters[0]) + } + + @Test + fun `should resolve declaration shadowed by placeholder of step`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("shadowedReferences") + + val placeholders = step.descendants().toList() + placeholders.shouldHaveSize(3) + + val references = step.descendants().toList() + references.shouldHaveSize(4) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(placeholders[0]) + } + + @Test + fun `should resolve declaration shadowed by parameter of lambda`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("shadowedReferences") + + val parameters = step.body.descendants().toList() + parameters.shouldHaveSize(1) + + val references = step.descendants().toList() + references.shouldHaveSize(4) + + val declaration = references[2].declaration + declaration.shouldBeResolved() + declaration.shouldBe(parameters[0]) + } + + @Test + fun `should resolve declaration shadowed by placeholder of lambda`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("shadowedReferences") + + val placeholders = step.descendants().toList() + placeholders.shouldHaveSize(3) + + val references = step.descendants().toList() + references.shouldHaveSize(4) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(placeholders[2]) + } + + @Test + fun `should not resolve function locals`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToFunctionLocals") + + val references = step.descendants().toList() + references.shouldHaveSize(2) + references.forEachAsClue { + it.declaration.shouldNotBeResolved() + } + } + + @Test + fun `should not resolve lambda locals`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToLambdaLocals") + + val references = step.descendants().toList() + references.shouldHaveSize(3) + references[0].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve step locals`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToStepLocals") + + val references = step.descendants().toList() + references.shouldHaveSize(3) + references.forEachAsClue { + it.declaration.shouldNotBeResolved() + } + } + + @Test + fun `should not resolve unknown declaration`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("unresolvedReferences") + + val references = step.descendants().toList() + references.shouldHaveSize(1) + references[0].declaration.shouldNotBeResolved() + } + + @Nested + inner class MemberAccess { + + @Test + fun `should resolve static class attribute accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToClassMembers") + val classStaticAttributeInSameFile = + findUniqueDeclarationOrFail("classStaticAttributeInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(classStaticAttributeInSameFile) + } + + @Test + fun `should resolve instance class attribute accessed from class instance`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToClassMembers") + val classInstanceAttributeInSameFile = + findUniqueDeclarationOrFail("classInstanceAttributeInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(classInstanceAttributeInSameFile) + } + + @Test + fun `should resolve nested class accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToClassMembers") + val classInClassInSameFile = + findUniqueDeclarationOrFail("ClassInClassInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(classInClassInSameFile) + } + + @Test + fun `should resolve nested enum accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToClassMembers") + val enumInClassInSameFile = + findUniqueDeclarationOrFail("EnumInClassInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[7].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumInClassInSameFile) + } + + @Test + fun `should resolve static class method accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToClassMembers") + val classStaticMethodInSameFile = + findUniqueDeclarationOrFail("classStaticMethodInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[9].declaration + declaration.shouldBeResolved() + declaration.shouldBe(classStaticMethodInSameFile) + } + + @Test + fun `should resolve instance class method accessed from class instance`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToClassMembers") + val classInstanceMethodInSameFile = + findUniqueDeclarationOrFail("classInstanceMethodInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[11].declaration + declaration.shouldBeResolved() + declaration.shouldBe(classInstanceMethodInSameFile) + } + + @Test + fun `should resolve enum variants`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToEnumVariants") + val enumVariantInSameFile = + findUniqueDeclarationOrFail("EnumVariantInSameFile") + + val references = step.body.descendants().toList() + references.shouldHaveSize(2) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumVariantInSameFile) + } + + @Test + fun `should resolve enum variants from step annotation`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToEnumVariants") + val enumVariantInSameFile = + findUniqueDeclarationOrFail("EnumVariantInSameFile") + + val annotations = step.annotationCallsOrEmpty() + annotations.shouldHaveSize(1) + + val references = annotations[0].descendants().toList() + references.shouldHaveSize(2) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumVariantInSameFile) + } + + @Test + fun `should resolve enum variants from parameter annotation`() = withResource(REFERENCE) { + val parameter = + findUniqueDeclarationOrFail("referenceToEnumVariantFromParameterAnnotation") + val enumVariantInSameFile = + findUniqueDeclarationOrFail("EnumVariantInSameFile") + + val annotations = parameter.annotationCallsOrEmpty() + annotations.shouldHaveSize(1) + + val references = annotations[0].descendants().toList() + references.shouldHaveSize(2) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumVariantInSameFile) + } + + @Test + fun `should resolve enum variants of nested enum from class annotation`() = withResource(REFERENCE) { + val parameter = findUniqueDeclarationOrFail("ReferencesToEnumVariantsInnerClass") + val enumVariantInSameFile = + findUniqueDeclarationOrFail("EnumVariantInSameClass") + + val annotations = parameter.annotationCallsOrEmpty() + annotations.shouldHaveSize(2) + + val references = annotations[0].descendants().toList() + references.shouldHaveSize(2) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumVariantInSameFile) + } + + @Test + fun `should resolve enum variants of global enum from class annotation`() = withResource(REFERENCE) { + val parameter = findUniqueDeclarationOrFail("ReferencesToEnumVariantsInnerClass") + val enumVariantInSameClass = + findUniqueDeclarationOrFail("EnumVariantInSameFile") + + val annotations = parameter.annotationCallsOrEmpty() + annotations.shouldHaveSize(2) + + val references = annotations[1].descendants().toList() + references.shouldHaveSize(2) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumVariantInSameClass) + } + + @Test + fun `should resolve parameters of enum variants`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToEnumVariantParameters") + val enumVariantParameterInSameFile = + findUniqueDeclarationOrFail("enumVariantParameterInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(3) + + val declaration = references[2].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumVariantParameterInSameFile) + } + + @Test + fun `should resolve inherited static class attribute accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") + val superClassStaticAttribute = + findUniqueDeclarationOrFail("superClassStaticAttribute") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(superClassStaticAttribute) + } + + @Test + fun `should resolve inherited instance class attribute accessed from class instance`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") + val superClassInstanceAttribute = + findUniqueDeclarationOrFail("superClassInstanceAttribute") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(superClassInstanceAttribute) + } + + @Test + fun `should resolve inherited nested class accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") + val classInSuperClass = + findUniqueDeclarationOrFail("ClassInSuperClass") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(classInSuperClass) + } + + @Test + fun `should resolve inherited nested enum accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") + val enumInSuperClass = + findUniqueDeclarationOrFail("EnumInSuperClass") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[7].declaration + declaration.shouldBeResolved() + declaration.shouldBe(enumInSuperClass) + } + + @Test + fun `should resolve inherited static class method accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") + val superClassStaticMethod = + findUniqueDeclarationOrFail("superClassStaticMethod") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[9].declaration + declaration.shouldBeResolved() + declaration.shouldBe(superClassStaticMethod) + } + + @Test + fun `should resolve inherited instance class method accessed from class instance`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToInheritedClassMembers") + val superClassInstanceMethod = + findUniqueDeclarationOrFail("superClassInstanceMethod") + + val references = step.descendants().toList() + references.shouldHaveSize(12) + + val declaration = references[11].declaration + declaration.shouldBeResolved() + declaration.shouldBe(superClassInstanceMethod) + } + + @Test + fun `should resolve overridden instance attribute`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToOverriddenMembers") + val subClassForOverriding = findUniqueDeclarationOrFail("SubClassForOverriding") + val instanceAttributeForOverriding = + subClassForOverriding.findUniqueDeclarationOrFail("instanceAttributeForOverriding") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(instanceAttributeForOverriding) + } + + @Test + fun `should resolve overridden instance method`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToOverriddenMembers") + val subClassForOverriding = findUniqueDeclarationOrFail("SubClassForOverriding") + val instanceMethodForOverriding = + subClassForOverriding.findUniqueDeclarationOrFail("instanceMethodForOverriding") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[7].declaration + declaration.shouldBeResolved() + declaration.shouldBe(instanceMethodForOverriding) + } + + @Test + fun `should resolve hidden static attribute`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") + val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") + val staticAttributeForHiding = + subClassForHiding.findUniqueDeclarationOrFail("staticAttributeForHiding") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(staticAttributeForHiding) + } + + @Test + fun `should resolve hidden nested class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") + val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") + val nestedClassForHiding = + subClassForHiding.findUniqueDeclarationOrFail("NestedClassForHiding") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(nestedClassForHiding) + } + + @Test + fun `should resolve hidden nested enum`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") + val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") + val nestedEnumForHiding = + subClassForHiding.findUniqueDeclarationOrFail("NestedEnumForHiding") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(nestedEnumForHiding) + } + + @Test + fun `should resolve hidden static method`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToHiddenMembers") + val subClassForHiding = findUniqueDeclarationOrFail("SubClassForHiding") + val staticMethodForHiding = + subClassForHiding.findUniqueDeclarationOrFail("staticMethodForHiding") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[7].declaration + declaration.shouldBeResolved() + declaration.shouldBe(staticMethodForHiding) + } + + @Test + fun `should not resolve static class members accessed from instance`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToStaticClassMembersFromInstance") + val classInSameFile = findUniqueDeclarationOrFail("ClassInSameFile") + + val references = step.descendants() + .filter { it.declaration != classInSameFile } + .toList() + references.shouldHaveSize(8) + references.forEachAsClue { + it.declaration.shouldNotBeResolved() + } + } + + @Test + fun `should not resolve instance class members accessed from class`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToInstanceClassMembersFromClass") + val classInSameFile = findUniqueDeclarationOrFail("ClassInSameFile") + + val references = step.descendants() + .filter { it.declaration != classInSameFile } + .toList() + references.shouldHaveSize(4) + references.forEachAsClue { + it.declaration.shouldNotBeResolved() + } + } + + @Test + fun `should not resolve class members with unqualified access`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("unqualifiedReferencesToClassMembers") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + references.forEachAsClue { + it.declaration.shouldNotBeResolved() + } + } + + @Test + fun `should not resolve enum variants with unqualified access`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("unqualifiedReferencesToEnumVariants") + + val references = step.descendants().toList() + references.shouldHaveSize(1) + references[0].declaration.shouldNotBeResolved() + } + + @Test + fun `should not resolve parameters of enum variants with unqualified access`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("unqualifiedReferencesToEnumVariantParameters") + + val references = step.descendants().toList() + references.shouldHaveSize(1) + references[0].declaration.shouldNotBeResolved() + } + + @Test + fun `should resolve result of callable type with one result without matching member`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") + val singleResult = step.findUniqueDeclarationOrFail("singleResult") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(singleResult) + } + + @Test + fun `should resolve attribute for callable type with one result with matching class attribute`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") + val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") + val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result) + } + + @Test + fun `should resolve result for callable type with one result with matching enum variant`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") + val callableWithOneResultWithIdenticalEnumVariant = + step.findUniqueDeclarationOrFail("callableWithOneResultWithIdenticalEnumVariant") + val result = + callableWithOneResultWithIdenticalEnumVariant.findUniqueDeclarationOrFail("result") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result) + } + + @Test + fun `should resolve result of callable type with multiple results`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToCallableTypeResults") + val result1 = step.findUniqueDeclarationOrFail("result1") + + val references = step.descendants().toList() + references.shouldHaveSize(8) + + val declaration = references[7].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result1) + } + + @Test + fun `should resolve result of function with one result without matching member`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToFunctionResults") + val globalFunctionResultInSameFile = + findUniqueDeclarationOrFail("globalFunctionResultInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(globalFunctionResultInSameFile) + } + + @Test + fun `should resolve member for function with one result with matching member`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToFunctionResults") + val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") + val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result) + } + + @Test + fun `should resolve result of function with multiple results`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToFunctionResults") + val globalFunctionWithTwoResults = + findUniqueDeclarationOrFail("globalFunctionWithTwoResults") + val result1 = globalFunctionWithTwoResults.findUniqueDeclarationOrFail("result1") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result1) + } + + @Test + fun `should resolve result of lambda with one result without matching member`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToLambdaResults") + val singleResult = step.findUniqueDeclarationOrFail("singleResult") + + val references = step.descendants().toList() + references.shouldHaveSize(7) + + val declaration = references[2].declaration + declaration.shouldBeResolved() + declaration.shouldBe(singleResult) + } + + @Test + fun `should resolve member for lambda with one result with matching member`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToLambdaResults") + val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") + val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") + + val references = step.descendants().toList() + references.shouldHaveSize(7) + + val declaration = references[4].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result) + } + + @Test + fun `should resolve result of lambda with multiple results`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToLambdaResults") + val result1 = step.findUniqueDeclarationOrFail("result1") + + val references = step.descendants().toList() + references.shouldHaveSize(7) + + val declaration = references[6].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result1) + } + + @Test + fun `should resolve result of step with one result without matching member`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToStepResults") + val stepResultInSameFile = findUniqueDeclarationOrFail("stepResultInSameFile") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[1].declaration + declaration.shouldBeResolved() + declaration.shouldBe(stepResultInSameFile) + } + + @Test + fun `should resolve member for step with one result with matching member`() = + withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToStepResults") + val classForResultMemberAccess = findUniqueDeclarationOrFail("ClassForResultMemberAccess") + val result = classForResultMemberAccess.findUniqueDeclarationOrFail("result") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[3].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result) + } + + @Test + fun `should resolve result of step with multiple results`() = withResource(REFERENCE) { + val step = findUniqueDeclarationOrFail("referencesToStepResults") + val stepInSameFileWithTwoResults = + findUniqueDeclarationOrFail("stepWithTwoResults") + val result1 = stepInSameFileWithTwoResults.findUniqueDeclarationOrFail("result1") + + val references = step.descendants().toList() + references.shouldHaveSize(6) + + val declaration = references[5].declaration + declaration.shouldBeResolved() + declaration.shouldBe(result1) + } + } + } + + @Nested + inner class TypeArgument { + + @Test + fun `should resolve type parameter in used class in same file`() = + withResource(TYPE_ARGUMENT) { + val typeArguments = this.descendants().toList() + typeArguments.shouldHaveSize(11) + + val typeParameterInSameFile = + findUniqueDeclarationOrFail("TYPE_PARAMETER_IN_CLASS_IN_SAME_FILE") + + val referencedTypeParameter = typeArguments[0].typeParameter + referencedTypeParameter.shouldBeResolved() + referencedTypeParameter.shouldBe(typeParameterInSameFile) + } + + @Test + fun `should resolve type parameter in used enum variant in same file`() = + withResource(TYPE_ARGUMENT) { + val typeArguments = this.descendants().toList() + typeArguments.shouldHaveSize(11) + + val typeParameterInSameFile = + findUniqueDeclarationOrFail("TYPE_PARAMETER_IN_ENUM_VARIANT_IN_SAME_FILE") + + val referencedTypeParameter = typeArguments[1].typeParameter + referencedTypeParameter.shouldBeResolved() + referencedTypeParameter.shouldBe(typeParameterInSameFile) + } + + @Test + fun `should resolve type parameter in used function in same file`() = + withResource(TYPE_ARGUMENT) { + val typeArguments = this.descendants().toList() + typeArguments.shouldHaveSize(11) + + val typeParameterInSameFile = + findUniqueDeclarationOrFail("TYPE_PARAMETER_IN_FUNCTION_IN_SAME_FILE") + + val referencedTypeParameter = typeArguments[2].typeParameter + referencedTypeParameter.shouldBeResolved() + referencedTypeParameter.shouldBe(typeParameterInSameFile) + } + + @Test + fun `should resolve type parameter in used declaration in same package`() = + withResource(TYPE_ARGUMENT) { + val typeArguments = this.descendants().toList() + typeArguments.shouldHaveSize(11) + + val referencedTypeParameter = typeArguments[3].typeParameter + referencedTypeParameter.shouldBeResolved() + referencedTypeParameter.name.shouldBe("TYPE_PARAMETER_IN_SAME_PACKAGE") + } + + @Test + fun `should resolve type parameter in used declaration that is imported and in another package`() = + withResource(TYPE_ARGUMENT) { + val typeArguments = this.descendants().toList() + typeArguments.shouldHaveSize(11) + + val referencedTypeParameter = typeArguments[4].typeParameter + referencedTypeParameter.shouldBeResolved() + referencedTypeParameter.name.shouldBe("TYPE_PARAMETER_IN_OTHER_PACKAGE1") + } + + @Test + fun `should not resolve type parameter in used declaration that is not imported and in another package`() = + withResource(TYPE_ARGUMENT) { + val typeArguments = this.descendants().toList() + typeArguments.shouldHaveSize(11) + typeArguments[5].typeParameter.shouldNotBeResolved() + } + + @Test + fun `should not resolve type parameter in declaration other than used one in same package`() = + withResource(TYPE_ARGUMENT) { + val typeArguments = this.descendants().toList() + typeArguments.shouldHaveSize(11) + typeArguments[6].typeParameter.shouldNotBeResolved() + } + + @Test + fun `should not resolve type parameter in declaration other than used one that is imported and in another package`() = + withResource(TYPE_ARGUMENT) { + val typeArguments = this.descendants().toList() + typeArguments.shouldHaveSize(11) + typeArguments[7].typeParameter.shouldNotBeResolved() + } + + @Test + fun `should not resolve type parameter in declaration other than used one that is not imported and in another package`() = + withResource(TYPE_ARGUMENT) { + val typeArguments = this.descendants().toList() + typeArguments.shouldHaveSize(11) + typeArguments[8].typeParameter.shouldNotBeResolved() + } + + @Test + fun `should not resolve unknown declaration`() = withResource(TYPE_ARGUMENT) { + val typeArguments = this.descendants().toList() + typeArguments.shouldHaveSize(11) + typeArguments[9].typeParameter.shouldNotBeResolved() + } + + @Test + fun `should not resolve something that is not a type parameter`() = withResource(TYPE_ARGUMENT) { + val typeArguments = this.descendants().toList() + typeArguments.shouldHaveSize(11) + typeArguments[10].typeParameter.shouldNotBeResolved() + } + } + + @Nested + inner class TypeParameterConstraint { + + @Test + fun `should resolve type parameter in same class`() = withResource(TYPE_PARAMETER_CONSTRAINT) { + val testClass = findUniqueDeclarationOrFail("TestClass") + val typeParameterConstraints = testClass.descendants().toList() + typeParameterConstraints.shouldHaveSize(1) + + val typeParameterInSameDeclaration = + findUniqueDeclarationOrFail("TYPE_PARAMETER_IN_SAME_CLASS") + + val referencedTypeParameter = typeParameterConstraints[0].leftOperand + referencedTypeParameter.shouldBeResolved() + referencedTypeParameter.shouldBe(typeParameterInSameDeclaration) + } + + @Test + fun `should resolve type parameter in same enum variant`() = withResource(TYPE_PARAMETER_CONSTRAINT) { + val testEnumVariant = findUniqueDeclarationOrFail("TestEnumVariant") + val typeParameterConstraints = testEnumVariant.descendants().toList() + typeParameterConstraints.shouldHaveSize(1) + + val typeParameterInSameDeclaration = + findUniqueDeclarationOrFail("TYPE_PARAMETER_IN_SAME_ENUM_VARIANT") + + val referencedTypeParameter = typeParameterConstraints[0].leftOperand + referencedTypeParameter.shouldBeResolved() + referencedTypeParameter.shouldBe(typeParameterInSameDeclaration) + } + + @Test + fun `should resolve type parameter in same function`() = withResource(TYPE_PARAMETER_CONSTRAINT) { + val testFunction = findUniqueDeclarationOrFail("testFunction") + val typeParameterConstraints = testFunction.descendants().toList() + typeParameterConstraints.shouldHaveSize(7) + + val typeParameterInSameDeclaration = + findUniqueDeclarationOrFail("TYPE_PARAMETER_IN_SAME_FUNCTION") + + val referencedTypeParameter = typeParameterConstraints[0].leftOperand + referencedTypeParameter.shouldBeResolved() + referencedTypeParameter.shouldBe(typeParameterInSameDeclaration) + } + + @Test + fun `should not resolve type parameter in another declaration in same file`() = withResource( + TYPE_PARAMETER_CONSTRAINT + ) { + val testFunction = findUniqueDeclarationOrFail("testFunction") + val typeParameterConstraints = testFunction.descendants().toList() + typeParameterConstraints.shouldHaveSize(7) + typeParameterConstraints[1].leftOperand.shouldNotBeResolved() + } + + @Test + fun `should not resolve type parameter in another declaration in same package`() = + withResource(TYPE_PARAMETER_CONSTRAINT) { + val testFunction = findUniqueDeclarationOrFail("testFunction") + val typeParameterConstraints = testFunction.descendants().toList() + typeParameterConstraints.shouldHaveSize(7) + typeParameterConstraints[2].leftOperand.shouldNotBeResolved() + } + + @Test + fun `should not resolve type parameter in another declaration that is imported and in another package`() = + withResource(TYPE_PARAMETER_CONSTRAINT) { + val testFunction = findUniqueDeclarationOrFail("testFunction") + val typeParameterConstraints = testFunction.descendants().toList() + typeParameterConstraints.shouldHaveSize(7) + typeParameterConstraints[3].leftOperand.shouldNotBeResolved() + } + + @Test + fun `should not resolve type parameter in another declaration that is not imported and in another package`() = + withResource(TYPE_PARAMETER_CONSTRAINT) { + val testFunction = findUniqueDeclarationOrFail("testFunction") + val typeParameterConstraints = testFunction.descendants().toList() + typeParameterConstraints.shouldHaveSize(7) + typeParameterConstraints[4].leftOperand.shouldNotBeResolved() + } + + @Test + fun `should not resolve unknown declaration`() = withResource(TYPE_PARAMETER_CONSTRAINT) { + val testFunction = findUniqueDeclarationOrFail("testFunction") + val typeParameterConstraints = testFunction.descendants().toList() + typeParameterConstraints.shouldHaveSize(7) + typeParameterConstraints[5].leftOperand.shouldNotBeResolved() + } + + @Test + fun `should not something that is not a type parameter`() = withResource(TYPE_PARAMETER_CONSTRAINT) { + val testFunction = findUniqueDeclarationOrFail("testFunction") + val typeParameterConstraints = testFunction.descendants().toList() + typeParameterConstraints.shouldHaveSize(7) + typeParameterConstraints[6].leftOperand.shouldNotBeResolved() + } + } + + @Nested + inner class Yield { + + @Test + fun `should resolve result in same step`() = withResource(YIELD) { + val yields = this.descendants().toList() + yields.shouldHaveSize(7) + + val resultsInSameFunction = findUniqueDeclarationOrFail("resultInSameStep") + + val referencedResult = yields[0].result + referencedResult.shouldBeResolved() + referencedResult.shouldBe(resultsInSameFunction) + } + + @Test + fun `should not resolve result in another step in same file`() = withResource(YIELD) { + val yields = this.descendants().toList() + yields.shouldHaveSize(7) + yields[1].result.shouldNotBeResolved() + } + + @Test + fun `should not resolve result in another step in same package`() = withResource(YIELD) { + val yields = this.descendants().toList() + yields.shouldHaveSize(7) + yields[2].result.shouldNotBeResolved() + } + + @Test + fun `should not resolve result in another step that is imported and in another package`() = + withResource(YIELD) { + val yields = this.descendants().toList() + yields.shouldHaveSize(7) + yields[3].result.shouldNotBeResolved() + } + + @Test + fun `should not resolve result in another step that is not imported and in another package`() = + withResource(YIELD) { + val yields = this.descendants().toList() + yields.shouldHaveSize(7) + yields[4].result.shouldNotBeResolved() + } + + @Test + fun `should not resolve unknown declaration`() = withResource(YIELD) { + val yields = this.descendants().toList() + yields.shouldHaveSize(7) + yields[5].result.shouldNotBeResolved() + } + + @Test + fun `should not something that is not a result`() = withResource(YIELD) { + val yields = this.descendants().toList() + yields.shouldHaveSize(7) + yields[6].result.shouldNotBeResolved() + } + } + + private fun withResource( + resourceName: ResourceName, + lambda: SmlCompilationUnit.() -> Unit + ) { + + val compilationUnit = + parseHelper.parseResource( + "scoping/$resourceName/main.${SmlFileExtension.Test}", + listOf( + "scoping/$resourceName/externalsInOtherPackage.${SmlFileExtension.Test}", + "scoping/$resourceName/externalsInSamePackage.${SmlFileExtension.Test}", + ) + ) ?: throw IllegalArgumentException("File is not a compilation unit.") + + compilationUnit.apply(lambda) + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/serializer/SerializerExtensionsTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/serializer/SerializerExtensionsTest.kt new file mode 100644 index 000000000..0a3467910 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/serializer/SerializerExtensionsTest.kt @@ -0,0 +1,113 @@ +@file:Suppress("ClassName") + +package de.unibonn.simpleml.serializer + +import com.google.inject.Inject +import de.unibonn.simpleml.emf.createSmlCompilationUnit +import de.unibonn.simpleml.simpleML.SimpleMLPackage +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.assertions.findUniqueDeclarationOrFail +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldNotContain +import io.kotest.matchers.types.shouldBeInstanceOf +import org.eclipse.xtext.resource.XtextResource +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class SerializerExtensionsTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + private val factory = SimpleMLPackage.eINSTANCE.simpleMLFactory + + @Nested + inner class serializeToFormattedString { + + @Test + fun `should serialize and format a complete EMF model created from a resource`() { + val compilationUnit = + parseHelper.parseResource("serialization/extensionsTest.smltest") + compilationUnit.shouldNotBeNull() + + val result = compilationUnit.serializeToFormattedString() + result.shouldBeInstanceOf() + result.code.shouldBe( + """ + |package tests + | + |class MyClass { + | attr myAttribute: Int + |} + """.trimMargin() + ) + } + + @Test + fun `should serialize and format a subtree of the EMF model from a resource`() { + val compilationUnit = + parseHelper.parseResource("serialization/extensionsTest.smltest") + compilationUnit.shouldNotBeNull() + + val `class` = compilationUnit.findUniqueDeclarationOrFail("MyClass") + + val result = `class`.serializeToFormattedString() + result.shouldBeInstanceOf() + result.code.shouldBe( + """class MyClass { + | attr myAttribute: Int + |} + """.trimMargin() + ) + } + + @Test + fun `should use line feed as line separator`() { + val compilationUnit = + parseHelper.parseResource("serialization/extensionsTest.smltest") + compilationUnit.shouldNotBeNull() + + val `class` = compilationUnit.findUniqueDeclarationOrFail("MyClass") + + val result = `class`.serializeToFormattedString() + result.shouldBeInstanceOf() + result.code.count { it == '\n' } shouldBe 2 + + if (System.lineSeparator() != "\n") { + result.code.shouldNotContain(System.lineSeparator()) + } + } + + @Test + fun `should not serialize EObjects without Resource`() { + val compilationUnit = createSmlCompilationUnit(packageName = "test") + + val result = compilationUnit.serializeToFormattedString() + result.shouldBeInstanceOf() + } + + @Test + fun `should not serialize wrong EMF models`() { + val compilationUnit = factory.createSmlCompilationUnit().apply { + // Missing SmlAnnotationCallHolder + members += factory.createSmlClass().apply { + name = "tests" + } + } + + val dummyResource = XtextResource() + dummyResource.contents += compilationUnit + + val result = compilationUnit.serializeToFormattedString() + result.shouldBeInstanceOf() + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/RecursionTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/RecursionTest.kt new file mode 100644 index 000000000..18e770478 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/RecursionTest.kt @@ -0,0 +1,75 @@ +package de.unibonn.simpleml.staticAnalysis + +import com.google.inject.Inject +import de.unibonn.simpleml.emf.annotationCallsOrEmpty +import de.unibonn.simpleml.emf.descendants +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.assertions.findUniqueDeclarationOrFail +import io.kotest.assertions.asClue +import io.kotest.matchers.booleans.shouldNotBeTrue +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import org.eclipse.xtext.nodemodel.util.NodeModelUtils +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class RecursionTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + private lateinit var compilationUnit: SmlCompilationUnit + + @BeforeAll + fun reset() { + compilationUnit = parseHelper + .parseResource("staticAnalysis/recursion.smltest") + .shouldNotBeNull() + } + + @ParameterizedTest(name = "isRecursive should return {1} for all calls in {0}") + @MethodSource("stepSource") + fun `should mark exactly calls that trigger recursion as recursive`( + stepName: String, + callsShouldBeRecursive: Boolean + ) { + val step = compilationUnit + .findUniqueDeclarationOrFail(stepName) + .shouldNotBeNull() + + step.descendants().toList().forEach { call -> + NodeModelUtils.getNode(call).text.trim().asClue { + call.isRecursive() shouldBe callsShouldBeRecursive + } + } + } + + private fun stepSource(): List { + return compilationUnit.descendants() + .mapNotNull { step -> + val callsShouldBeRecursive = step.annotationCallsOrEmpty().any { + it.annotation.name == "CallsShouldBeRecursive" + } + val callsShouldNotBeRecursive = step.annotationCallsOrEmpty().any { + it.annotation.name == "CallsShouldNotBeRecursive" + } + (callsShouldBeRecursive && callsShouldNotBeRecursive).shouldNotBeTrue() + + Arguments.of(step.name, callsShouldBeRecursive) + } + .toList() + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/SideEffectsTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/SideEffectsTest.kt new file mode 100644 index 000000000..2863f039b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/SideEffectsTest.kt @@ -0,0 +1,75 @@ +package de.unibonn.simpleml.staticAnalysis + +import com.google.inject.Inject +import de.unibonn.simpleml.emf.annotationCallsOrEmpty +import de.unibonn.simpleml.emf.descendants +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.assertions.findUniqueDeclarationOrFail +import io.kotest.assertions.asClue +import io.kotest.matchers.booleans.shouldNotBeTrue +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import org.eclipse.xtext.nodemodel.util.NodeModelUtils +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class SideEffectsTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + private lateinit var compilationUnit: SmlCompilationUnit + + @BeforeAll + fun reset() { + compilationUnit = parseHelper + .parseResource("staticAnalysis/sideEffects.smltest") + .shouldNotBeNull() + } + + @ParameterizedTest(name = "isPureExpression should return {1} for all calls in {0}") + @MethodSource("stepSource") + fun `should mark exactly calls that trigger recursion as recursive`( + stepName: String, + callsShouldBePure: Boolean + ) { + val step = compilationUnit + .findUniqueDeclarationOrFail(stepName) + .shouldNotBeNull() + + step.descendants().toList().forEach { call -> + NodeModelUtils.getNode(call).text.trim().asClue { + call.expressionHasNoSideEffects() shouldBe callsShouldBePure + } + } + } + + private fun stepSource(): List { + return compilationUnit.descendants() + .mapNotNull { step -> + val shouldHaveNoSideEffects = step.annotationCallsOrEmpty().any { + it.annotation.name == "ShouldHaveNoSideEffects" + } + val shouldHaveSideEffects = step.annotationCallsOrEmpty().any { + it.annotation.name == "ShouldHaveSideEffects" + } + (shouldHaveNoSideEffects && shouldHaveSideEffects).shouldNotBeTrue() + + Arguments.of(step.name, shouldHaveNoSideEffects) + } + .toList() + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/linking/ArgumentToParameterTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/linking/ArgumentToParameterTest.kt new file mode 100644 index 000000000..38368fc73 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/linking/ArgumentToParameterTest.kt @@ -0,0 +1,188 @@ +package de.unibonn.simpleml.staticAnalysis.linking + +import de.unibonn.simpleml.emf.createSmlAnnotation +import de.unibonn.simpleml.emf.createSmlAnnotationCall +import de.unibonn.simpleml.emf.createSmlArgument +import de.unibonn.simpleml.emf.createSmlArgumentList +import de.unibonn.simpleml.emf.createSmlCall +import de.unibonn.simpleml.emf.createSmlFunction +import de.unibonn.simpleml.emf.createSmlNull +import de.unibonn.simpleml.emf.createSmlParameter +import de.unibonn.simpleml.emf.createSmlParameterList +import de.unibonn.simpleml.emf.createSmlReference +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.simpleML.SmlAnnotationCall +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlParameter +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +class ArgumentToParameterTest { + private lateinit var normalParameter: SmlParameter + private lateinit var variadicParameter: SmlParameter + + private lateinit var positionalArgument: SmlArgument + private lateinit var namedArgument: SmlArgument + + private lateinit var function: SmlFunction + private lateinit var call: SmlCall + + private lateinit var annotation: SmlAnnotation + private lateinit var annotationCall: SmlAnnotationCall + + @BeforeEach + fun reset() { + normalParameter = createSmlParameter(name = "normalParameter") + variadicParameter = createSmlParameter( + name = "variadicParameter", + isVariadic = true + ) + + positionalArgument = createSmlArgument( + value = createSmlNull() + ) + namedArgument = createSmlArgument( + value = createSmlNull(), + parameter = normalParameter, + ) + + function = createSmlFunction(name = "f") + call = createSmlCall( + createSmlReference(function) + ) + + annotation = createSmlAnnotation(name = "A") + annotationCall = createSmlAnnotationCall(annotation) + } + + @Nested + inner class ParameterOrNull { + + @Test + fun `should resolve argument (positional, function call, valid index)`() { + function.parameterList = createSmlParameterList(listOf(normalParameter)) + call.argumentList = createSmlArgumentList(listOf(positionalArgument)) + + positionalArgument.parameterOrNull() shouldBe normalParameter + } + + @Test + fun `should resolve argument (named, function call, valid index)`() { + function.parameterList = createSmlParameterList(listOf(normalParameter)) + call.argumentList = createSmlArgumentList(listOf(namedArgument)) + + namedArgument.parameterOrNull() shouldBe normalParameter + } + + @Test + fun `should resolve argument (positional, annotation call, valid index)`() { + annotation.parameterList = createSmlParameterList(listOf(normalParameter)) + annotationCall.argumentList = createSmlArgumentList(listOf(positionalArgument)) + + positionalArgument.parameterOrNull() shouldBe normalParameter + } + + @Test + fun `should resolve argument (named, annotation call, valid index)`() { + annotation.parameterList = createSmlParameterList(listOf(normalParameter)) + annotationCall.argumentList = createSmlArgumentList(listOf(namedArgument)) + + namedArgument.parameterOrNull() shouldBe normalParameter + } + + @Test + fun `should resolve arguments (function call, variadic)`() { + namedArgument.parameter = variadicParameter + function.parameterList = createSmlParameterList(listOf(variadicParameter)) + call.argumentList = createSmlArgumentList(listOf(positionalArgument, namedArgument)) + + namedArgument.parameterOrNull() shouldBe variadicParameter + positionalArgument.parameterOrNull() shouldBe variadicParameter + } + + @Test + fun `should resolve arguments (annotation call, variadic)`() { + namedArgument.parameter = variadicParameter + annotation.parameterList = createSmlParameterList(listOf(variadicParameter)) + annotationCall.argumentList = createSmlArgumentList(listOf(positionalArgument, namedArgument)) + + namedArgument.parameterOrNull() shouldBe variadicParameter + positionalArgument.parameterOrNull() shouldBe variadicParameter + } + + @Test + fun `should return null if named arguments precede positional arguments (function call)`() { + function.parameterList = createSmlParameterList(listOf(normalParameter)) + call.argumentList = createSmlArgumentList(listOf(namedArgument, positionalArgument)) + + positionalArgument.parameterOrNull().shouldBeNull() + } + + @Test + fun `should return null if named arguments precede positional arguments (annotation call)`() { + annotation.parameterList = createSmlParameterList(listOf(normalParameter)) + annotationCall.argumentList = createSmlArgumentList(listOf(namedArgument, positionalArgument)) + + positionalArgument.parameterOrNull().shouldBeNull() + } + + @Test + fun `should return null if no matching parameter exists (function call)`() { + call.argumentList = createSmlArgumentList(listOf(positionalArgument)) + + positionalArgument.parameterOrNull().shouldBeNull() + } + + @Test + fun `should return null if no matching parameter exists (annotation call)`() { + annotationCall.argumentList = createSmlArgumentList(listOf(positionalArgument)) + + positionalArgument.parameterOrNull().shouldBeNull() + } + + @Test + fun `should return null if argument list cannot be matched to parameter list`() { + createSmlArgumentList(listOf(positionalArgument)) + + positionalArgument.parameterOrNull().shouldBeNull() + } + + @Test + fun `should return null for positional arguments that are not in an argument list`() { + positionalArgument.parameterOrNull().shouldBeNull() + } + } + + @Nested + inner class ParametersOrNull { + + @Test + fun `should resolve argument list of function call`() { + function.parameterList = createSmlParameterList(listOf(normalParameter, variadicParameter)) + call.argumentList = createSmlArgumentList(emptyList()) + + call.argumentList.parametersOrNull().shouldContainExactly(normalParameter, variadicParameter) + } + + @Test + fun `should resolve argument list of annotation call`() { + annotation.parameterList = createSmlParameterList(listOf(normalParameter, variadicParameter)) + annotationCall.argumentList = createSmlArgumentList(emptyList()) + + annotationCall.argumentList.parametersOrNull().shouldContainExactly(normalParameter, variadicParameter) + } + + @Test + fun `should return null if the argument list cannot be resolved`() { + val argumentList = createSmlArgumentList(emptyList()) + + argumentList.parametersOrNull().shouldBeNull() + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/linking/ResultToYieldTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/linking/ResultToYieldTest.kt new file mode 100644 index 000000000..2959d7573 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/linking/ResultToYieldTest.kt @@ -0,0 +1,102 @@ +package de.unibonn.simpleml.staticAnalysis.linking + +import de.unibonn.simpleml.emf.createSmlAssignment +import de.unibonn.simpleml.emf.createSmlNull +import de.unibonn.simpleml.emf.createSmlResult +import de.unibonn.simpleml.emf.createSmlResultList +import de.unibonn.simpleml.emf.createSmlStep +import de.unibonn.simpleml.emf.createSmlWildcard +import de.unibonn.simpleml.emf.createSmlYield +import de.unibonn.simpleml.simpleML.SmlAssignment +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlYield +import io.kotest.matchers.collections.shouldBeEmpty +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +class ResultToYieldTest { + private lateinit var firstResult: SmlResult + private lateinit var secondResult: SmlResult + + private lateinit var yieldOfFirstResult1: SmlYield + private lateinit var yieldOfFirstResult2: SmlYield + private lateinit var yieldOfSecondResult: SmlYield + + private lateinit var assignment: SmlAssignment + private lateinit var step: SmlStep + + @BeforeEach + fun reset() { + firstResult = createSmlResult(name = "firstResult") + secondResult = createSmlResult(name = "secondResult") + + yieldOfFirstResult1 = createSmlYield(firstResult) + yieldOfFirstResult2 = createSmlYield(firstResult) + yieldOfSecondResult = createSmlYield(secondResult) + + assignment = createSmlAssignment( + assignees = listOf(createSmlWildcard()), + expression = createSmlNull() + ) + step = createSmlStep( + name = "s", + statements = listOf(assignment) + ) + } + + @Nested + inner class UniqueYieldOrNull { + + @Test + fun `should return the unique corresponding yield`() { + step.resultList = createSmlResultList(listOf(firstResult)) + assignment.assigneeList.assignees += yieldOfFirstResult1 + + firstResult.uniqueYieldOrNull() shouldBe yieldOfFirstResult1 + } + + @Test + fun `should return null if no corresponding yields exist`() { + step.resultList = createSmlResultList(listOf(firstResult)) + + firstResult.uniqueYieldOrNull().shouldBeNull() + } + + @Test + fun `should return null if multiple corresponding yields exist`() { + step.resultList = createSmlResultList(listOf(firstResult)) + assignment.assigneeList.assignees += listOf(yieldOfFirstResult1, yieldOfFirstResult2) + + firstResult.uniqueYieldOrNull().shouldBeNull() + } + } + + @Nested + inner class YieldsOrEmpty { + + @Test + fun `should return all corresponding yields in the body of the step`() { + step.resultList = createSmlResultList(listOf(firstResult, secondResult)) + assignment.assigneeList.assignees += listOf(yieldOfFirstResult1, yieldOfFirstResult2, yieldOfSecondResult) + + firstResult.yieldsOrEmpty().shouldContainExactly(yieldOfFirstResult1, yieldOfFirstResult2) + } + + @Test + fun `should return an empty list if the result is not in a result list`() { + firstResult.yieldsOrEmpty().shouldBeEmpty() + } + + @Test + fun `should return an empty list if the result is not in a step`() { + createSmlResultList(listOf(firstResult)) + + firstResult.yieldsOrEmpty().shouldBeEmpty() + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/linking/TypeArgumentToTypeParameterTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/linking/TypeArgumentToTypeParameterTest.kt new file mode 100644 index 000000000..c8bee4e96 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/linking/TypeArgumentToTypeParameterTest.kt @@ -0,0 +1,163 @@ +package de.unibonn.simpleml.staticAnalysis.linking + +import de.unibonn.simpleml.emf.createSmlCall +import de.unibonn.simpleml.emf.createSmlClass +import de.unibonn.simpleml.emf.createSmlFunction +import de.unibonn.simpleml.emf.createSmlNamedType +import de.unibonn.simpleml.emf.createSmlReference +import de.unibonn.simpleml.emf.createSmlStarProjection +import de.unibonn.simpleml.emf.createSmlTypeArgument +import de.unibonn.simpleml.emf.createSmlTypeArgumentList +import de.unibonn.simpleml.emf.createSmlTypeParameter +import de.unibonn.simpleml.emf.createSmlTypeParameterList +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlNamedType +import de.unibonn.simpleml.simpleML.SmlTypeArgument +import de.unibonn.simpleml.simpleML.SmlTypeParameter +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +class TypeArgumentToTypeParameterTest { + private lateinit var typeParameter: SmlTypeParameter + + private lateinit var positionalTypeArgument: SmlTypeArgument + private lateinit var namedTypeArgument: SmlTypeArgument + + private lateinit var function: SmlFunction + private lateinit var call: SmlCall + + private lateinit var `class`: SmlClass + private lateinit var namedType: SmlNamedType + + @BeforeEach + fun reset() { + typeParameter = createSmlTypeParameter(name = "T") + + positionalTypeArgument = createSmlTypeArgument( + value = createSmlStarProjection() + ) + namedTypeArgument = createSmlTypeArgument( + value = createSmlStarProjection(), + typeParameter = typeParameter, + ) + + function = createSmlFunction(name = "f") + call = createSmlCall( + createSmlReference(function) + ) + + `class` = createSmlClass(name = "C") + namedType = createSmlNamedType(`class`) + } + + @Nested + inner class TypeParameterOrNull { + + @Test + fun `should resolve type argument (positional, call, valid index)`() { + function.typeParameterList = createSmlTypeParameterList(listOf(typeParameter)) + call.typeArgumentList = createSmlTypeArgumentList(listOf(positionalTypeArgument)) + + positionalTypeArgument.typeParameterOrNull() shouldBe typeParameter + } + + @Test + fun `should resolve type argument (named, call, valid index)`() { + function.typeParameterList = createSmlTypeParameterList(listOf(typeParameter)) + call.typeArgumentList = createSmlTypeArgumentList(listOf(namedTypeArgument)) + + namedTypeArgument.typeParameterOrNull() shouldBe typeParameter + } + + @Test + fun `should resolve argument (positional, named type, valid index)`() { + `class`.typeParameterList = createSmlTypeParameterList(listOf(typeParameter)) + namedType.typeArgumentList = createSmlTypeArgumentList(listOf(positionalTypeArgument)) + + positionalTypeArgument.typeParameterOrNull() shouldBe typeParameter + } + + @Test + fun `should resolve argument (named, named type, valid index)`() { + `class`.typeParameterList = createSmlTypeParameterList(listOf(typeParameter)) + namedType.typeArgumentList = createSmlTypeArgumentList(listOf(namedTypeArgument)) + + namedTypeArgument.typeParameterOrNull() shouldBe typeParameter + } + + @Test + fun `should return null if named type arguments precede positional type argument (call)`() { + function.typeParameterList = createSmlTypeParameterList(listOf(typeParameter)) + call.typeArgumentList = createSmlTypeArgumentList(listOf(namedTypeArgument, positionalTypeArgument)) + + positionalTypeArgument.typeParameterOrNull().shouldBeNull() + } + + @Test + fun `should return null if named type arguments precede positional type argument (named type)`() { + `class`.typeParameterList = createSmlTypeParameterList(listOf(typeParameter)) + namedType.typeArgumentList = createSmlTypeArgumentList(listOf(namedTypeArgument, positionalTypeArgument)) + + positionalTypeArgument.typeParameterOrNull().shouldBeNull() + } + + @Test + fun `should return null if no matching type parameter exists (call)`() { + call.typeArgumentList = createSmlTypeArgumentList(listOf(positionalTypeArgument)) + + positionalTypeArgument.typeParameterOrNull().shouldBeNull() + } + + @Test + fun `should return null if no matching type parameter exists (named type)`() { + namedType.typeArgumentList = createSmlTypeArgumentList(listOf(positionalTypeArgument)) + + positionalTypeArgument.typeParameterOrNull().shouldBeNull() + } + + @Test + fun `should return null if type argument list cannot be matched to type parameter list`() { + createSmlTypeArgumentList(listOf(positionalTypeArgument)) + + positionalTypeArgument.typeParameterOrNull().shouldBeNull() + } + + @Test + fun `should return null for positional type arguments that are not in a type argument list`() { + positionalTypeArgument.typeParameterOrNull().shouldBeNull() + } + } + + @Nested + inner class TypeParametersOrNull { + + @Test + fun `should resolve type argument list of call`() { + function.typeParameterList = createSmlTypeParameterList(listOf(typeParameter)) + call.typeArgumentList = createSmlTypeArgumentList(listOf(positionalTypeArgument)) + + call.typeArgumentList.typeParametersOrNull().shouldContainExactly(typeParameter) + } + + @Test + fun `should resolve type argument list of annotation call`() { + `class`.typeParameterList = createSmlTypeParameterList(listOf(typeParameter)) + namedType.typeArgumentList = createSmlTypeArgumentList(listOf(positionalTypeArgument)) + + namedType.typeArgumentList.typeParametersOrNull().shouldContainExactly(typeParameter) + } + + @Test + fun `should return null if the type argument list cannot be resolved`() { + val typeArgumentList = createSmlTypeArgumentList(listOf(positionalTypeArgument)) + + typeArgumentList.typeParametersOrNull().shouldBeNull() + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/partialEvaluation/ToConstantExpressionTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/partialEvaluation/ToConstantExpressionTest.kt new file mode 100644 index 000000000..4c7fa1eb4 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/partialEvaluation/ToConstantExpressionTest.kt @@ -0,0 +1,1506 @@ +package de.unibonn.simpleml.staticAnalysis.partialEvaluation + +import com.google.inject.Inject +import de.unibonn.simpleml.constant.SmlInfixOperationOperator +import de.unibonn.simpleml.constant.SmlPrefixOperationOperator +import de.unibonn.simpleml.emf.createSmlAnnotation +import de.unibonn.simpleml.emf.createSmlArgument +import de.unibonn.simpleml.emf.createSmlAssignment +import de.unibonn.simpleml.emf.createSmlAttribute +import de.unibonn.simpleml.emf.createSmlBlockLambda +import de.unibonn.simpleml.emf.createSmlBoolean +import de.unibonn.simpleml.emf.createSmlCall +import de.unibonn.simpleml.emf.createSmlEnum +import de.unibonn.simpleml.emf.createSmlEnumVariant +import de.unibonn.simpleml.emf.createSmlExpressionLambda +import de.unibonn.simpleml.emf.createSmlFloat +import de.unibonn.simpleml.emf.createSmlInfixOperation +import de.unibonn.simpleml.emf.createSmlInt +import de.unibonn.simpleml.emf.createSmlMemberAccess +import de.unibonn.simpleml.emf.createSmlNull +import de.unibonn.simpleml.emf.createSmlParameter +import de.unibonn.simpleml.emf.createSmlParenthesizedExpression +import de.unibonn.simpleml.emf.createSmlPlaceholder +import de.unibonn.simpleml.emf.createSmlPrefixOperation +import de.unibonn.simpleml.emf.createSmlReference +import de.unibonn.simpleml.emf.createSmlStep +import de.unibonn.simpleml.emf.createSmlString +import de.unibonn.simpleml.emf.createSmlTemplateString +import de.unibonn.simpleml.emf.descendants +import de.unibonn.simpleml.emf.statementsOrEmpty +import de.unibonn.simpleml.simpleML.SimpleMLFactory +import de.unibonn.simpleml.simpleML.SmlAbstractExpression +import de.unibonn.simpleml.simpleML.SmlBlockLambda +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlExpressionLambda +import de.unibonn.simpleml.simpleML.SmlExpressionStatement +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlWorkflow +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.assertions.findUniqueDeclarationOrFail +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.CsvSource + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class ToConstantExpressionTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + private val factory = SimpleMLFactory.eINSTANCE + + private lateinit var impureBlockLambda: SmlBlockLambda + private lateinit var pureBlockLambda: SmlBlockLambda + private lateinit var recursiveBlockLambda: SmlBlockLambda + private lateinit var impureExpressionLambda: SmlExpressionLambda + private lateinit var pureExpressionLambda: SmlExpressionLambda + private lateinit var recursiveExpressionLambda: SmlExpressionLambda + private lateinit var impureStep: SmlStep + private lateinit var pureStep: SmlStep + private lateinit var recursiveStep: SmlStep + + @BeforeEach + fun reset() { + val compilationUnit = parseHelper.parseResource("partialEvaluation/callables.smltest") + compilationUnit.shouldNotBeNull() + + val blockLambdas = compilationUnit.descendants().toList() + blockLambdas.shouldHaveSize(3) + + impureBlockLambda = blockLambdas[0] + pureBlockLambda = blockLambdas[1] + recursiveBlockLambda = blockLambdas[2] + + val expressionLambdas = compilationUnit.descendants().toList() + expressionLambdas.shouldHaveSize(3) + + impureExpressionLambda = expressionLambdas[0] + pureExpressionLambda = expressionLambdas[1] + recursiveExpressionLambda = expressionLambdas[2] + + impureStep = compilationUnit.findUniqueDeclarationOrFail("impureStep") + pureStep = compilationUnit.findUniqueDeclarationOrFail("pureStep") + recursiveStep = compilationUnit.findUniqueDeclarationOrFail("recursiveStep") + } + + @Nested + inner class BaseCases { + + @Test + fun `should return value of boolean literal`() { + val testData = createSmlBoolean(true) + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(true) + } + + @Test + fun `should return value of float literal`() { + val testData = createSmlFloat(1.0) + testData.toConstantExpressionOrNull() shouldBe SmlConstantFloat(1.0) + } + + @Test + fun `should return value of int literal`() { + val testData = createSmlInt(1) + testData.toConstantExpressionOrNull() shouldBe SmlConstantInt(1) + } + + @Test + fun `should return value of null literal`() { + val testData = createSmlNull() + testData.toConstantExpressionOrNull() shouldBe SmlConstantNull + } + + @Test + fun `should return value of for string literal`() { + val testData = createSmlString("test") + testData.toConstantExpressionOrNull() shouldBe SmlConstantString("test") + } + + @Test + fun `should return value of template string start`() { + val testData = factory.createSmlTemplateStringStart().apply { value = "test" } + testData.toConstantExpressionOrNull() shouldBe SmlConstantString("test") + } + + @Test + fun `should return value of template string inner`() { + val testData = factory.createSmlTemplateStringInner().apply { value = "test" } + testData.toConstantExpressionOrNull() shouldBe SmlConstantString("test") + } + + @Test + fun `should return value of template string end`() { + val testData = factory.createSmlTemplateStringEnd().apply { value = "test" } + testData.toConstantExpressionOrNull() shouldBe SmlConstantString("test") + } + + @Test + fun `toConstantExpression should return null for block lambda`() { + val testData = createSmlBlockLambda() + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `simplify should return null for impure block lambda`() { + impureBlockLambda.simplify(emptyMap()).shouldBeNull() + } + + @Test + fun `simplify should return intermediate block lambda for pure block lambda`() { + pureBlockLambda.simplify(emptyMap()).shouldBeInstanceOf() + } + + @Test + fun `simplify should return null for block lambda with recursive call`() { + recursiveBlockLambda.simplify(emptyMap()).shouldBeNull() + } + + @Test + fun `toConstantExpression should return null for expression lambda`() { + val testData = createSmlExpressionLambda(result = createSmlNull()) + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `simplify should return null for impure expression lambda`() { + impureExpressionLambda.simplify(emptyMap()).shouldBeNull() + } + + @Test + fun `simplify should return intermediate expression lambda for pure expression lambda`() { + pureExpressionLambda.simplify(emptyMap()).shouldBeInstanceOf() + } + + @Test + fun `simplify should return null for expression lambda with recursive call`() { + recursiveExpressionLambda.simplify(emptyMap()).shouldBeNull() + } + } + + @Nested + inner class Argument { + + @Test + fun `should return value as constant expression for arguments`() { + val testData = createSmlArgument(value = createSmlNull()) + testData.toConstantExpressionOrNull() shouldBe SmlConstantNull + } + } + + @Nested + inner class InfixOperation { + + @Nested + inner class Or { + + @ParameterizedTest + @CsvSource( + delimiter = '|', + textBlock = """ + false | false | false + false | true | true + true | false | true + true | true | true""" + ) + fun `should return if left or right operand is true`( + leftOperand: Boolean, + rightOperand: Boolean, + expected: Boolean + ) { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(leftOperand), + operator = SmlInfixOperationOperator.Or, + rightOperand = createSmlBoolean(rightOperand) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(expected) + } + + @Test + fun `should return null if the left operand is not a constant boolean`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlNull(), + operator = SmlInfixOperationOperator.Or, + rightOperand = createSmlBoolean(true) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant boolean`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.Or, + rightOperand = createSmlNull() + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class And { + + @ParameterizedTest + @CsvSource( + delimiter = '|', + textBlock = """ + false | false | false + false | true | false + true | false | false + true | true | true""" + ) + fun `should return if left and right operand is true`( + leftOperand: Boolean, + rightOperand: Boolean, + expected: Boolean + ) { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(leftOperand), + operator = SmlInfixOperationOperator.And, + rightOperand = createSmlBoolean(rightOperand) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(expected) + } + + @Test + fun `should return null if the left operand is not a constant boolean`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlNull(), + operator = SmlInfixOperationOperator.And, + rightOperand = createSmlBoolean(true) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant boolean`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.And, + rightOperand = createSmlNull() + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class Equals { + + @Test + fun `should return true boolean literal if left and right operands are equal`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.Equals, + rightOperand = createSmlBoolean(true) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(true) + } + + @Test + fun `should return false boolean literal if left and right operands are not equal`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.Equals, + rightOperand = createSmlInt(1) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(false) + } + + @Test + fun `should return null if the left operand is not a constant expression`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlCall( + receiver = createSmlNull() + ), + operator = SmlInfixOperationOperator.Equals, + rightOperand = createSmlBoolean(true) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant expression`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.Equals, + rightOperand = createSmlCall( + receiver = createSmlNull() + ) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class NotEquals { + + @Test + fun `should return true boolean literal if left and right operands are not equal`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.NotEquals, + rightOperand = createSmlInt(1) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(true) + } + + @Test + fun `should return false boolean literal if left and right operands are equal`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.NotEquals, + rightOperand = createSmlBoolean(true) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(false) + } + + @Test + fun `should return null if the left operand is not a constant expression`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlCall( + receiver = createSmlNull() + ), + operator = SmlInfixOperationOperator.NotEquals, + rightOperand = createSmlBoolean(true) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant expression`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.NotEquals, + rightOperand = createSmlCall( + receiver = createSmlNull() + ) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class IdenticalTo { + + @Test + fun `should return true boolean literal if left and right operands are identical`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.IdenticalTo, + rightOperand = createSmlBoolean(true) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(true) + } + + @Test + fun `should return false boolean literal if left and right operands are not identical`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.IdenticalTo, + rightOperand = createSmlInt(1) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(false) + } + + @Test + fun `should return null if the left operand is not a constant expression`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlCall( + receiver = createSmlNull() + ), + operator = SmlInfixOperationOperator.IdenticalTo, + rightOperand = createSmlBoolean(true) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant expression`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.IdenticalTo, + rightOperand = createSmlCall( + receiver = createSmlNull() + ) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class NotIdenticalTo { + + @Test + fun `should return true boolean literal if left and right operands are not identical`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.NotIdenticalTo, + rightOperand = createSmlInt(1) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(true) + } + + @Test + fun `should return false boolean literal if left and right operands are identical`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.NotIdenticalTo, + rightOperand = createSmlBoolean(true) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(false) + } + + @Test + fun `should return null if the left operand is not a constant expression`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlCall( + receiver = createSmlNull() + ), + operator = SmlInfixOperationOperator.NotIdenticalTo, + rightOperand = createSmlBoolean(true) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant expression`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.NotIdenticalTo, + rightOperand = createSmlCall( + receiver = createSmlNull() + ) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class LessThan { + + @ParameterizedTest + @CsvSource( + delimiter = '|', + textBlock = """ + 0.5 | 1.5 | true + 0.5 | 1 | true + 0 | 1.5 | true + 0 | 1 | true + 1.5 | 0.5 | false + 1.5 | 0 | false + 1 | 0.5 | false + 1 | 0 | false""" + ) + fun `should return whether left operand is less than right operand`( + leftOperand: Double, + rightOperand: Double, + expected: Boolean + ) { + val testData = createSmlInfixOperation( + leftOperand = leftOperand.toSmlNumber(), + operator = SmlInfixOperationOperator.LessThan, + rightOperand = rightOperand.toSmlNumber() + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(expected) + } + + @Test + fun `should return null if the left operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlNull(), + operator = SmlInfixOperationOperator.LessThan, + rightOperand = createSmlInt(1) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlInt(1), + operator = SmlInfixOperationOperator.LessThan, + rightOperand = createSmlNull() + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class LessThanOrEquals { + + @ParameterizedTest + @CsvSource( + delimiter = '|', + textBlock = """ + 0.5 | 0.5 | true + 0.5 | 1 | true + 0 | 1.5 | true + 0 | 1 | true + 1.5 | 0.5 | false + 1.5 | 0 | false + 1 | 0.5 | false + 1 | 0 | false""" + ) + fun `should return whether left operand is less than or equal to right operand`( + leftOperand: Double, + rightOperand: Double, + expected: Boolean + ) { + val testData = createSmlInfixOperation( + leftOperand = leftOperand.toSmlNumber(), + operator = SmlInfixOperationOperator.LessThanOrEquals, + rightOperand = rightOperand.toSmlNumber() + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(expected) + } + + @Test + fun `should return null if the left operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlNull(), + operator = SmlInfixOperationOperator.LessThanOrEquals, + rightOperand = createSmlInt(1) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlInt(1), + operator = SmlInfixOperationOperator.LessThanOrEquals, + rightOperand = createSmlNull() + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class GreaterThanOrEquals { + + @ParameterizedTest + @CsvSource( + delimiter = '|', + textBlock = """ + 0.5 | 0.5 | true + 1.5 | 0 | true + 1 | 0.5 | true + 1 | 0 | true + 0.5 | 1.5 | false + 0.5 | 1 | false + 0 | 1.5 | false + 0 | 1 | false""" + ) + fun `should return whether left operand is greater than or equal to right operand`( + leftOperand: Double, + rightOperand: Double, + expected: Boolean + ) { + val testData = createSmlInfixOperation( + leftOperand = leftOperand.toSmlNumber(), + operator = SmlInfixOperationOperator.GreaterThanOrEquals, + rightOperand = rightOperand.toSmlNumber() + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(expected) + } + + @Test + fun `should return null if the left operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlNull(), + operator = SmlInfixOperationOperator.GreaterThanOrEquals, + rightOperand = createSmlInt(1) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlInt(1), + operator = SmlInfixOperationOperator.GreaterThanOrEquals, + rightOperand = createSmlNull() + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class GreaterThan { + + @ParameterizedTest + @CsvSource( + delimiter = '|', + textBlock = """ + 1.5 | 0.5 | true + 1.5 | 0 | true + 1 | 0.5 | true + 1 | 0 | true + 0.5 | 1.5 | false + 0.5 | 1 | false + 0 | 1.5 | false + 0 | 1 | false""" + ) + fun `should return whether left operand is greater than right operand`( + leftOperand: Double, + rightOperand: Double, + expected: Boolean + ) { + val testData = createSmlInfixOperation( + leftOperand = leftOperand.toSmlNumber(), + operator = SmlInfixOperationOperator.GreaterThan, + rightOperand = rightOperand.toSmlNumber() + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(expected) + } + + @Test + fun `should return null if the left operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlNull(), + operator = SmlInfixOperationOperator.GreaterThan, + rightOperand = createSmlInt(1) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlInt(1), + operator = SmlInfixOperationOperator.GreaterThan, + rightOperand = createSmlNull() + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class Plus { + + @ParameterizedTest + @CsvSource( + delimiter = '|', + textBlock = """ + 1.5 | 0.25 | 1.75 + 1.5 | 1 | 2.5 + 1 | 0.25 | 1.25 + 1 | 1 | 2""" + ) + fun `should return sum of left and right operand`( + leftOperand: Double, + rightOperand: Double, + expected: Double + ) { + val testData = createSmlInfixOperation( + leftOperand = leftOperand.toSmlNumber(), + operator = SmlInfixOperationOperator.Plus, + rightOperand = rightOperand.toSmlNumber() + ) + + testData.toConstantExpressionOrNull() shouldBe expected.toSmlNumber().toConstantExpressionOrNull() + } + + @Test + fun `should return null if the left operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlNull(), + operator = SmlInfixOperationOperator.Plus, + rightOperand = createSmlInt(1) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlInt(1), + operator = SmlInfixOperationOperator.Plus, + rightOperand = createSmlNull() + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class Minus { + + @ParameterizedTest + @CsvSource( + delimiter = '|', + textBlock = """ + 1.5 | 0.25 | 1.25 + 1.5 | 1 | 0.5 + 1 | 0.25 | 0.75 + 1 | 1 | 0""" + ) + fun `should return difference between left and right operand`( + leftOperand: Double, + rightOperand: Double, + expected: Double + ) { + val testData = createSmlInfixOperation( + leftOperand = leftOperand.toSmlNumber(), + operator = SmlInfixOperationOperator.Minus, + rightOperand = rightOperand.toSmlNumber() + ) + + testData.toConstantExpressionOrNull() shouldBe expected.toSmlNumber().toConstantExpressionOrNull() + } + + @Test + fun `should return null if the left operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlNull(), + operator = SmlInfixOperationOperator.Minus, + rightOperand = createSmlInt(1) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlInt(1), + operator = SmlInfixOperationOperator.Minus, + rightOperand = createSmlNull() + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class Times { + + @ParameterizedTest + @CsvSource( + delimiter = '|', + textBlock = """ + 1.5 | 0.5 | 0.75 + 1.5 | 1 | 1.5 + 1 | 0.25 | 0.25 + 1 | 1 | 1""" + ) + fun `should return product of left and right operand`( + leftOperand: Double, + rightOperand: Double, + expected: Double + ) { + val testData = createSmlInfixOperation( + leftOperand = leftOperand.toSmlNumber(), + operator = SmlInfixOperationOperator.Times, + rightOperand = rightOperand.toSmlNumber() + ) + + testData.toConstantExpressionOrNull() shouldBe expected.toSmlNumber().toConstantExpressionOrNull() + } + + @Test + fun `should return null if the left operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlNull(), + operator = SmlInfixOperationOperator.Times, + rightOperand = createSmlInt(1) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlInt(1), + operator = SmlInfixOperationOperator.Times, + rightOperand = createSmlNull() + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class By { + + @ParameterizedTest + @CsvSource( + delimiter = '|', + textBlock = """ + 0.25 | 0.5 | 0.5 + 1.5 | 1 | 1.5 + 1 | 0.625 | 1.6 + 1 | 1 | 1""" + ) + fun `should return quotient of left and right operand`( + leftOperand: Double, + rightOperand: Double, + expected: Double + ) { + val testData = createSmlInfixOperation( + leftOperand = leftOperand.toSmlNumber(), + operator = SmlInfixOperationOperator.By, + rightOperand = rightOperand.toSmlNumber() + ) + + testData.toConstantExpressionOrNull() shouldBe expected.toSmlNumber().toConstantExpressionOrNull() + } + + @Test + fun `should return null if the left operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlNull(), + operator = SmlInfixOperationOperator.By, + rightOperand = createSmlInt(1) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant number`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlInt(1), + operator = SmlInfixOperationOperator.By, + rightOperand = createSmlNull() + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is constant integer 0`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlInt(1), + operator = SmlInfixOperationOperator.By, + rightOperand = createSmlInt(0) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is constant float 0`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlInt(1), + operator = SmlInfixOperationOperator.By, + rightOperand = createSmlFloat(0.0) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is constant float -0`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlInt(1), + operator = SmlInfixOperationOperator.By, + rightOperand = createSmlFloat(-0.0) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class Elvis { + + @Test + fun `should return left operand if it does not evaluate to a constant null`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlBoolean(true), + operator = SmlInfixOperationOperator.Elvis, + rightOperand = createSmlBoolean(true) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(true) + } + + @Test + fun `should return right operand if the left operand evaluates to a constant null`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlNull(), + operator = SmlInfixOperationOperator.Elvis, + rightOperand = createSmlBoolean(true) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(true) + } + + @Test + fun `should return null if the left operand is not a constant expression`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlCall( + receiver = createSmlNull() + ), + operator = SmlInfixOperationOperator.Elvis, + rightOperand = createSmlInt(1) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null if the right operand is not a constant expression`() { + val testData = createSmlInfixOperation( + leftOperand = createSmlInt(1), + operator = SmlInfixOperationOperator.Elvis, + rightOperand = createSmlCall( + receiver = createSmlNull() + ) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + } + + @Nested + inner class ParenthesizedExpression { + + @Test + fun `should return expression as constant expression for parenthesized expressions`() { + val testData = createSmlParenthesizedExpression(createSmlNull()) + testData.toConstantExpressionOrNull() shouldBe SmlConstantNull + } + } + + @Nested + inner class PrefixOperation { + + @Nested + inner class Not { + + @Test + fun `should return negated operand if it is a constant boolean`() { + val testData = createSmlPrefixOperation( + operator = SmlPrefixOperationOperator.Not, + operand = createSmlBoolean(true) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantBoolean(false) + } + + @Test + fun `should return null if the operand is not a constant boolean`() { + val testData = createSmlPrefixOperation( + operator = SmlPrefixOperationOperator.Not, + operand = createSmlNull() + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class Minus { + + @Test + fun `should return negated operand if it is a constant float`() { + val testData = createSmlPrefixOperation( + operator = SmlPrefixOperationOperator.Minus, + operand = createSmlFloat(1.0) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantFloat(-1.0) + } + + @Test + fun `should return negated operand if it is a constant int`() { + val testData = createSmlPrefixOperation( + operator = SmlPrefixOperationOperator.Minus, + operand = createSmlInt(1) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantInt(-1) + } + + @Test + fun `should return null if the operand is not a constant number`() { + val testData = createSmlPrefixOperation( + operator = SmlPrefixOperationOperator.Minus, + operand = createSmlNull() + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + } + + @Nested + inner class TemplateString { + + @Test + fun `should return concatenated string`() { + val testData = createSmlTemplateString( + stringParts = listOf( + "start ", + " inner1 ", + " inner2 ", + " inner3 ", + " inner4 ", + " inner5 ", + " end" + ), + templateExpressions = listOf( + createSmlBoolean(true), + createSmlFloat(1.0), + createSmlInt(1), + createSmlNull(), + createSmlString("string"), + createSmlReference(createSmlEnumVariant("Variant")) + ) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantString( + value = "start true inner1 1.0 inner2 1 inner3 null inner4 string inner5 Variant end" + ) + } + + @Test + fun `should return null if any expression is converted to null`() { + val testData = createSmlTemplateString( + stringParts = listOf("start ", " end"), + templateExpressions = listOf( + createSmlCall(receiver = createSmlNull()) + ) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class Call { + + private lateinit var compilationUnit: SmlCompilationUnit + + @BeforeEach + fun reset() { + compilationUnit = parseHelper.parseResource("partialEvaluation/calls.smltest")!! + } + + @Test + fun `should evaluate calls of block lambdas`() { + val workflow = compilationUnit.findUniqueDeclarationOrFail("callToBlockLambda") + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull() shouldBe SmlConstantInt(1) + } + + @Test + fun `should evaluate calls of expression lambdas`() { + val workflow = compilationUnit.findUniqueDeclarationOrFail("callToExpressionLambda") + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull() shouldBe SmlConstantInt(1) + } + + @Test + fun `should evaluate calls of steps`() { + val workflow = compilationUnit.findUniqueDeclarationOrFail("callToStep") + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull() shouldBe SmlConstantInt(1) + } + + @Test + fun `should evaluate calls of steps with variadic parameter`() { + val workflow = compilationUnit.findUniqueDeclarationOrFail("callToStepWithVariadicParameter") + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should evaluate calls of steps with indexed variadic parameter`() { + val workflow = compilationUnit + .findUniqueDeclarationOrFail("callToStepWithIndexedVariadicParameter") + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull() shouldBe SmlConstantInt(1) + } + + @Test + fun `should substitute parameters that were bound at call of a lambda`() { + val workflow = compilationUnit.findUniqueDeclarationOrFail( + "parameterAssignedDuringCall" + ) + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull() shouldBe SmlConstantInt(10) + } + + @Test + fun `should substitute parameters that were bound at creation of a lambda`() { + val workflow = compilationUnit.findUniqueDeclarationOrFail( + "parameterAssignedDuringCreationOfLambda" + ) + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull() shouldBe SmlConstantInt(1) + } + + @Test + fun `should evaluate calls with lambda as parameter`() { + val workflow = compilationUnit.findUniqueDeclarationOrFail("lambdaAsParameter") + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull() shouldBe SmlConstantInt(1) + } + + @Test + fun `should return null otherwise`() { + val testData = createSmlCall(receiver = createSmlNull()) + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class MemberAccess { + + @Test + fun `should return constant enum variant if referenced enum variant has no parameters`() { + val testEnumVariant = createSmlEnumVariant(name = "TestEnumVariant") + val testEnum = createSmlEnum( + name = "TestEnum", + variants = listOf(testEnumVariant) + ) + val testData = createSmlMemberAccess( + receiver = createSmlReference(testEnum), + member = createSmlReference(testEnumVariant) + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantEnumVariant(testEnumVariant) + } + + @Test + fun `should return null if referenced enum variant has parameters`() { + val testEnumVariant = createSmlEnumVariant( + name = "TestEnumVariant", + parameters = listOf( + createSmlParameter(name = "testParameter") + ) + ) + val testEnum = createSmlEnum( + name = "TestEnum", + variants = listOf(testEnumVariant) + ) + val testData = createSmlMemberAccess( + receiver = createSmlReference(testEnum), + member = createSmlReference(testEnumVariant) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return constant null if receiver is constant null and member access is null safe`() { + val testData = createSmlMemberAccess( + receiver = createSmlNull(), + member = createSmlReference(createSmlAttribute("testAttribute")), + isNullSafe = true + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantNull + } + + @Test + fun `should return null if receiver is constant null and member access is not null safe`() { + val testData = createSmlMemberAccess( + receiver = createSmlNull(), + member = createSmlReference(createSmlAttribute("testAttribute")) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should access the result of a call by name if result exists`() { + val compilationUnit = + parseHelper.parseResource("partialEvaluation/memberAccesses.smltest") + compilationUnit.shouldNotBeNull() + + val workflow = compilationUnit.findUniqueDeclarationOrFail("successfulResultAccess") + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull() shouldBe SmlConstantInt(1) + } + + @Test + fun `should return null if accessed result does not exist`() { + val compilationUnit = + parseHelper.parseResource("partialEvaluation/memberAccesses.smltest") + compilationUnit.shouldNotBeNull() + + val workflow = compilationUnit.findUniqueDeclarationOrFail("failedResultAccess") + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should return null for other receivers`() { + val testData = createSmlMemberAccess( + receiver = createSmlInt(1), + member = createSmlReference( + createSmlEnumVariant( + name = "TestEnumVariant", + parameters = listOf( + createSmlParameter(name = "testParameter") + ) + ) + ) + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } + + @Nested + inner class Reference { + + @Test + fun `should return constant enum variant if referenced enum variant has no parameters`() { + val testEnumVariant = createSmlEnumVariant(name = "TestEnumVariant") + val testData = createSmlReference( + declaration = testEnumVariant + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantEnumVariant(testEnumVariant) + } + + @Test + fun `should return null if referenced enum variant has parameters`() { + val testEnumVariant = createSmlEnumVariant( + name = "TestEnumVariant", + parameters = listOf( + createSmlParameter(name = "testParameter") + ) + ) + val testData = createSmlReference( + declaration = testEnumVariant + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should convert assigned value of referenced placeholder`() { + val testPlaceholder = createSmlPlaceholder("testPlaceholder") + createSmlAssignment( + assignees = listOf(testPlaceholder), + createSmlNull() + ) + val testData = createSmlReference( + declaration = testPlaceholder + ) + + testData.toConstantExpressionOrNull() shouldBe SmlConstantNull + } + + @Test + fun `should return null if referenced placeholder has no assigned value`() { + val testData = createSmlReference( + declaration = createSmlPlaceholder("testPlaceholder") + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `simplify should return substituted value if it exists`() { + val testParameter = createSmlParameter("testParameter") + val testData = createSmlReference( + declaration = testParameter + ) + + testData.simplify(mapOf(testParameter to SmlConstantNull)) shouldBe SmlConstantNull + } + + @Test + fun `simplify should return default value if referenced parameter is not substituted but optional`() { + val testParameter = createSmlParameter( + name = "testParameter", + defaultValue = createSmlNull() + ) + val testData = createSmlReference( + declaration = testParameter + ) + + testData.simplify(emptyMap()) shouldBe SmlConstantNull + } + + @Test + fun `simplify should return null if referenced parameter is required and not substituted`() { + val testParameter = createSmlParameter("testParameter") + val testData = createSmlReference( + declaration = testParameter + ) + + testData.simplify(emptyMap()).shouldBeNull() + } + + @Test + fun `toConstantExpression should return null if step is referenced`() { + val testData = createSmlReference(createSmlStep("testStep")) + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `simplify should return null if referenced step is impure`() { + val testData = createSmlReference(impureStep) + testData.simplify(emptyMap()).shouldBeNull() + } + + @Test + fun `simplify should return intermediate step if referenced step is pure`() { + val testData = createSmlReference(pureStep) + testData.simplify(emptyMap()).shouldBeInstanceOf() + } + + @Test + fun `simplify should return null if referenced step has recursive calls`() { + val testData = createSmlReference(recursiveStep) + testData.simplify(emptyMap()).shouldBeNull() + } + + @Test + fun `should return value of placeholders inside valid assignment with call as expression`() { + val compilationUnit = + parseHelper.parseResource("partialEvaluation/references.smltest") + compilationUnit.shouldNotBeNull() + + val workflow = compilationUnit.findUniqueDeclarationOrFail("successfulRecordAssignment") + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull() shouldBe SmlConstantInt(1) + } + + @Test + fun `should return null for references to placeholders inside invalid assignment with call as expression`() { + val compilationUnit = + parseHelper.parseResource("partialEvaluation/references.smltest") + compilationUnit.shouldNotBeNull() + + val workflow = compilationUnit.findUniqueDeclarationOrFail("failedRecordAssignment") + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull().shouldBeNull() + } + + @Test + fun `should evaluate references to placeholders (assigned, called step has different yield order)`() { + val compilationUnit = + parseHelper.parseResource("partialEvaluation/references.smltest") + compilationUnit.shouldNotBeNull() + + val workflow = compilationUnit.findUniqueDeclarationOrFail( + "recordAssignmentWithDifferentYieldOrder" + ) + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull() shouldBe SmlConstantInt(1) + } + + @Test + fun `should evaluate references to placeholders (assigned, called step has missing yield)`() { + val compilationUnit = + parseHelper.parseResource("partialEvaluation/references.smltest") + compilationUnit.shouldNotBeNull() + + val workflow = compilationUnit.findUniqueDeclarationOrFail("recordAssignmentWithMissingYield") + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull() shouldBe SmlConstantInt(1) + } + + @Test + fun `should evaluate references to placeholders (assigned, called step has additional yield)`() { + val compilationUnit = + parseHelper.parseResource("partialEvaluation/references.smltest") + compilationUnit.shouldNotBeNull() + + val workflow = compilationUnit.findUniqueDeclarationOrFail( + "recordAssignmentWithAdditionalYield" + ) + val testData = workflow.expectedExpression() + + testData.toConstantExpressionOrNull() shouldBe SmlConstantInt(1) + } + + @Test + fun `should return null for other declarations`() { + val testData = createSmlReference( + declaration = createSmlAnnotation("TestAnnotation") + ) + + testData.toConstantExpressionOrNull().shouldBeNull() + } + } +} + +private fun Double.toSmlNumber(): SmlAbstractExpression { + return when { + this == this.toInt().toDouble() -> createSmlInt(this.toInt()) + else -> createSmlFloat(this) + } +} + +/** + * Helper method for tests loaded from a resource that returns the expression of the first expression statement in the + * workflow. + */ +private fun SmlWorkflow.expectedExpression() = statementsOrEmpty() + .filterIsInstance() + .firstOrNull() + .shouldNotBeNull() + .expression diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/typing/TypeComputerTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/typing/TypeComputerTest.kt new file mode 100644 index 000000000..f2808a5fa --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/staticAnalysis/typing/TypeComputerTest.kt @@ -0,0 +1,992 @@ +package de.unibonn.simpleml.staticAnalysis.typing + +import com.google.inject.Inject +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.constant.SmlInfixOperationOperator +import de.unibonn.simpleml.constant.operator +import de.unibonn.simpleml.emf.blockLambdaResultsOrEmpty +import de.unibonn.simpleml.emf.descendants +import de.unibonn.simpleml.emf.parametersOrEmpty +import de.unibonn.simpleml.emf.resultsOrEmpty +import de.unibonn.simpleml.emf.typeArgumentsOrEmpty +import de.unibonn.simpleml.simpleML.SmlAbstractObject +import de.unibonn.simpleml.simpleML.SmlArgument +import de.unibonn.simpleml.simpleML.SmlAttribute +import de.unibonn.simpleml.simpleML.SmlBlockLambda +import de.unibonn.simpleml.simpleML.SmlBlockLambdaResult +import de.unibonn.simpleml.simpleML.SmlCall +import de.unibonn.simpleml.simpleML.SmlCallableType +import de.unibonn.simpleml.simpleML.SmlClass +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlEnum +import de.unibonn.simpleml.simpleML.SmlEnumVariant +import de.unibonn.simpleml.simpleML.SmlExpressionLambda +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.simpleML.SmlIndexedAccess +import de.unibonn.simpleml.simpleML.SmlInfixOperation +import de.unibonn.simpleml.simpleML.SmlMemberAccess +import de.unibonn.simpleml.simpleML.SmlMemberType +import de.unibonn.simpleml.simpleML.SmlNamedType +import de.unibonn.simpleml.simpleML.SmlParameter +import de.unibonn.simpleml.simpleML.SmlParenthesizedExpression +import de.unibonn.simpleml.simpleML.SmlParenthesizedType +import de.unibonn.simpleml.simpleML.SmlPlaceholder +import de.unibonn.simpleml.simpleML.SmlReference +import de.unibonn.simpleml.simpleML.SmlResult +import de.unibonn.simpleml.simpleML.SmlStep +import de.unibonn.simpleml.simpleML.SmlUnionType +import de.unibonn.simpleml.simpleML.SmlWorkflow +import de.unibonn.simpleml.simpleML.SmlYield +import de.unibonn.simpleml.staticAnalysis.assignedOrNull +import de.unibonn.simpleml.stdlibAccess.StdlibClasses +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.assertions.findUniqueDeclarationOrFail +import de.unibonn.simpleml.testing.getResourcePath +import io.kotest.assertions.forEachAsClue +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.sequences.shouldHaveSize +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import java.nio.file.Files +import java.nio.file.Path + +@Suppress("PrivatePropertyName") +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class TypeComputerTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + private val testRoot = javaClass.classLoader.getResourcePath("typeComputer").toString() + + // ***************************************************************************************************************** + // Assignees + // ****************************************************************************************************************/ + + @Nested + inner class BlockLambdaResults { + + @Test + fun `attributes should have declared type`() { + withCompilationUnitFromFile("assignees/blockLambdaResults") { + descendants().forEach { + val assigned = it.assignedOrNull() + assigned.shouldNotBeNull() + it shouldHaveType assigned + } + } + } + } + + @Nested + inner class Placeholders { + + @Test + fun `classes should have non-nullable class type`() { + withCompilationUnitFromFile("assignees/placeholders") { + descendants().forEach { + val assigned = it.assignedOrNull() + assigned.shouldNotBeNull() + it shouldHaveType assigned + } + } + } + } + + @Nested + inner class Yields { + + @Test + fun `enums should have non-nullable enum type`() { + withCompilationUnitFromFile("assignees/yields") { + descendants().forEach { + val assigned = it.assignedOrNull() + assigned.shouldNotBeNull() + it shouldHaveType assigned + } + } + } + } + + // ***************************************************************************************************************** + // Declarations + // ****************************************************************************************************************/ + + @Nested + inner class Attributes { + + @Test + fun `attributes should have declared type`() { + withCompilationUnitFromFile("declarations/attributes") { + descendants().forEach { + it shouldHaveType it.type + } + } + } + } + + @Nested + inner class Classes { + + @Test + fun `classes should have non-nullable class type`() { + withCompilationUnitFromFile("declarations/classes") { + descendants().forEach { + it shouldHaveType ClassType(it, isNullable = false) + } + } + } + } + + @Nested + inner class Enums { + + @Test + fun `enums should have non-nullable enum type`() { + withCompilationUnitFromFile("declarations/enums") { + descendants().forEach { + it shouldHaveType EnumType(it, isNullable = false) + } + } + } + } + + @Nested + inner class EnumVariants { + + @Test + fun `enum variants should have non-nullable enum variant type`() { + withCompilationUnitFromFile("declarations/enumVariants") { + descendants().forEach { + it shouldHaveType EnumVariantType(it, isNullable = false) + } + } + } + } + + @Nested + inner class Functions { + + @Test + fun `functions should have callable type with respective parameters and results`() { + withCompilationUnitFromFile("declarations/functions") { + descendants().forEach { function -> + function shouldHaveType CallableType( + function.parametersOrEmpty().map { it.type() }, + function.resultsOrEmpty().map { it.type() } + ) + } + } + } + } + + @Nested + inner class Parameters { + + @Test + fun `parameters should have declared type`() { + withCompilationUnitFromFile("declarations/parameters") { + findUniqueDeclarationOrFail("myStepWithNormalParameter") + .descendants().forEach { + it shouldHaveType it.type + } + } + } + + @Test + fun `variadic parameters should have variadic type with declared element type`() { + withCompilationUnitFromFile("declarations/parameters") { + findUniqueDeclarationOrFail("myStepWithVariadicParameter") + .descendants().forEach { + it shouldHaveType VariadicType(it.type.type()) + } + } + } + + @Test + fun `lambda parameters should have type inferred from context`() { + withCompilationUnitFromFile("declarations/parameters") { + findUniqueDeclarationOrFail("myStepWithLambdas") + .descendants() + .toList() + .forEachAsClue { + it shouldHaveType String + } + } + } + } + + @Nested + inner class Results { + + @Test + fun `results should have declared type`() { + withCompilationUnitFromFile("declarations/results") { + descendants().forEach { + it shouldHaveType it.type + } + } + } + } + + @Nested + inner class Steps { + + @Test + fun `steps should have callable type with respective parameters and results`() { + withCompilationUnitFromFile("declarations/steps") { + descendants().forEach { step -> + step shouldHaveType CallableType( + step.parametersOrEmpty().map { it.type() }, + step.resultsOrEmpty().map { it.type() } + ) + } + } + } + } + + // ***************************************************************************************************************** + // Expressions + // ****************************************************************************************************************/ + + @Nested + inner class Literals { + + @Test + fun `boolean literals should have type Boolean`() { + withCompilationUnitFromFile("expressions/literals") { + placeholderWithName("booleanLiteral").assignedValueOrFail() shouldHaveType Boolean + } + } + + @Test + fun `float literals should have type Float`() { + withCompilationUnitFromFile("expressions/literals") { + placeholderWithName("floatLiteral").assignedValueOrFail() shouldHaveType Float + } + } + + @Test + fun `int literals should have type Int`() { + withCompilationUnitFromFile("expressions/literals") { + placeholderWithName("intLiteral").assignedValueOrFail() shouldHaveType Int + } + } + + @Test + fun `null literals should have type nullable Nothing`() { + withCompilationUnitFromFile("expressions/literals") { + placeholderWithName("nullLiteral").assignedValueOrFail() shouldHaveType NothingOrNull + } + } + + @Test + fun `string literals should have type String`() { + withCompilationUnitFromFile("expressions/literals") { + placeholderWithName("stringLiteral").assignedValueOrFail() shouldHaveType String + } + } + } + + @Nested + inner class TemplateStrings { + + @Test + fun `template strings should have type String`() { + withCompilationUnitFromFile("expressions/templateStrings") { + placeholderWithName("templateString").assignedValueOrFail() shouldHaveType String + } + } + } + + @Nested + inner class Arguments { + + @Test + fun `arguments should have type of value`() { + withCompilationUnitFromFile("expressions/arguments") { + descendants().forEach { + it shouldHaveType it.value + } + } + } + } + + @Nested + inner class BlockLambdas { + + @Test + fun `block lambdas should have callable type (explicit parameter types)`() { + withCompilationUnitFromFile("expressions/blockLambdas") { + findUniqueDeclarationOrFail("lambdasWithExplicitParameterTypes") + .descendants().forEach { lambda -> + lambda shouldHaveType CallableType( + lambda.parametersOrEmpty().map { it.type() }, + lambda.blockLambdaResultsOrEmpty().map { it.type() } + ) + } + } + } + + @Test + fun `block lambdas should have callable type (explicit variadic parameter type)`() { + withCompilationUnitFromFile("expressions/blockLambdas") { + findUniqueDeclarationOrFail("lambdasWithExplicitVariadicType") + .descendants().forEach { lambda -> + lambda shouldHaveType CallableType( + lambda.parametersOrEmpty().map { it.type() }, + lambda.blockLambdaResultsOrEmpty().map { it.type() } + ) + } + } + } + + @Test + fun `block lambdas should have callable type (yielded)`() { + withCompilationUnitFromFile("expressions/blockLambdas") { + val step = findUniqueDeclarationOrFail("yieldedLambda") + + val result = step.findUniqueDeclarationOrFail("result") + val resultType = result.type.shouldBeInstanceOf() + + val lambdas = step.descendants() + lambdas.shouldHaveSize(1) + val lambda = lambdas.first() + + lambda shouldHaveType CallableType( + resultType.parametersOrEmpty().map { it.type() }, + lambda.blockLambdaResultsOrEmpty().map { it.type() } + ) + } + } + + @Test + fun `block lambdas should have callable type (argument)`() { + withCompilationUnitFromFile("expressions/blockLambdas") { + val parameter = findUniqueDeclarationOrFail("parameter") + val parameterType = parameter.type.shouldBeInstanceOf() + + val step = findUniqueDeclarationOrFail("argumentLambda") + val lambdas = step.descendants() + lambdas.shouldHaveSize(1) + val lambda = lambdas.first() + + lambda shouldHaveType CallableType( + parameterType.parametersOrEmpty().map { it.type() }, + lambda.blockLambdaResultsOrEmpty().map { it.type() } + ) + } + } + } + + @Nested + inner class Calls { + + @Test + fun `class call should have class type of called class`() { + withCompilationUnitFromFile("expressions/calls") { + val `class` = findUniqueDeclarationOrFail("C") + + val calls = descendants().toList() + calls.shouldHaveSize(11) + calls[0] shouldHaveType ClassType(`class`, isNullable = false) + } + } + + @Test + fun `callable type call should have type of result (one result)`() { + withCompilationUnitFromFile("expressions/calls") { + val parameter = findUniqueDeclarationOrFail("p1") + val parameterType = parameter.type.shouldBeInstanceOf() + parameterType.resultsOrEmpty().shouldHaveSize(1) + + val calls = descendants().toList() + calls.shouldHaveSize(11) + calls[1] shouldHaveType parameterType.resultsOrEmpty()[0] + } + } + + @Test + fun `callable type call should have record type (multiple result)`() { + withCompilationUnitFromFile("expressions/calls") { + val parameter = findUniqueDeclarationOrFail("p2") + val parameterType = parameter.type.shouldBeInstanceOf() + + val calls = descendants().toList() + calls.shouldHaveSize(11) + calls[2] shouldHaveType RecordType(parameterType.resultsOrEmpty().map { it.name to it.type() }) + } + } + + @Test + fun `enum variant call should have enum variant type of called enum variant`() { + withCompilationUnitFromFile("expressions/calls") { + val enumVariant = findUniqueDeclarationOrFail("V") + + val calls = descendants().toList() + calls.shouldHaveSize(11) + calls[3] shouldHaveType EnumVariantType(enumVariant, isNullable = false) + } + } + + @Test + fun `function call should have type of result (one result)`() { + withCompilationUnitFromFile("expressions/calls") { + val function = findUniqueDeclarationOrFail("f1") + function.resultsOrEmpty().shouldHaveSize(1) + + val calls = descendants().toList() + calls.shouldHaveSize(11) + calls[4] shouldHaveType function.resultsOrEmpty()[0] + } + } + + @Test + fun `function call should have record type (multiple result)`() { + withCompilationUnitFromFile("expressions/calls") { + val function = findUniqueDeclarationOrFail("f2") + + val calls = descendants().toList() + calls.shouldHaveSize(11) + calls[5] shouldHaveType RecordType(function.resultsOrEmpty().map { it.name to it.type() }) + } + } + + @Test + fun `block lambda call should have type of result (one result)`() { + withCompilationUnitFromFile("expressions/calls") { + val blockLambdas = descendants().toList() + blockLambdas.shouldHaveSize(2) + val blockLambda = blockLambdas[0] + + val calls = descendants().toList() + calls.shouldHaveSize(11) + calls[6] shouldHaveType blockLambda.blockLambdaResultsOrEmpty()[0] + } + } + + @Test + fun `block lambda call should have record type (multiple result)`() { + withCompilationUnitFromFile("expressions/calls") { + val blockLambdas = descendants().toList() + blockLambdas.shouldHaveSize(2) + val blockLambda = blockLambdas[1] + + val calls = descendants().toList() + calls.shouldHaveSize(11) + calls[7] shouldHaveType RecordType(blockLambda.blockLambdaResultsOrEmpty().map { it.name to it.type() }) + } + } + + @Test + fun `expression lambda call should have type of result`() { + withCompilationUnitFromFile("expressions/calls") { + val expressionLambdas = descendants().toList() + expressionLambdas.shouldHaveSize(1) + val expressionLambda = expressionLambdas[0] + + val calls = descendants().toList() + calls.shouldHaveSize(11) + calls[8] shouldHaveType expressionLambda.result + } + } + + @Test + fun `step call should have type of result (one result)`() { + withCompilationUnitFromFile("expressions/calls") { + val step = findUniqueDeclarationOrFail("s1") + step.resultsOrEmpty().shouldHaveSize(1) + + val calls = descendants().toList() + calls.shouldHaveSize(11) + calls[9] shouldHaveType step.resultsOrEmpty()[0] + } + } + + @Test + fun `step call should have record type (multiple result)`() { + withCompilationUnitFromFile("expressions/calls") { + val step = findUniqueDeclarationOrFail("s2") + + val calls = descendants().toList() + calls.shouldHaveSize(11) + calls[10] shouldHaveType RecordType(step.resultsOrEmpty().map { it.name to it.type() }) + } + } + } + + @Nested + inner class ExpressionLambdas { + + @Test + fun `expression lambdas should have callable type (explicit parameter types)`() { + withCompilationUnitFromFile("expressions/expressionLambdas") { + findUniqueDeclarationOrFail("lambdasWithExplicitParameterTypes") + .descendants().forEach { lambda -> + lambda shouldHaveType CallableType( + lambda.parametersOrEmpty().map { it.type() }, + listOf(lambda.result.type()) + ) + } + } + } + + @Test + fun `expression lambdas should have callable type (explicit variadic parameter type)`() { + withCompilationUnitFromFile("expressions/expressionLambdas") { + findUniqueDeclarationOrFail("lambdasWithExplicitVariadicType") + .descendants().forEach { lambda -> + lambda shouldHaveType CallableType( + lambda.parametersOrEmpty().map { it.type() }, + listOf(lambda.result.type()) + ) + } + } + } + + @Test + fun `expression lambdas should have callable type (yielded)`() { + withCompilationUnitFromFile("expressions/expressionLambdas") { + val step = findUniqueDeclarationOrFail("yieldedLambda") + + val result = step.findUniqueDeclarationOrFail("result") + val resultType = result.type.shouldBeInstanceOf() + + val lambdas = step.descendants() + lambdas.shouldHaveSize(1) + val lambda = lambdas.first() + + lambda shouldHaveType CallableType( + resultType.parametersOrEmpty().map { it.type() }, + listOf(lambda.result.type()) + ) + } + } + + @Test + fun `expression lambdas should have callable type (argument)`() { + withCompilationUnitFromFile("expressions/expressionLambdas") { + val parameter = findUniqueDeclarationOrFail("parameter") + val parameterType = parameter.type.shouldBeInstanceOf() + + val step = findUniqueDeclarationOrFail("argumentLambda") + val lambdas = step.descendants() + lambdas.shouldHaveSize(1) + val lambda = lambdas.first() + + lambda shouldHaveType CallableType( + parameterType.parametersOrEmpty().map { it.type() }, + listOf(lambda.result.type()) + ) + } + } + } + + @Nested + inner class IndexedAccesses { + + @Test + fun `indexed accesses should return element type if receiver is variadic (myStep1)`() { + withCompilationUnitFromFile("expressions/indexedAccesses") { + findUniqueDeclarationOrFail("myStep1") + .descendants() + .forEach { + it shouldHaveType Int + } + } + } + + @Test + fun `indexed accesses should return element type if receiver is variadic (myStep2)`() { + withCompilationUnitFromFile("expressions/indexedAccesses") { + findUniqueDeclarationOrFail("myStep2") + .descendants() + .forEach { + it shouldHaveType String + } + } + } + + @Test + fun `indexed accesses should return Nothing type if receiver is not variadic`() { + withCompilationUnitFromFile("expressions/indexedAccesses") { + findUniqueDeclarationOrFail("myStep3") + .descendants() + .forEach { + it shouldHaveType Nothing + } + } + } + + @Test + fun `indexed accesses should return Unresolved type if receiver is unresolved`() { + withCompilationUnitFromFile("expressions/indexedAccesses") { + findUniqueDeclarationOrFail("myStep4") + .descendants() + .forEach { + it shouldHaveType UnresolvedType + } + } + } + } + + @Nested + inner class MemberAccesses { + + @Test + fun `non-null-safe member accesses should have type of referenced member`() { + withCompilationUnitFromFile("expressions/memberAccesses") { + descendants() + .filter { !it.isNullSafe } + .forEach { + it shouldHaveType it.member + } + } + } + + @Test + fun `null-safe member accesses should have type of referenced member but nullable`() { + withCompilationUnitFromFile("expressions/memberAccesses") { + descendants() + .filter { it.isNullSafe } + .forEach { + it shouldHaveType it.member.type().setIsNullableOnCopy(isNullable = true) + } + } + } + } + + @Nested + inner class ParenthesizedExpressions { + + @Test + fun `parenthesized expressions should have type of expressions`() { + withCompilationUnitFromFile("expressions/parenthesizedExpressions") { + descendants().forEach { + it shouldHaveType it.expression + } + } + } + } + + @Nested + inner class References { + + @Test + fun `references should have type of referenced declaration`() { + withCompilationUnitFromFile("expressions/references") { + descendants().forEach { + it shouldHaveType it.declaration + } + } + } + } + + @Nested + inner class Operations { + + @ParameterizedTest + @ValueSource( + strings = [ + "additionIntInt", + "subtractionIntInt", + "multiplicationIntInt", + "divisionIntInt", + "negationInt" + ], + ) + fun `arithmetic operations with only Int operands should have type Int`(placeholderName: String) { + withCompilationUnitFromFile("expressions/operations/arithmetic") { + placeholderWithName(placeholderName).assignedValueOrFail() shouldHaveType Int + } + } + + @ParameterizedTest + @ValueSource( + strings = [ + "additionIntFloat", + "subtractionIntFloat", + "multiplicationIntFloat", + "divisionIntFloat", + "negationFloat", + "additionInvalid", + "subtractionInvalid", + "multiplicationInvalid", + "divisionInvalid", + "negationInvalid" + ], + ) + fun `arithmetic operations with non-Int operands should have type Float`(placeholderName: String) { + withCompilationUnitFromFile("expressions/operations/arithmetic") { + placeholderWithName(placeholderName).assignedValueOrFail() shouldHaveType Float + } + } + + @ParameterizedTest + @ValueSource( + strings = [ + "lessThan", + "lessThanOrEquals", + "greaterThanOrEquals", + "greaterThan", + "lessThanInvalid", + "lessThanOrEqualsInvalid", + "greaterThanOrEqualsInvalid", + "greaterThanInvalid" + ], + ) + fun `comparison operations should have type Boolean`(placeholderName: String) { + withCompilationUnitFromFile("expressions/operations/comparison") { + placeholderWithName(placeholderName).assignedValueOrFail() shouldHaveType Boolean + } + } + + @ParameterizedTest + @ValueSource( + strings = [ + "equals", + "notEquals" + ], + ) + fun `equality operations should have type Boolean`(placeholderName: String) { + withCompilationUnitFromFile("expressions/operations/equality") { + placeholderWithName(placeholderName).assignedValueOrFail() shouldHaveType Boolean + } + } + + @ParameterizedTest + @ValueSource( + strings = [ + "strictlyEquals", + "notStrictlyEquals" + ], + ) + fun `strict equality operations should have type Boolean`(placeholderName: String) { + withCompilationUnitFromFile("expressions/operations/strictEquality") { + placeholderWithName(placeholderName).assignedValueOrFail() shouldHaveType Boolean + } + } + + @ParameterizedTest + @ValueSource( + strings = [ + "conjunction", + "disjunction", + "negation", + "conjunctionInvalid", + "disjunctionInvalid", + "negationInvalid" + ], + ) + fun `logical operations should have type Boolean`(placeholderName: String) { + withCompilationUnitFromFile("expressions/operations/logical") { + placeholderWithName(placeholderName).assignedValueOrFail() shouldHaveType Boolean + } + } + + @Test + fun `elvis operator with non-nullable left operand should have type of left operand`() { + withCompilationUnitFromFile("expressions/operations/elvis") { + findUniqueDeclarationOrFail("elvisWithNonNullableLeftOperand") + .descendants() + .filter { it.operator() == SmlInfixOperationOperator.Elvis } + .forEach { it shouldHaveType it.leftOperand } + } + } + + @Test + fun `elvis operator with nullable left operand should have lowest common supertype of non-nullable left operand and right operand (intOrNullElseIntOrNull)`() { + withCompilationUnitFromFile("expressions/operations/elvis") { + placeholderWithName("intOrNullElseIntOrNull") shouldHaveType IntOrNull + } + } + + @Test + fun `elvis operator with nullable left operand should have lowest common supertype of non-nullable left operand and right operand (intOrNullElseNull)`() { + withCompilationUnitFromFile("expressions/operations/elvis") { + placeholderWithName("intOrNullElseNull") shouldHaveType IntOrNull + } + } + + @Test + fun `elvis operator with nullable left operand should have lowest common supertype of non-nullable left operand and right operand (intOrNullElseInt)`() { + withCompilationUnitFromFile("expressions/operations/elvis") { + placeholderWithName("intOrNullElseInt") shouldHaveType Int + } + } + + @Test + fun `elvis operator with nullable left operand should have lowest common supertype of non-nullable left operand and right operand (intOrNullElseFloat)`() { + withCompilationUnitFromFile("expressions/operations/elvis") { + placeholderWithName("intOrNullElseFloat") shouldHaveType Number + } + } + + @Test + fun `elvis operator with nullable left operand should have lowest common supertype of non-nullable left operand and right operand (intOrNullElseString)`() { + withCompilationUnitFromFile("expressions/operations/elvis") { + placeholderWithName("intOrNullElseString") shouldHaveType Any + } + } + + @Test + fun `elvis operator with nullable left operand should have lowest common supertype of non-nullable left operand and right operand (intOrNullElseStringOrNull)`() { + withCompilationUnitFromFile("expressions/operations/elvis") { + placeholderWithName("intOrNullElseStringOrNull") shouldHaveType AnyOrNull + } + } + } + +// ***************************************************************************************************************** +// Types +// ****************************************************************************************************************/ + + @Nested + inner class CallableTypes { + + @Test + fun `callable type should have callable type with respective parameters and results`() { + withCompilationUnitFromFile("types/callableTypes") { + descendants().forEach { callableType -> + callableType shouldHaveType CallableType( + callableType.parametersOrEmpty().map { it.type() }, + callableType.resultsOrEmpty().map { it.type() } + ) + } + } + } + } + + @Nested + inner class MemberTypes { + + @Test + fun `non-nullable member type should have type of referenced member`() { + withCompilationUnitFromFile("types/memberTypes") { + findUniqueDeclarationOrFail("nonNullableMemberTypes") + .descendants().forEach { + it shouldHaveType it.member + } + } + } + + @Test + fun `nullable member type should have nullable type of referenced member`() { + withCompilationUnitFromFile("types/memberTypes") { + findUniqueDeclarationOrFail("nullableMemberTypes") + .descendants().forEach { + it shouldHaveType it.member.type().setIsNullableOnCopy(isNullable = true) + } + } + } + } + + @Nested + inner class NamedTypes { + + @Test + fun `non-nullable named type should have type of referenced declaration`() { + withCompilationUnitFromFile("types/namedTypes") { + findUniqueDeclarationOrFail("nonNullableNamedTypes") + .descendants().forEach { + it shouldHaveType it.declaration + } + } + } + + @Test + fun `nullable named type should have nullable type of referenced declaration`() { + withCompilationUnitFromFile("types/namedTypes") { + findUniqueDeclarationOrFail("nullableNamedTypes") + .descendants().forEach { + it shouldHaveType it.declaration.type().setIsNullableOnCopy(isNullable = true) + } + } + } + } + + @Nested + inner class ParenthesizedTypes { + + @Test + fun `parenthesized type should have type of type`() { + withCompilationUnitFromFile("types/parenthesizedTypes") { + descendants().forEach { + it shouldHaveType it.type + } + } + } + } + + @Nested + inner class UnionTypes { + + @Test + fun `union type should have union type over its type arguments`() { + withCompilationUnitFromFile("types/unionTypes") { + descendants().forEach { unionType -> + unionType shouldHaveType UnionType( + unionType.typeArgumentsOrEmpty().map { it.type() }.toSet() + ) + } + } + } + } + +// ***************************************************************************************************************** +// Helpers +// ****************************************************************************************************************/ + + infix fun SmlAbstractObject.shouldHaveType(expectedType: Type) { + this.type().shouldBe(expectedType) + } + + infix fun SmlAbstractObject.shouldHaveType(expected: SmlAbstractObject) { + this.type().shouldBe(expected.type()) + } + + private fun SmlPlaceholder.assignedValueOrFail(): SmlAbstractObject { + return this.assignedOrNull() + ?: throw IllegalArgumentException("No value is assigned to placeholder with name '$name'.") + } + + private fun SmlCompilationUnit.placeholderWithName(name: String): SmlPlaceholder { + val candidates = this.eAllContents().asSequence() + .filterIsInstance() + .filter { it.name == name } + .toList() + + when (candidates.size) { + 1 -> return candidates.first() + else -> throw IllegalArgumentException("File contains ${candidates.size} placeholders with name '$name'.") + } + } + + private fun withCompilationUnitFromFile(file: String, lambda: SmlCompilationUnit.() -> Unit) { + val program = Files.readString(Path.of(testRoot, "$file.${SmlFileExtension.Test}")) + val compilationUnit = parseHelper.parseProgramText(program) + ?: throw IllegalArgumentException("File is not a compilation unit.") + compilationUnit.apply(lambda) + } + + private val SmlCompilationUnit.Any get() = stdlibType(this, StdlibClasses.Any) + private val SmlCompilationUnit.AnyOrNull get() = stdlibType(this, StdlibClasses.Any, isNullable = true) + private val SmlCompilationUnit.Boolean get() = stdlibType(this, StdlibClasses.Boolean) + private val SmlCompilationUnit.Number get() = stdlibType(this, StdlibClasses.Number) + private val SmlCompilationUnit.Float get() = stdlibType(this, StdlibClasses.Float) + private val SmlCompilationUnit.Int get() = stdlibType(this, StdlibClasses.Int) + private val SmlCompilationUnit.IntOrNull get() = stdlibType(this, StdlibClasses.Int, isNullable = true) + private val SmlCompilationUnit.Nothing get() = stdlibType(this, StdlibClasses.Nothing) + private val SmlCompilationUnit.NothingOrNull get() = stdlibType(this, StdlibClasses.Nothing, isNullable = true) + private val SmlCompilationUnit.String get() = stdlibType(this, StdlibClasses.String) +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibAnnotationsTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibAnnotationsTest.kt new file mode 100644 index 000000000..304e042d9 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/stdlibAccess/StdlibAnnotationsTest.kt @@ -0,0 +1,197 @@ +package de.unibonn.simpleml.stdlibAccess + +import com.google.inject.Inject +import de.unibonn.simpleml.constant.SmlFileExtension +import de.unibonn.simpleml.simpleML.SmlAnnotation +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.simpleML.SmlFunction +import de.unibonn.simpleml.testing.ParseHelper +import de.unibonn.simpleml.testing.SimpleMLInjectorProvider +import de.unibonn.simpleml.testing.assertions.findUniqueDeclarationOrFail +import io.kotest.matchers.booleans.shouldBeFalse +import io.kotest.matchers.booleans.shouldBeTrue +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +private const val testRoot = "stdlibAccess/annotations" + +@ExtendWith(InjectionExtension::class) +@InjectWith(SimpleMLInjectorProvider::class) +class StdlibAnnotationsTest { + + @Inject + private lateinit var parseHelper: ParseHelper + + @Nested + inner class DescriptionOrNull { + + @Test + fun `should return description if it exists and is unique`() = withCompilationUnit("description") { + val testData = findUniqueDeclarationOrFail("functionWithUniqueDescription") + testData.descriptionOrNull() shouldBe "Lorem ipsum" + } + + @Test + fun `should return null if description is not unique`() = withCompilationUnit("description") { + val testData = findUniqueDeclarationOrFail("functionWithMultipleDescriptions") + testData.descriptionOrNull().shouldBeNull() + } + + @Test + fun `should return null if declaration does not have a description`() = withCompilationUnit("description") { + val testData = findUniqueDeclarationOrFail("functionWithoutDescription") + testData.descriptionOrNull().shouldBeNull() + } + } + + @Nested + inner class IsDeprecated { + + @Test + fun `should return false if the declaration is not deprecated`() = withCompilationUnit("deprecated") { + val testData = findUniqueDeclarationOrFail("nonDeprecatedFunction") + testData.isDeprecated().shouldBeFalse() + } + + @Test + fun `should return true if the declaration is deprecated`() = withCompilationUnit("deprecated") { + val testData = findUniqueDeclarationOrFail("deprecatedFunction") + testData.isDeprecated().shouldBeTrue() + } + } + + @Nested + inner class IsPure { + + @Test + fun `should return false if the function is not pure`() = withCompilationUnit("pure") { + val testData = findUniqueDeclarationOrFail("impureFunction") + testData.isPure().shouldBeFalse() + } + + @Test + fun `should return true if the function is pure`() = withCompilationUnit("pure") { + val testData = findUniqueDeclarationOrFail("pureFunction") + testData.isPure().shouldBeTrue() + } + } + + @Nested + inner class IsRepeatable { + + @Test + fun `should return false if the annotation is not repeatable`() = withCompilationUnit("repeatable") { + val testData = findUniqueDeclarationOrFail("UnrepeatableAnnotation") + testData.isRepeatable().shouldBeFalse() + } + + @Test + fun `should return true if the annotation is repeatable`() = withCompilationUnit("repeatable") { + val testData = findUniqueDeclarationOrFail("RepeatableAnnotation") + testData.isRepeatable().shouldBeTrue() + } + } + + @Nested + inner class PythonModuleOrNull { + + @Test + fun `should return Python module if it exists and is unique`() = withCompilationUnit("pythonModule") { + pythonModuleOrNull() shouldBe "python_module" + } + + @Test + fun `should return null if Python module is not unique`() = + withCompilationUnit("pythonModuleMultipleAnnotations") { + pythonModuleOrNull().shouldBeNull() + } + + @Test + fun `should return null if compilation unit does not have a Python module`() = + withCompilationUnit("pythonModuleMissing") { + pythonModuleOrNull().shouldBeNull() + } + } + + @Nested + inner class PythonNameOrNull { + + @Test + fun `should return Python name if it exists and is unique`() = withCompilationUnit("pythonName") { + val testData = findUniqueDeclarationOrFail("functionWithUniquePythonName") + testData.pythonNameOrNull() shouldBe "function_with_python_name" + } + + @Test + fun `should return null if Python name is not unique`() = withCompilationUnit("pythonName") { + val testData = findUniqueDeclarationOrFail("functionWithMultiplePythonNames") + testData.pythonNameOrNull().shouldBeNull() + } + + @Test + fun `should return null if compilation unit does not have a Python name`() = withCompilationUnit("pythonName") { + val testData = findUniqueDeclarationOrFail("functionWithoutPythonName") + testData.pythonNameOrNull().shouldBeNull() + } + } + + @Nested + inner class SinceVersionOrNull { + + @Test + fun `should return version if it exists and is unique`() = withCompilationUnit("since") { + val testData = findUniqueDeclarationOrFail("functionWithUniqueSince") + testData.sinceVersionOrNull() shouldBe "1.0.0" + } + + @Test + fun `should return null if version is not unique`() = withCompilationUnit("since") { + val testData = findUniqueDeclarationOrFail("functionWithoutSince") + testData.sinceVersionOrNull().shouldBeNull() + } + + @Test + fun `should return null if declaration does not specify the version it was added`() = + withCompilationUnit("since") { + val testData = findUniqueDeclarationOrFail("functionWithMultipleSinces") + testData.sinceVersionOrNull().shouldBeNull() + } + } + + @Nested + inner class ValidTargets { + + @Test + fun `should return targets if it exists and is unique`() = withCompilationUnit("target") { + val testData = findUniqueDeclarationOrFail("AnnotationWithUniqueTarget") + testData.validTargets().shouldContainExactly(StdlibEnums.AnnotationTarget.Class) + } + + @Test + fun `should return all possible targets if targets is not unique`() = withCompilationUnit("target") { + val testData = findUniqueDeclarationOrFail("AnnotationWithoutTarget") + testData.validTargets() shouldBe StdlibEnums.AnnotationTarget.values() + } + + @Test + fun `should return all possible targets if annotation does not restrict its targets`() = + withCompilationUnit("target") { + val testData = findUniqueDeclarationOrFail("AnnotationWithMultipleTargets") + testData.validTargets() shouldBe StdlibEnums.AnnotationTarget.values() + } + } + + private fun withCompilationUnit(resourceName: String, check: SmlCompilationUnit.() -> Unit) { + parseHelper + .parseResource("$testRoot/$resourceName.${SmlFileExtension.Test}") + .shouldNotBeNull() + .check() + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/testing/TestRangesTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/testing/TestRangesTest.kt new file mode 100644 index 000000000..4e8a08c6f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/testing/TestRangesTest.kt @@ -0,0 +1,127 @@ +package de.unibonn.simpleml.testing + +import de.unibonn.simpleml.location.XtextPosition +import de.unibonn.simpleml.location.XtextRange +import de.unibonn.simpleml.testing.FindTestRangesResult.CloseWithoutOpenFailure +import de.unibonn.simpleml.testing.FindTestRangesResult.OpenWithoutCloseFailure +import de.unibonn.simpleml.testing.FindTestRangesResult.Success +import de.unibonn.simpleml.testing.TestMarker.CLOSE +import de.unibonn.simpleml.testing.TestMarker.OPEN +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf +import org.junit.jupiter.api.Test + +class TestRangesTest { + + @Test + fun `should find all ranges enclosed by test markers in order of opening markers`() { + val result = findTestRanges("text${OPEN}text$CLOSE\n${OPEN}text$CLOSE") + result.shouldBeInstanceOf() + result.ranges.shouldHaveSize(2) + + result.ranges[0] shouldBe XtextRange.fromInts( + startLine = 1, + startColumn = 6, + endLine = 1, + endColumn = 10, + length = 4 + ) + + result.ranges[1] shouldBe XtextRange.fromInts( + startLine = 2, + startColumn = 2, + endLine = 2, + endColumn = 6, + length = 4 + ) + } + + @Test + fun `should handle nested test markers`() { + val result = findTestRanges("$OPEN\n $OPEN$CLOSE\n$CLOSE") + result.shouldBeInstanceOf() + result.ranges.shouldHaveSize(2) + + result.ranges[0] shouldBe XtextRange.fromInts( + startLine = 1, + startColumn = 2, + endLine = 3, + endColumn = 1, + length = 8 + ) + + result.ranges[1] shouldBe XtextRange.fromInts( + startLine = 2, + startColumn = 6, + endLine = 2, + endColumn = 6, + length = 0 + ) + } + + @Test + fun `should handle line feed (Unix)`() { + val result = findTestRanges("\n$OPEN\n$CLOSE") + result.shouldBeInstanceOf() + result.ranges.shouldHaveSize(1) + + result.ranges[0] shouldBe XtextRange.fromInts( + startLine = 2, + startColumn = 2, + endLine = 3, + endColumn = 1, + length = 1 + ) + } + + @Test + fun `should handle carriage return (MacOS)`() { + val result = findTestRanges("\r$OPEN\r$CLOSE") + result.shouldBeInstanceOf() + result.ranges.shouldHaveSize(1) + + result.ranges[0] shouldBe XtextRange.fromInts( + startLine = 2, + startColumn = 2, + endLine = 3, + endColumn = 1, + length = 1 + ) + } + + @Test + fun `should handle carriage return + line feed (Windows)`() { + val result = findTestRanges("\r\n$OPEN\r\n$CLOSE") + result.shouldBeInstanceOf() + result.ranges.shouldHaveSize(1) + + result.ranges[0] shouldBe XtextRange.fromInts( + startLine = 2, + startColumn = 2, + endLine = 3, + endColumn = 1, + length = 2 + ) + } + + @Test + fun `should report closing test markers without matching opening test marker`() { + val result = findTestRanges("$OPEN\n$CLOSE$CLOSE") + result.shouldBeInstanceOf() + result.position shouldBe XtextPosition.fromInts(line = 2, column = 2) + result.message shouldBe "Found '$CLOSE' without previous '$OPEN' at 2:2." + } + + @Test + fun `should report opening test markers without matching closing test marker`() { + val result = findTestRanges("$OPEN\n$OPEN$OPEN$CLOSE") + result.shouldBeInstanceOf() + + result.positions.shouldHaveSize(2) + result.positions[0] shouldBe XtextPosition.fromInts(line = 1, column = 1) + result.positions[1] shouldBe XtextPosition.fromInts(line = 2, column = 1) + + result.message shouldBe "Found '$OPEN' without following '$CLOSE' at 1:1, 2:1." + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/utils/CollectionUtilsTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/utils/CollectionUtilsTest.kt new file mode 100644 index 000000000..1ee260413 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/utils/CollectionUtilsTest.kt @@ -0,0 +1,94 @@ +package de.unibonn.simpleml.utils + +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +class CollectionUtilsTest { + + @Nested + inner class DuplicatesBy { + + @Test + fun `should keep only elements that are mapped to same non-null label as other element`() { + val actualResult = listOf(1, 1, 2, null).duplicatesBy { element -> element?.let { it + 1 } } + actualResult.shouldContainExactly(1, 1) + } + } + + @Nested + inner class UniqueBy { + + @Test + fun `should keep only elements that are mapped to unique non-null label`() { + val actualResult = listOf(1, 1, 2, null).uniqueBy { element -> element?.let { it + 1 } } + actualResult.shouldContainExactly(2) + } + } + + @Nested + inner class UniqueOrNull { + + @Test + fun `should return the sole element of the list if no filter is provided`() { + listOf(1).uniqueOrNull() shouldBe 1 + } + + @Test + fun `should return null if the list is empty and no filter is provided`() { + emptyList().uniqueOrNull().shouldBeNull() + } + + @Test + fun `should return null if the list has multiple elements and no filter is provided`() { + listOf(1, 2).uniqueOrNull().shouldBeNull() + } + + @Test + fun `should return the unique element that matches the filter`() { + listOf(1, 2).uniqueOrNull { it < 2 } shouldBe 1 + } + + @Test + fun `should return null if no element matches the filter`() { + listOf(1, 2).uniqueOrNull { it < 1 }.shouldBeNull() + } + + @Test + fun `should return null if multiple elements match the filter`() { + listOf(1, 2).uniqueOrNull { true }.shouldBeNull() + } + } + + @Nested + inner class NullIfEmptyElse { + + @Test + fun `should return null if the list is empty`() { + emptyList().nullIfEmptyElse { it }.shouldBeNull() + } + + @Test + fun `should call the initializer if the list is not empty`() { + listOf(1, 2, 3).nullIfEmptyElse { list -> list.map { it + 1 } }.shouldContainExactly(2, 3, 4) + } + } + + @Nested + inner class OuterZipBy { + + @Test + fun `should zip the two lists while padding the shorter list with nulls at the end (left shorter)`() { + val actualResult = outerZipBy(listOf(3), listOf(1, 2)) { a, b -> a to b } + actualResult.shouldContainExactly(3 to 1, null to 2) + } + + @Test + fun `should zip the two lists while padding the shorter list with nulls at the end (right shorter)`() { + val actualResult = outerZipBy(listOf(1, 2), listOf(3)) { a, b -> a to b } + actualResult.shouldContainExactly(1 to 3, 2 to null) + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/utils/IdManagerTest.kt b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/utils/IdManagerTest.kt new file mode 100644 index 000000000..24ad49c1d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/utils/IdManagerTest.kt @@ -0,0 +1,112 @@ +package de.unibonn.simpleml.utils + +import io.kotest.matchers.booleans.shouldBeFalse +import io.kotest.matchers.booleans.shouldBeTrue +import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +class IdManagerTest { + + class TestClass + + private lateinit var idManager: IdManager + + @BeforeEach + fun reset() { + idManager = IdManager() + } + + @Nested + inner class AssignIdIfAbsent { + + @Test + fun `should return a new unique ID for an unseen object`() { + val firstObject = TestClass() + val secondObject = TestClass() + + idManager.assignIdIfAbsent(firstObject) shouldNotBe idManager.assignIdIfAbsent(secondObject) + } + + @Test + fun `should return the same ID for the same object`() { + val obj = TestClass() + + idManager.assignIdIfAbsent(obj) shouldBe idManager.assignIdIfAbsent(obj) + } + } + + @Nested + inner class GetObjectById { + + @Test + fun `should return the object with the given ID if it exists`() { + val obj = TestClass() + val id = idManager.assignIdIfAbsent(obj) + + idManager.getObjectById(id) shouldBe obj + } + + @Test + fun `should return null if no object with the given ID exists`() { + idManager.getObjectById(Id(10)).shouldBeNull() + } + } + + @Nested + inner class KnowsObject { + + @Test + fun `should return whether an ID has been assigned to this object`() { + val firstObject = TestClass() + val secondObject = TestClass() + idManager.assignIdIfAbsent(firstObject) + + idManager.knowsObject(firstObject).shouldBeTrue() + idManager.knowsObject(secondObject).shouldBeFalse() + } + } + + @Nested + inner class KnowsId { + + @Test + fun `should return whether an ID is in use`() { + val obj = TestClass() + val id = idManager.assignIdIfAbsent(obj) + + idManager.knowsId(id).shouldBeTrue() + idManager.knowsId(Id(10)).shouldBeFalse() + } + } + + @Nested + inner class Reset { + + @Test + fun `should clear all mappings from object to ID`() { + val obj = TestClass() + val id = idManager.assignIdIfAbsent(obj) + + idManager.reset() + + idManager.knowsObject(obj).shouldBeFalse() + idManager.knowsId(id).shouldBeFalse() + } + + @Test + fun `should reset the counter`() { + val obj = TestClass() + val idBeforeClear = idManager.assignIdIfAbsent(obj) + + idManager.reset() + + val idAfterClear = idManager.assignIdIfAbsent(obj) + + idBeforeClear shouldBe idAfterClear + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/annotationCalls.smltest b/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/annotationCalls.smltest new file mode 100644 index 000000000..0f112e7b7 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/annotationCalls.smltest @@ -0,0 +1,12 @@ +package tests.astToPrologFactbase.annotationCalls + +annotation MyAnnotation + +@MyAnnotation +class MyClassWithSimpleAnnotationCall + +@MyAnnotation(1, 2) +class MyClassWithComplexAnnotationCall + +@MyUnresolvedAnnotation +class MyClassWithUnresolvedAnnotationCall diff --git a/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/declarations.smltest b/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/declarations.smltest new file mode 100644 index 000000000..d91202a50 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/declarations.smltest @@ -0,0 +1,73 @@ +package tests.astToPrologFactbase.declarations + +import myPackage.MyClass +import myPackage.MyOtherClass as Class +import myPackage.* + + +// Annotations --------------------------------------------------------------------------------------------------------- +annotation MySimpleAnnotation + +@MySimpleAnnotation +annotation MyComplexAnnotation( + a: Int, + b: Int +) where T1 super Number, T2 sub Number + + +// Classes, Attributes & Type parameters ------------------------------------------------------------------------------- +class MySimpleClass + +@MySimpleAnnotation +class MyComplexClass + + (a, b) + sub SuperClass1, SuperClass2 + where T1 super Number, T2 sub Number +{ + attr mySimpleAttribute + @AnnotationUse static attr myComplexAttribute: Int + + class MyClass + enum MyEnum + static fun myStaticMethod() +} + + +// Enums & Enum variants ----------------------------------------------------------------------------------------------- +enum MySimpleEnum + +@MySimpleAnnotation +enum MyComplexEnum { + MySimpleVariant + @AnnotationUse MyComplexVariant(a, b) where T1 super Number, T2 sub Number +} + + +// Functions, Parameters & Results ------------------------------------------------------------------------------------- +fun mySimpleFunction() + +@MySimpleAnnotation +fun myComplexFunction + + (mySimpleParameter, @AnnotationUse vararg myComplexParameter: Int = 3) + -> (mySimpleResult, @AnnotationUse myComplexResult: Int) + where T1 super Number, T2 sub Number + + +// Steps --------------------------------------------------------------------------------------------------------------- +step mySimpleStep() {} + +@MySimpleAnnotation +private step myComplexStep (a, b) -> (a, b) { + val a = 1; +} + + +// Workflows ----------------------------------------------------------------------------------------------------------- +workflow mySimpleWorkflow {} + +@MySimpleAnnotation +workflow myComplexWorkflow { + val a = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/empty.smltest b/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/empty.smltest new file mode 100644 index 000000000..e69de29bb diff --git a/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/expressions.smltest b/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/expressions.smltest new file mode 100644 index 000000000..1ca2c51cc --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/expressions.smltest @@ -0,0 +1,98 @@ +package tests.astToPrologFactbase.expressions + +// Arguments ----------------------------------------------------------------------------------------------------------- +fun f(a: Int) + +workflow myWorkflowWithPositionalArgument { + f(0); +} + +workflow myWorkflowWithResolvableNamedArgument { + f(a = 1); +} + +workflow myWorkflowWithUnresolvedArgument { + f(myUnresolvedParameter = 2); +} + +// Block lambdas ------------------------------------------------------------------------------------------------------- +workflow myWorkflowWithSimpleBlockLambda { + val simpleBlockLambda = () {}; +} + +workflow myWorkflowWithComplexBlockLambda { + val complexBlockLambda = (a, b) { + 1; + 2; + }; +} + + +// Calls --------------------------------------------------------------------------------------------------------------- +workflow myWorkflowWithSimpleCall { + val simpleCall = f(); +} + +workflow myWorkflowWithComplexCall { + val complexCall = f(1, 2); +} + + +// Expression lambdas -------------------------------------------------------------------------------------------------- +workflow myWorkflowWithSimpleExpressionLambda { + val simpleExpressionLambda = () -> 1; +} + +workflow myWorkflowWithComplexExpressionLambda { + val complexExpressionLambda = (a, b) -> 1; +} + +// Indexed accesses ---------------------------------------------------------------------------------------------------- +workflow myWorkflowWithIndexedAccess { + val indexedAccess = C[1]; +} + +// Literals ------------------------------------------------------------------------------------------------------------ +workflow myWorkflowWithLiterals { + val booleanLiteral = true; + val floatLiteral = 1.0; + val intLiteral = 42; + val nullLiteral = null; + val stringLiteral = "bla"; +} + + +// Member accesses ----------------------------------------------------------------------------------------------------- +workflow myWorkflowWithMemberAccess { + val memberAccess = C?.f; +} + + +// Operations ---------------------------------------------------------------------------------------------------------- +workflow myWorkflowWithOperations { + val infixOperation = 1 + 2; + val prefixOperation = -1; +} + + +// Parenthesized expression -------------------------------------------------------------------------------------------- +workflow myWorkflowWithParenthesizedExpression { + val parenthesizedExpression = (1); +} + + +// Template strings ---------------------------------------------------------------------------------------------------- +workflow myWorkflowWithTemplateString { + "start {{ 1 }} inner {{ 1 }} end"; +} + + +// References ---------------------------------------------------------------------------------------------------------- +workflow myWorkflowWithResolvableReference { + val a = 1; + a; +} + +workflow myWorkflowWithUnresolvableReference { + myUnresolvedDeclaration; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/protocols.smltest b/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/protocols.smltest new file mode 100644 index 000000000..04524b9b2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/protocols.smltest @@ -0,0 +1,23 @@ +package tests.astToPrologFactbase.protocols + +class MyClassWithSimpleProtocol { + protocol {} +} + +class MyClassWithComplexProtocol { + attr member: Int + + protocol { + subterm alternative = . | .; + subterm simpleComplement = [^]; + subterm complexComplement = [\a^ member member]; + subterm parenthesizedTerm = (.); + subterm quantifiedTerm = .?; + subterm reference = member; + subterm unresolvedReference = unresolved; + subterm sequence = . .; + subterm tokenClass = \a; + + . + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/statements.smltest b/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/statements.smltest new file mode 100644 index 000000000..9245122ed --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/statements.smltest @@ -0,0 +1,21 @@ +package tests.astToPrologFactbase.statements + +annotation MySimpleAnnotation + +step myFunctionalStep() -> a { + + // Assignment with placeholder, wildcard, and yield + val mySimplePlaceholder, val myComplexPlaceholder, _, yield a = f(); + + // Expression statement + () { + + // Lambda result + yield mySimpleLambdaResult = 1; + yield myComplexLambdaResult = 1; + }; +} + +step myStepWithUnresolvedYield() { + yield myUnresolvedResult = f(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/types.smltest b/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/types.smltest new file mode 100644 index 000000000..2aec0beab --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/astToPrologFactbase/types.smltest @@ -0,0 +1,66 @@ +package tests.astToPrologFactbase.types + +// Callable types ------------------------------------------------------------------------------------------------------ +step myWorkflowStepWithSimpleCallableType(a: () -> ()) {} + +step myWorkflowStepWithComplexCallableType(a: (o, p) -> (s, r)) {} + + +// Member types -------------------------------------------------------------------------------------------------------- +step myWorkflowStepWithMemberType(a: A.B) {} + + +// Named types --------------------------------------------------------------------------------------------------------- +step myWorkflowStepWithSimpleResolvableNamedType(a: C) {} + +step myWorkflowStepWithComplexResolvableNamedType(a: C<*>?) {} + +step myWorkflowWithUnresolvableNamedType(a: MyUnresolvedDeclaration) {} + + +// Parenthesized expression -------------------------------------------------------------------------------------------- +step myWorkflowStepWithParenthesizedType(a: (C)) {} + + +// Star projections ---------------------------------------------------------------------------------------------------- +workflow myWorkflowWithStarProjection { + val starProjection = C<*>(); +} + + +// Type arguments ------------------------------------------------------------------------------------------------------ +class C + +workflow myWorkflowWithPositionalTypeArgument { + C<*>(); +} + +workflow myWorkflowWithResolvableNamedTypeArgument { + C(); +} + +workflow myWorkflowWithUnresolvedTypeArgument { + C(); +} + + +// Type parameter constraints ------------------------------------------------------------------------------------------ +fun myFunctionWithResolvableTypeParameterConstraint() where T sub Number + +fun myFunctionWithUnresolvableTypeParameterConstraint() where MY_UNRESOLVED_TYPE_PARAMETER sub Number + + +// Type projections ---------------------------------------------------------------------------------------------------- +workflow myWorkflowWithSimpleTypeProjection { + C(); +} + +workflow myWorkflowWithComplexTypeProjection { + C(); +} + + +// Union types --------------------------------------------------------------------------------------------------------- +step myWorkflowStepWithSimpleUnionType(a: union<>) {} + +step myWorkflowStepWithComplexUnionType(a: union) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/conversion/idValueConverter.smltest b/DSL/de.unibonn.simpleml/src/test/resources/conversion/idValueConverter.smltest new file mode 100644 index 000000000..cb379ba8b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/conversion/idValueConverter.smltest @@ -0,0 +1 @@ +class `class` diff --git a/DSL/de.unibonn.simpleml/src/test/resources/conversion/intValueConverter.smltest b/DSL/de.unibonn.simpleml/src/test/resources/conversion/intValueConverter.smltest new file mode 100644 index 000000000..b4f94107f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/conversion/intValueConverter.smltest @@ -0,0 +1,3 @@ +workflow templateStringParts { + 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/conversion/qualifiedNameValueConverter.smltest b/DSL/de.unibonn.simpleml/src/test/resources/conversion/qualifiedNameValueConverter.smltest new file mode 100644 index 000000000..18db65582 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/conversion/qualifiedNameValueConverter.smltest @@ -0,0 +1,3 @@ +package simpleml.`package` + +import simpleml.`package`.* diff --git a/DSL/de.unibonn.simpleml/src/test/resources/conversion/stringValueConverter.smltest b/DSL/de.unibonn.simpleml/src/test/resources/conversion/stringValueConverter.smltest new file mode 100644 index 000000000..9b5052a29 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/conversion/stringValueConverter.smltest @@ -0,0 +1,15 @@ +workflow escapedOpeningBrace { + "\{"; +} + +workflow unescapedOpeningBrace { + "{"; +} + +workflow escapedSingleQuote { + "\'"; +} + +workflow unescapedSingleQuote { + "'"; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/conversion/templateStringPartValueConverter.smltest b/DSL/de.unibonn.simpleml/src/test/resources/conversion/templateStringPartValueConverter.smltest new file mode 100644 index 000000000..e771e1f66 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/conversion/templateStringPartValueConverter.smltest @@ -0,0 +1,3 @@ +workflow templateStringParts { + "start{{ 1 }}inner{{ 1 }}end"; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/compilationUnits/empty.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/compilationUnits/empty.smltest new file mode 100644 index 000000000..8ca91d28a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/compilationUnits/empty.smltest @@ -0,0 +1,2 @@ + +// ----------------------------------------------------------------------------- diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/compilationUnits/withoutPackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/compilationUnits/withoutPackage.smltest new file mode 100644 index 000000000..d559558bc --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/compilationUnits/withoutPackage.smltest @@ -0,0 +1,14 @@ + workflow w1 { } workflow w2 {} +workflow w3 { + + + +} + +// ----------------------------------------------------------------------------- + +workflow w1 {} + +workflow w2 {} + +workflow w3 {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/annotation.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/annotation.smltest new file mode 100644 index 000000000..f1336c321 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/annotation.smltest @@ -0,0 +1,19 @@ + annotation MyAnnotation1 + + @Annotation1 @Annotation2 + annotation MyAnnotation2 + ( @AnnotationUse a : Int + , vararg b : Int + = 3 ) + + where T2 super +Number , +T3 sub Number + +// ----------------------------------------------------------------------------- + +annotation MyAnnotation1 + +@Annotation1 +@Annotation2 +annotation MyAnnotation2(@AnnotationUse a: Int, vararg b: Int = 3) where T2 super Number, T3 sub Number diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/annotationCall on compilation unit.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/annotationCall on compilation unit.smltest new file mode 100644 index 000000000..80e8ad82d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/annotationCall on compilation unit.smltest @@ -0,0 +1,8 @@ +@ AnnotationUse ( 1 , b = 2 ) +class MyClass + +// ----------------------------------------------------------------------------- + +@AnnotationUse(1, b = 2) + +class MyClass diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/annotationCall on declaration.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/annotationCall on declaration.smltest new file mode 100644 index 000000000..e3bb9b6de --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/annotationCall on declaration.smltest @@ -0,0 +1,11 @@ +package test + +@ AnnotationUse ( 1 , b = 2 ) +class MyClass + +// ----------------------------------------------------------------------------- + +package test + +@AnnotationUse(1, b = 2) +class MyClass diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/attribute.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/attribute.smltest new file mode 100644 index 000000000..4256969b0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/attribute.smltest @@ -0,0 +1,32 @@ +class MyClass { + attr myAttribute1 + + @Annotation1 + @Annotation2 + attr + myAttribute2 : Int + + static + attr myAttribute3 : Int + + @Annotation1 + @Annotation2 + static + attr myAttribute4 : Int +} + +// ----------------------------------------------------------------------------- + +class MyClass { + attr myAttribute1 + + @Annotation1 + @Annotation2 + attr myAttribute2: Int + + static attr myAttribute3: Int + + @Annotation1 + @Annotation2 + static attr myAttribute4: Int +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/class.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/class.smltest new file mode 100644 index 000000000..fd1b5caa4 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/class.smltest @@ -0,0 +1,32 @@ + class + MyClass1 + + class MyClass2 { } + + @Annotation1 @Annotation2 class MyClass3 + < @AnnotationUse + T1 , in + T2 , out T3 > + ( + @AnnotationUse + a : + Int , vararg b : Int + = 3 ) sub SuperClass , SuperInterface + where T2 super Number , T3 sub + Number { + attr myAttribute1 attr myAttribute2 +} + +// ----------------------------------------------------------------------------- + +class MyClass1 + +class MyClass2 {} + +@Annotation1 +@Annotation2 +class MyClass3<@AnnotationUse T1, in T2, out T3>(@AnnotationUse a: Int, vararg b: Int = 3) sub SuperClass, SuperInterface where T2 super Number, T3 sub Number { + attr myAttribute1 + + attr myAttribute2 +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/enum.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/enum.smltest new file mode 100644 index 000000000..93ab3f6d8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/enum.smltest @@ -0,0 +1,24 @@ +enum myEnum1 + + + enum myEnum2 { + } + +@Annotation1 @Annotation2 enum + myEnum3 { MyVariant1 + MyVariant2 + } + +// ----------------------------------------------------------------------------- + +enum myEnum1 + +enum myEnum2 {} + +@Annotation1 +@Annotation2 +enum myEnum3 { + MyVariant1 + + MyVariant2 +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/enumVariant.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/enumVariant.smltest new file mode 100644 index 000000000..e4b192ee8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/enumVariant.smltest @@ -0,0 +1,33 @@ +enum myEnum { + MyVariant1 + + + + @Annotation1 @Annotation2 + MyVariant2 < + @AnnotationUse + + T1, in T2, out + T3 > + ( + + @AnnotationUse + a : + Int , vararg + + b: + Int = 3 + ) where T2 + super Number , T3 sub + Number +} + +// ----------------------------------------------------------------------------- + +enum myEnum { + MyVariant1 + + @Annotation1 + @Annotation2 + MyVariant2<@AnnotationUse T1, in T2, out T3>(@AnnotationUse a: Int, vararg b: Int = 3) where T2 super Number, T3 sub Number +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/function.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/function.smltest new file mode 100644 index 000000000..4a2aa8187 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/function.smltest @@ -0,0 +1,63 @@ +fun myFunction1 + ( ) + +@Annotation1 @Annotation2 fun + myFunction2 + < + @AnnotationUse + T1 , in + T2 , out T3 + >( @AnnotationUse + a : Int , vararg + b : Int = + 3 ) -> + ( + @AnnotationUse a : + Int , b : Int ) where + T2 super Number , T3 sub Number + +class MyClass { + fun + myFunction1 ( + + + ) @Annotation1 @Annotation2 + + fun + myFunction2 ( ) +-> + a : + Int + + + static + + fun myFunction3 ( ) @Annotation1 @Annotation2 static + + fun + myFunction4( + + ) +} + +// ----------------------------------------------------------------------------- + +fun myFunction1() + +@Annotation1 +@Annotation2 +fun myFunction2<@AnnotationUse T1, in T2, out T3>(@AnnotationUse a: Int, vararg b: Int = 3) -> (@AnnotationUse a: Int, b: Int) where T2 super Number, T3 sub Number + +class MyClass { + fun myFunction1() + + @Annotation1 + @Annotation2 + fun myFunction2() -> a: Int + + static fun myFunction3() + + @Annotation1 + @Annotation2 + static fun myFunction4() +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/import.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/import.smltest new file mode 100644 index 000000000..a66318b16 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/import.smltest @@ -0,0 +1,14 @@ +import tests . formattting as format import tests.formatting2 as format2 +import tests.formatting3 as format3 + +import tests . formatting4 import tests.formatting5 +import tests.formatting6 + +// ----------------------------------------------------------------------------- + +import tests.formattting as format +import tests.formatting2 as format2 +import tests.formatting3 as format3 +import tests.formatting4 +import tests.formatting5 +import tests.formatting6 diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/annotationUsesWithImports.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/annotationUsesWithImports.smltest new file mode 100644 index 000000000..db5683ea4 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/annotationUsesWithImports.smltest @@ -0,0 +1,11 @@ + @AnnotationUse1 + + @AnnotationUse2 + import myPackage . MyClass + +// ----------------------------------------------------------------------------- + +@AnnotationUse1 +@AnnotationUse2 + +import myPackage.MyClass diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/annotationUsesWithName.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/annotationUsesWithName.smltest new file mode 100644 index 000000000..21862115d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/annotationUsesWithName.smltest @@ -0,0 +1,11 @@ + @AnnotationUse1 + + @AnnotationUse2 + package tests . formatting + +// ----------------------------------------------------------------------------- + +@AnnotationUse1 +@AnnotationUse2 + +package tests.formatting diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/full.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/full.smltest new file mode 100644 index 000000000..fc9e1baab --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/full.smltest @@ -0,0 +1,36 @@ + + @AnnotationUse1 + + + @AnnotationUse2 + + package tests . formatting + + + import tests . formatting1 import tests.formatting2 +import tests.formatting3 + + + workflow w1 { } workflow w2 {} +workflow w3 { + + + +} + +// ----------------------------------------------------------------------------- + +@AnnotationUse1 +@AnnotationUse2 + +package tests.formatting + +import tests.formatting1 +import tests.formatting2 +import tests.formatting3 + +workflow w1 {} + +workflow w2 {} + +workflow w3 {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/importsAndMembers.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/importsAndMembers.smltest new file mode 100644 index 000000000..cad33af77 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/importsAndMembers.smltest @@ -0,0 +1,22 @@ + import tests . formatting1 import tests.formatting2 +import tests.formatting3 + + + workflow w1 { } workflow w2 {} +workflow w3 { + + + +} + +// ----------------------------------------------------------------------------- + +import tests.formatting1 +import tests.formatting2 +import tests.formatting3 + +workflow w1 {} + +workflow w2 {} + +workflow w3 {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/nameAndMembers.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/nameAndMembers.smltest new file mode 100644 index 000000000..aa2913cc0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/packages/nameAndMembers.smltest @@ -0,0 +1,19 @@ + package tests . formatting + + + workflow w1 { } workflow w2 {} +workflow w3 { + + + +} + +// ----------------------------------------------------------------------------- + +package tests.formatting + +workflow w1 {} + +workflow w2 {} + +workflow w3 {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/alternative.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/alternative.smltest new file mode 100644 index 000000000..a68c91ad3 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/alternative.smltest @@ -0,0 +1,16 @@ +class C { + protocol { + f|g +| + + ( h ) + } +} + +// ----------------------------------------------------------------------------- + +class C { + protocol { + f | g | (h) + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/body.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/body.smltest new file mode 100644 index 000000000..ec1233ea5 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/body.smltest @@ -0,0 +1,19 @@ +class C { + protocol { + subterm term1 = f; subterm term2 = g; +subterm term3 = h; + f g + } +} + +// ----------------------------------------------------------------------------- + +class C { + protocol { + subterm term1 = f; + subterm term2 = g; + subterm term3 = h; + + f g + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/complement.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/complement.smltest new file mode 100644 index 000000000..940e97999 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/complement.smltest @@ -0,0 +1,22 @@ +class C { + protocol { + [ ^ + ] [ ^ f g + +] [ . ^ f + h ] [ \a ^ + a b ] [ \f +^ + f + h +] + } +} + +// ----------------------------------------------------------------------------- + +class C { + protocol { + [^] [^ f g] [.^ f h] [\a^ a b] [\f^ f h] + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/empty.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/empty.smltest new file mode 100644 index 000000000..c154c9804 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/empty.smltest @@ -0,0 +1,13 @@ +class C { + protocol { + + + + } +} + +// ----------------------------------------------------------------------------- + +class C { + protocol {} +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/full.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/full.smltest new file mode 100644 index 000000000..16cbdf4ab --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/full.smltest @@ -0,0 +1,30 @@ +class C { + protocol { + subterm + + term1 + = f ; subterm term2 + = g + ; + f + ( + + ( + g h +) | ( + i j + ) ) ? [ . ^ + k +] +} } + +// ----------------------------------------------------------------------------- + +class C { + protocol { + subterm term1 = f; + subterm term2 = g; + + f ((g h) | (i j))? [.^ k] + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/parenthesizedTerm.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/parenthesizedTerm.smltest new file mode 100644 index 000000000..9b4fda027 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/parenthesizedTerm.smltest @@ -0,0 +1,15 @@ +class C { + protocol { + ( f + g + ) + } +} + +// ----------------------------------------------------------------------------- + +class C { + protocol { + (f g) + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/protocols and members in one class.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/protocols and members in one class.smltest new file mode 100644 index 000000000..bcf984405 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/protocols and members in one class.smltest @@ -0,0 +1,24 @@ +class C { + attr a: Int protocol + { + + } fun f() + + protocol + + { + +} +} + +// ----------------------------------------------------------------------------- + +class C { + attr a: Int + + protocol {} + + fun f() + + protocol {} +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/quantifiedTerm.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/quantifiedTerm.smltest new file mode 100644 index 000000000..9142767f9 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/quantifiedTerm.smltest @@ -0,0 +1,13 @@ +class C { + protocol { + . ? . * ( f ) + + } +} + +// ----------------------------------------------------------------------------- + +class C { + protocol { + .? .* (f)+ + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/sequence.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/sequence.smltest new file mode 100644 index 000000000..5de7616ef --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/sequence.smltest @@ -0,0 +1,14 @@ +class C { + protocol { + f + g ( h ) + } +} + +// ----------------------------------------------------------------------------- + +class C { + protocol { + f g (h) + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/subterm.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/subterm.smltest new file mode 100644 index 000000000..d0da82f42 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/subterm.smltest @@ -0,0 +1,16 @@ +class C { + protocol { + subterm + + term1 + = f; + } +} + +// ----------------------------------------------------------------------------- + +class C { + protocol { + subterm term1 = f; + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/subtermList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/subtermList.smltest new file mode 100644 index 000000000..335553c53 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/protocols/subtermList.smltest @@ -0,0 +1,19 @@ +class C { + protocol { + subterm term1 = f; subterm term2 = g; + + + subterm term3 = h; + + } +} + +// ----------------------------------------------------------------------------- + +class C { + protocol { + subterm term1 = f; + subterm term2 = g; + subterm term3 = h; + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/step.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/step.smltest new file mode 100644 index 000000000..828c556ce --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/step.smltest @@ -0,0 +1,29 @@ +step myStep1 ( + +) { } + +internal step myStep2 ( ) { } + +@Annotation1 + @Annotation2 private step + myStep3 + ( a : Int , b : Int + + ) + -> ( + a: + Int , b : Int ) + { + val a = 1; } + +// ----------------------------------------------------------------------------- + +step myStep1() {} + +internal step myStep2() {} + +@Annotation1 +@Annotation2 +private step myStep3(a: Int, b: Int) -> (a: Int, b: Int) { + val a = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/workflow.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/workflow.smltest new file mode 100644 index 000000000..9f771b1b6 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/declarations/workflow.smltest @@ -0,0 +1,17 @@ + workflow myWorkflow1 {} + + + + @Annotation1 @Annotation2 workflow myWorkflow2 { + val a = 1; + } + +// ----------------------------------------------------------------------------- + +workflow myWorkflow1 {} + +@Annotation1 +@Annotation2 +workflow myWorkflow2 { + val a = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/blockLambda.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/blockLambda.smltest new file mode 100644 index 000000000..ca0fc2ce2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/blockLambda.smltest @@ -0,0 +1,19 @@ +workflow myWorkflow { + () { + + }; + ( a + , vararg b + = 1 ) + { + val a = 1; }; +} + +// ----------------------------------------------------------------------------- + +workflow myWorkflow { + () {}; + (a, vararg b = 1) { + val a = 1; + }; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/call.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/call.smltest new file mode 100644 index 000000000..f2808fbd6 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/call.smltest @@ -0,0 +1,16 @@ +workflow myWorkflow { + f ( ) ; + f < * , + in Number , out + Number , T = + Number > ( 1 + , b = 2 + ); +} + +// ----------------------------------------------------------------------------- + +workflow myWorkflow { + f(); + f<*, in Number, out Number, T = Number>(1, b = 2); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/expressionLambda.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/expressionLambda.smltest new file mode 100644 index 000000000..93893d316 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/expressionLambda.smltest @@ -0,0 +1,18 @@ +workflow myWorkflow { + () -> + + 1; + ( a + , + vararg b = + 1 ) + -> + 1; +} + +// ----------------------------------------------------------------------------- + +workflow myWorkflow { + () -> 1; + (a, vararg b = 1) -> 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/indexedAccess.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/indexedAccess.smltest new file mode 100644 index 000000000..56b1af0ef --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/indexedAccess.smltest @@ -0,0 +1,9 @@ +workflow myWorkflow { + a [ 1 ]; +} + +// ----------------------------------------------------------------------------- + +workflow myWorkflow { + a[1]; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/infixOperations.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/infixOperations.smltest new file mode 100644 index 000000000..a454c38aa --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/infixOperations.smltest @@ -0,0 +1,9 @@ +workflow myWorkflow { + true and false; +} + +// ----------------------------------------------------------------------------- + +workflow myWorkflow { + true and false; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/memberAccess.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/memberAccess.smltest new file mode 100644 index 000000000..a741edf54 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/memberAccess.smltest @@ -0,0 +1,11 @@ +workflow myWorkflow { + a . member; + a ? . member; +} + +// ----------------------------------------------------------------------------- + +workflow myWorkflow { + a.member; + a?.member; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/parenthesizedExpression.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/parenthesizedExpression.smltest new file mode 100644 index 000000000..162cbbf56 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/parenthesizedExpression.smltest @@ -0,0 +1,19 @@ +workflow w { + ( 1 + ); + ( true ) or false; + not ( true ); + ( f + )(( 3 )); + ( c ).member; +} + +// ----------------------------------------------------------------------------- + +workflow w { + (1); + (true) or false; + not (true); + (f)((3)); + (c).member; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/prefixOperations.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/prefixOperations.smltest new file mode 100644 index 000000000..b6023f950 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/prefixOperations.smltest @@ -0,0 +1,11 @@ +workflow myWorkflow { + not true and not false; + - 1; +} + +// ----------------------------------------------------------------------------- + +workflow myWorkflow { + not true and not false; + -1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/templateString.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/templateString.smltest new file mode 100644 index 000000000..09405ee62 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/expressions/templateString.smltest @@ -0,0 +1,13 @@ +workflow myWorkflow { + "template {{ + + a + + }}"; +} + +// ----------------------------------------------------------------------------- + +workflow myWorkflow { + "template {{ a }}"; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/statements/assignment.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/statements/assignment.smltest new file mode 100644 index 000000000..030958ea0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/statements/assignment.smltest @@ -0,0 +1,25 @@ +workflow myWorkflow { + val + a = 0 ; + val a , _ , yield b = call() ; + () { + val a + = 0 ; + + + + val a, _ , yield b = + call() ; + }; +} + +// ----------------------------------------------------------------------------- + +workflow myWorkflow { + val a = 0; + val a, _, yield b = call(); + () { + val a = 0; + val a, _, yield b = call(); + }; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/statements/blocks.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/statements/blocks.smltest new file mode 100644 index 000000000..9ad934880 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/statements/blocks.smltest @@ -0,0 +1,12 @@ +workflow myWorkflow { +val a = 1; val b = 2; + val c = 54; + } + +// ----------------------------------------------------------------------------- + +workflow myWorkflow { + val a = 1; + val b = 2; + val c = 54; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/statements/expressionStatement.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/statements/expressionStatement.smltest new file mode 100644 index 000000000..eb6527a4c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/statements/expressionStatement.smltest @@ -0,0 +1,16 @@ +workflow myWorkflow { + call() + ; + () { + call() ; + }; +} + +// ----------------------------------------------------------------------------- + +workflow myWorkflow { + call(); + () { + call(); + }; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/callableType.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/callableType.smltest new file mode 100644 index 000000000..0794a3c78 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/callableType.smltest @@ -0,0 +1,20 @@ +step s1(f: ( ) + -> ( )) {} + +step s2(f: ( ) -> +result : Int ) {} + +step s3(f: + ( @AnnotationUse a + : Int , vararg b : + Int = 3 ) -> ( + @AnnotationUse a + : Int , b : Int )) {} + +// ----------------------------------------------------------------------------- + +step s1(f: () -> ()) {} + +step s2(f: () -> result: Int) {} + +step s3(f: (@AnnotationUse a: Int, vararg b: Int = 3) -> (@AnnotationUse a: Int, b: Int)) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/memberType.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/memberType.smltest new file mode 100644 index 000000000..3ad489e72 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/memberType.smltest @@ -0,0 +1,11 @@ +step s1(f: OuterClass . InnerClass) {} + +step s2(f: OuterClass ? +. MiddleClass < C , + D > ?) {} + +// ----------------------------------------------------------------------------- + +step s1(f: OuterClass.InnerClass) {} + +step s2(f: OuterClass?.MiddleClass?) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/namedType.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/namedType.smltest new file mode 100644 index 000000000..224d61798 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/namedType.smltest @@ -0,0 +1,12 @@ +step s1(f: Int) {} + +step s2(f: Int < * + , in Number + , out Number , T = + Number > ? ) {} + +// ----------------------------------------------------------------------------- + +step s1(f: Int) {} + +step s2(f: Int<*, in Number, out Number, T = Number>?) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/parenthesizedType.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/parenthesizedType.smltest new file mode 100644 index 000000000..47869a23e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/parenthesizedType.smltest @@ -0,0 +1,15 @@ +class C sub ( Int ) where T sub ( Int ) { + attr a: ( Int ) +} + +step s(p1: ( Int +), p2: union<( Int )>) -> r: ( + Int ) {} + +// ----------------------------------------------------------------------------- + +class C sub (Int) where T sub (Int) { + attr a: (Int) +} + +step s(p1: (Int), p2: union<(Int)>) -> r: (Int) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/unionType.smltest b/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/unionType.smltest new file mode 100644 index 000000000..a4553e121 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/formatting/types/unionType.smltest @@ -0,0 +1,11 @@ +step s1(f: union < >) {} + +step s2(f: union < + Int , + Float >) {} + +// ----------------------------------------------------------------------------- + +step s1(f: union<>) {} + +step s2(f: union) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty step/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty step/input.smltest new file mode 100644 index 000000000..08ac85c3e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty step/input.smltest @@ -0,0 +1,3 @@ +package tests.generator.emptyStep + +step test() {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty step/output/tests/generator/emptyStep/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty step/output/tests/generator/emptyStep/gen_input.py new file mode 100644 index 000000000..7a62d7d3c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty step/output/tests/generator/emptyStep/gen_input.py @@ -0,0 +1,4 @@ +# Steps ------------------------------------------------------------------------ + +def test(): + pass diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty workflow/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty workflow/input.smltest new file mode 100644 index 000000000..3b0c48cd5 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty workflow/input.smltest @@ -0,0 +1,3 @@ +package tests.generator.emptyWorkflow + +workflow test {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty workflow/output/tests/generator/emptyWorkflow/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty workflow/output/tests/generator/emptyWorkflow/gen_input.py new file mode 100644 index 000000000..7d1be0834 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty workflow/output/tests/generator/emptyWorkflow/gen_input.py @@ -0,0 +1,4 @@ +# Workflows -------------------------------------------------------------------- + +def test(): + pass diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty workflow/output/tests/generator/emptyWorkflow/gen_input_test.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty workflow/output/tests/generator/emptyWorkflow/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/empty workflow/output/tests/generator/emptyWorkflow/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/parameter with python name/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/parameter with python name/input.smltest new file mode 100644 index 000000000..67311a1fe --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/parameter with python name/input.smltest @@ -0,0 +1,14 @@ +package tests.generator.parameterWithPythonName + +fun f1(param: (a: Int, b: Int, c: Int) -> r: Int) +fun f2(param: (a: Int, b: Int, c: Int) -> ()) + +step test1(param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_3") param3: Int = 0) { + f1((param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_3") param3: Int = 0) -> 1); + f2((param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_3") param3: Int = 0) {}); +} + +step test2(param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_4") vararg param4: Int) { + f1((param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_4") vararg param4: Int) -> 1); + f2((param1: Int, @PythonName("param_2") param2: Int, @PythonName("param_4") vararg param4: Int) {}); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py new file mode 100644 index 000000000..5a04fef8f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/parameter with python name/output/tests/generator/parameterWithPythonName/gen_input.py @@ -0,0 +1,13 @@ +# Steps ------------------------------------------------------------------------ + +def test1(param1, param_2, param_3=0): + f1(lambda param1, param_2, param_3=0: 1) + def __block_lambda_0(param1, param_2, param_3=0): + pass + f2(__block_lambda_0) + +def test2(param1, param_2, *param_4): + f1(lambda param1, param_2, *param_4: 1) + def __block_lambda_0(param1, param_2, *param_4): + pass + f2(__block_lambda_0) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/step with python name/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/step with python name/input.smltest new file mode 100644 index 000000000..52ec22bfb --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/step with python name/input.smltest @@ -0,0 +1,8 @@ +package tests.generator.stepWithPythonName + +fun f() + +@PythonName("test_step") +step testStep() { + f(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/step with python name/output/tests/generator/stepWithPythonName/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/step with python name/output/tests/generator/stepWithPythonName/gen_input.py new file mode 100644 index 000000000..26cbaeb4d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/step with python name/output/tests/generator/stepWithPythonName/gen_input.py @@ -0,0 +1,4 @@ +# Steps ------------------------------------------------------------------------ + +def test_step(): + f() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two steps/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two steps/input.smltest new file mode 100644 index 000000000..024d67dbe --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two steps/input.smltest @@ -0,0 +1,11 @@ +package tests.generator.twoSteps + +fun f() + +step test1(a: Int, b: Int = 0) { + f(); +} + +step test2(a: Int, vararg c: Int) { + f(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two steps/output/tests/generator/twoSteps/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two steps/output/tests/generator/twoSteps/gen_input.py new file mode 100644 index 000000000..bb0c1f51b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two steps/output/tests/generator/twoSteps/gen_input.py @@ -0,0 +1,7 @@ +# Steps ------------------------------------------------------------------------ + +def test1(a, b=0): + f() + +def test2(a, *c): + f() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two workflows/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two workflows/input.smltest new file mode 100644 index 000000000..549e8ab91 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two workflows/input.smltest @@ -0,0 +1,11 @@ +package tests.generator.twoWorkflows + +fun f() + +workflow test1 { + f(); +} + +workflow test2 { + f(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two workflows/output/tests/generator/twoWorkflows/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two workflows/output/tests/generator/twoWorkflows/gen_input.py new file mode 100644 index 000000000..cb607a890 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two workflows/output/tests/generator/twoWorkflows/gen_input.py @@ -0,0 +1,7 @@ +# Workflows -------------------------------------------------------------------- + +def test1(): + f() + +def test2(): + f() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two workflows/output/tests/generator/twoWorkflows/gen_input_test1.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two workflows/output/tests/generator/twoWorkflows/gen_input_test1.py new file mode 100644 index 000000000..d4cb15745 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two workflows/output/tests/generator/twoWorkflows/gen_input_test1.py @@ -0,0 +1,4 @@ +from gen_input import test1 + +if __name__ == '__main__': + test1() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two workflows/output/tests/generator/twoWorkflows/gen_input_test2.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two workflows/output/tests/generator/twoWorkflows/gen_input_test2.py new file mode 100644 index 000000000..96daa0217 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/two workflows/output/tests/generator/twoWorkflows/gen_input_test2.py @@ -0,0 +1,4 @@ +from gen_input import test2 + +if __name__ == '__main__': + test2() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/workflow with python name/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/workflow with python name/input.smltest new file mode 100644 index 000000000..3f19c3b2c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/workflow with python name/input.smltest @@ -0,0 +1,8 @@ +package tests.generator.workflowWithPythonName + +fun f() + +@PythonName("test_flow") +workflow testFlow { + f(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/workflow with python name/output/tests/generator/workflowWithPythonName/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/workflow with python name/output/tests/generator/workflowWithPythonName/gen_input.py new file mode 100644 index 000000000..b004b87c3 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/workflow with python name/output/tests/generator/workflowWithPythonName/gen_input.py @@ -0,0 +1,4 @@ +# Workflows -------------------------------------------------------------------- + +def test_flow(): + f() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/workflow with python name/output/tests/generator/workflowWithPythonName/gen_input_test_flow.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/workflow with python name/output/tests/generator/workflowWithPythonName/gen_input_test_flow.py new file mode 100644 index 000000000..b0c0b5509 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/declarations/workflow with python name/output/tests/generator/workflowWithPythonName/gen_input_test_flow.py @@ -0,0 +1,4 @@ +from gen_input import test_flow + +if __name__ == '__main__': + test_flow() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/block lambda/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/block lambda/input.smltest new file mode 100644 index 000000000..bf67c82cc --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/block lambda/input.smltest @@ -0,0 +1,17 @@ +package tests.generator.blockLambda + +fun f1(param: (a: Int, b: Int) -> r: Int) +fun f2(param: (a: Int, vararg b: Int) -> r: Int) +fun f3(param: () -> ()) + +fun g() -> a: Int + +workflow test { + f1((a: Int, b: Int = 2) { + yield d = g(); + }); + f2((a: Int, vararg c: Int) { + yield d = g(); + }); + f3(() {}); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py new file mode 100644 index 000000000..752a69875 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/block lambda/output/tests/generator/blockLambda/gen_input.py @@ -0,0 +1,14 @@ +# Workflows -------------------------------------------------------------------- + +def test(): + def __block_lambda_0(a, b=2): + d = g() + return d + f1(__block_lambda_0) + def __block_lambda_1(a, *c): + d = g() + return d + f2(__block_lambda_1) + def __block_lambda_2(): + pass + f3(__block_lambda_2) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/block lambda/output/tests/generator/blockLambda/gen_input_test.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/block lambda/output/tests/generator/blockLambda/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/block lambda/output/tests/generator/blockLambda/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/call/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/call/input.smltest new file mode 100644 index 000000000..64cc81c5f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/call/input.smltest @@ -0,0 +1,32 @@ +package tests.generator.call + +fun f(param: Any?) + +fun g1( + param1: Int, + param2: Int = 0 +) -> result: Boolean + +fun g2( + param1: Int, + vararg param3: Int +) -> result: Boolean + +fun h1( + @PythonName("param_1") param1: Int, + @PythonName("param_2") param2: Int = 0 +) -> result: Boolean + +fun h2( + @PythonName("param_1") param1: Int, + @PythonName("param_3") vararg param3: Int +) -> result: Boolean + +workflow test { + f((g1(1, 2))); + f((g1(param2 = 1, param1 = 2))); + f((g2(2, 3, 4))); + f((h1(1, 2))); + f((h1(param2 = 1, param1 = 2))); + f((h2(2, 3, 4))); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/call/output/tests/generator/call/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/call/output/tests/generator/call/gen_input.py new file mode 100644 index 000000000..244119b03 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/call/output/tests/generator/call/gen_input.py @@ -0,0 +1,9 @@ +# Workflows -------------------------------------------------------------------- + +def test(): + f(g1(1, param2=2)) + f(g1(2, param2=1)) + f(g2(2, 3, 4)) + f(h1(1, param_2=2)) + f(h1(2, param_2=1)) + f(h2(2, 3, 4)) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/call/output/tests/generator/call/gen_input_test.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/call/output/tests/generator/call/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/call/output/tests/generator/call/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/constant/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/constant/input.smltest new file mode 100644 index 000000000..6a73ef04f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/constant/input.smltest @@ -0,0 +1,11 @@ +package tests.generator.constant + +fun f(param: Any?) + +workflow test { + f(1 < 2); + f(1.0 - 1.0); + f(1 + 1); + f(null); + f("person: {{ "me" }}"); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/constant/output/tests/generator/constant/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/constant/output/tests/generator/constant/gen_input.py new file mode 100644 index 000000000..9ce7d5b2b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/constant/output/tests/generator/constant/gen_input.py @@ -0,0 +1,8 @@ +# Workflows -------------------------------------------------------------------- + +def test(): + f(True) + f(0.0) + f(2) + f(None) + f('person: me') diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/constant/output/tests/generator/constant/gen_input_test.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/constant/output/tests/generator/constant/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/constant/output/tests/generator/constant/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/enum variant call/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/enum variant call/input.smltest new file mode 100644 index 000000000..6cce0c02a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/enum variant call/input.smltest @@ -0,0 +1,16 @@ +// Related to https://github.com/lars-reimann/Simple-ML/issues/118 + +package tests.generator.enumVariantCall + +fun f(p: Any) + +enum MyEnum { + Variant1 + Variant2(counter: Int) +} + +workflow test { + f(MyEnum.Variant1); + f(MyEnum.Variant1()); + f(MyEnum.Variant2(1)); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py new file mode 100644 index 000000000..e3a57e25f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input.py @@ -0,0 +1,6 @@ +# Workflows -------------------------------------------------------------------- + +def test(): + f(MyEnum.Variant1()) + f(MyEnum.Variant1()) + f(MyEnum.Variant2(1)) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input_test.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/enum variant call/output/tests/generator/enumVariantCall/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/expression lambda/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/expression lambda/input.smltest new file mode 100644 index 000000000..74e7a5796 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/expression lambda/input.smltest @@ -0,0 +1,9 @@ +package tests.generator.expressionLambda + +fun f1(param: (a: Int, b: Int) -> r: Int) +fun f2(param: (a: Int, vararg c: Int) -> r: Int) + +workflow test { + f1((a, b = 2) -> 1); + f2((a, vararg c) -> 1); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input.py new file mode 100644 index 000000000..3314a97b2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input.py @@ -0,0 +1,5 @@ +# Workflows -------------------------------------------------------------------- + +def test(): + f1(lambda a, b=2: 1) + f2(lambda a, *c: 1) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input_test.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/expression lambda/output/tests/generator/expressionLambda/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/indexed access/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/indexed access/input.smltest new file mode 100644 index 000000000..1c4b9fca0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/indexed access/input.smltest @@ -0,0 +1,7 @@ +package tests.generator.indexedAccess + +fun f(param: Any?) + +step test(vararg params: Int) { + f(params[0]); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/indexed access/output/tests/generator/indexedAccess/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/indexed access/output/tests/generator/indexedAccess/gen_input.py new file mode 100644 index 000000000..2673de5a6 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/indexed access/output/tests/generator/indexedAccess/gen_input.py @@ -0,0 +1,4 @@ +# Steps ------------------------------------------------------------------------ + +def test(*params): + f(params[0]) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/infix operation/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/infix operation/input.smltest new file mode 100644 index 000000000..b21898bde --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/infix operation/input.smltest @@ -0,0 +1,31 @@ +package tests.generator.infixOperation + +fun f(param: Any?) + +fun g() -> result: Boolean + +fun h() -> result: Int + +fun i() -> result: Int? + +workflow test { + f(g() or g()); + f(g() and g()); + + f(h() == h()); + f(h() != h()); + f(h() === h()); + f(h() !== h()); + + f(h() < h()); + f(h() <= h()); + f(h() >= h()); + f(h() > h()); + + f(h() + h()); + f(h() - h()); + f(h() * h()); + f(h() / h()); + + f(i() ?: i()); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/infix operation/output/tests/generator/infixOperation/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/infix operation/output/tests/generator/infixOperation/gen_input.py new file mode 100644 index 000000000..2c58a2282 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/infix operation/output/tests/generator/infixOperation/gen_input.py @@ -0,0 +1,22 @@ +# Imports ---------------------------------------------------------------------- + +import simpleml.codegen + +# Workflows -------------------------------------------------------------------- + +def test(): + f(simpleml.codegen.eager_or(g(), g())) + f(simpleml.codegen.eager_and(g(), g())) + f((h()) == (h())) + f((h()) != (h())) + f((h()) is (h())) + f((h()) is not (h())) + f((h()) < (h())) + f((h()) <= (h())) + f((h()) >= (h())) + f((h()) > (h())) + f((h()) + (h())) + f((h()) - (h())) + f((h()) * (h())) + f((h()) / (h())) + f(simpleml.codegen.eager_elvis(i(), i())) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/infix operation/output/tests/generator/infixOperation/gen_input_test.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/infix operation/output/tests/generator/infixOperation/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/infix operation/output/tests/generator/infixOperation/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/literals/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/literals/input.smltest new file mode 100644 index 000000000..4567d1472 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/literals/input.smltest @@ -0,0 +1,14 @@ +package tests.generator.literals + +fun f(param: Any?) + +workflow test { + f(true); + f(false); + f(1.0); + f(1); + f(null); + f(""); + f("multi +line"); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/literals/output/tests/generator/literals/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/literals/output/tests/generator/literals/gen_input.py new file mode 100644 index 000000000..0342ef5bd --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/literals/output/tests/generator/literals/gen_input.py @@ -0,0 +1,10 @@ +# Workflows -------------------------------------------------------------------- + +def test(): + f(True) + f(False) + f(1.0) + f(1) + f(None) + f('') + f('multi\nline') diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/literals/output/tests/generator/literals/gen_input_test.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/literals/output/tests/generator/literals/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/literals/output/tests/generator/literals/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/member access/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/member access/input.smltest new file mode 100644 index 000000000..b594a19d5 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/member access/input.smltest @@ -0,0 +1,24 @@ +package tests.generator.memberAccess + +fun f(param: Any?) + +fun g() -> result: Boolean + +fun h() -> (result1: Boolean, result2: Boolean) + +class C() { + attr a: Int + @PythonName("c") attr b: Int +} + +fun factory() -> instance: C? + +workflow test { + f(g().result); + f(h().result1); + f(h().result2); + f(C().a); + f(C().b); + f(factory()?.a); + f(factory()?.b); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/member access/output/tests/generator/memberAccess/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/member access/output/tests/generator/memberAccess/gen_input.py new file mode 100644 index 000000000..ab757cdb3 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/member access/output/tests/generator/memberAccess/gen_input.py @@ -0,0 +1,14 @@ +# Imports ---------------------------------------------------------------------- + +import simpleml.codegen + +# Workflows -------------------------------------------------------------------- + +def test(): + f(g()) + f(h()[0]) + f(h()[1]) + f(C().a) + f(C().c) + f(simpleml.codegen.safe_access(factory(), 'a')) + f(simpleml.codegen.safe_access(factory(), 'c')) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/member access/output/tests/generator/memberAccess/gen_input_test.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/member access/output/tests/generator/memberAccess/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/member access/output/tests/generator/memberAccess/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/parenthesized expression/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/parenthesized expression/input.smltest new file mode 100644 index 000000000..b1a14cd17 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/parenthesized expression/input.smltest @@ -0,0 +1,9 @@ +package tests.generator.parenthesizedExpression + +fun f(param: Any?) + +fun g() -> result: Boolean + +workflow test { + f((g())); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input.py new file mode 100644 index 000000000..0d659b1ac --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input.py @@ -0,0 +1,4 @@ +# Workflows -------------------------------------------------------------------- + +def test(): + f(g()) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input_test.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/parenthesized expression/output/tests/generator/parenthesizedExpression/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/prefix operation/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/prefix operation/input.smltest new file mode 100644 index 000000000..10b6c531a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/prefix operation/input.smltest @@ -0,0 +1,12 @@ +package tests.generator.prefixOperation + +fun f(param: Any?) + +fun g() -> result: Boolean + +fun h() -> result: Int + +workflow test { + f(not g()); + f(-h()); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/prefix operation/output/tests/generator/prefixOperation/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/prefix operation/output/tests/generator/prefixOperation/gen_input.py new file mode 100644 index 000000000..6c4cfa160 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/prefix operation/output/tests/generator/prefixOperation/gen_input.py @@ -0,0 +1,5 @@ +# Workflows -------------------------------------------------------------------- + +def test(): + f(not (g())) + f(-(h())) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/prefix operation/output/tests/generator/prefixOperation/gen_input_test.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/prefix operation/output/tests/generator/prefixOperation/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/prefix operation/output/tests/generator/prefixOperation/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/reference/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/reference/input.smltest new file mode 100644 index 000000000..d55a75bd2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/reference/input.smltest @@ -0,0 +1,10 @@ +package tests.generator.reference + +fun f(param: Any?) + +@PythonName("explain_model") +fun explainModel() -> result: Int + +workflow test { + f(explainModel()); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/reference/output/tests/generator/reference/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/reference/output/tests/generator/reference/gen_input.py new file mode 100644 index 000000000..f2a96c2f8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/reference/output/tests/generator/reference/gen_input.py @@ -0,0 +1,4 @@ +# Workflows -------------------------------------------------------------------- + +def test(): + f(explain_model()) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/reference/output/tests/generator/reference/gen_input_test.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/reference/output/tests/generator/reference/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/reference/output/tests/generator/reference/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/template string/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/template string/input.smltest new file mode 100644 index 000000000..76c20cd3b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/template string/input.smltest @@ -0,0 +1,12 @@ +package tests.generator.templateString + +fun f(param: Any?) + +fun g() -> result: Int + +workflow test { + f("start +{{ g() }} +inner {{ g() }} +end"); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/template string/output/tests/generator/templateString/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/template string/output/tests/generator/templateString/gen_input.py new file mode 100644 index 000000000..3049db417 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/template string/output/tests/generator/templateString/gen_input.py @@ -0,0 +1,4 @@ +# Workflows -------------------------------------------------------------------- + +def test(): + f(f'start\n{ g() }\ninner { g() }\nend') diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/template string/output/tests/generator/templateString/gen_input_test.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/template string/output/tests/generator/templateString/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/expressions/template string/output/tests/generator/templateString/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/_skip_ context different package.smlstub b/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/_skip_ context different package.smlstub new file mode 100644 index 000000000..fd7d34dd4 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/_skip_ context different package.smlstub @@ -0,0 +1,4 @@ +package tests.generator.differentPackage + +fun function1InDifferentPackage() -> result: Int +fun function2InDifferentPackage() -> result: Int diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/_skip_ context package with python module.smlstub b/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/_skip_ context package with python module.smlstub new file mode 100644 index 000000000..adc1112f6 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/_skip_ context package with python module.smlstub @@ -0,0 +1,6 @@ +@PythonModule("special_location") + +package tests.generator.withPythonModule + +fun function1InCompilationUnitWithPythonModule() -> result: Int +fun function2InCompilationUnitWithPythonModule() -> result: Int diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/_skip_ context same package.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/_skip_ context same package.smltest new file mode 100644 index 000000000..de0186d0a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/_skip_ context same package.smltest @@ -0,0 +1,11 @@ +package tests.generator.imports + +fun impureFunction() -> result: Int + +step step1InSamePackage() -> result: Int { + yield result = impureFunction(); +} + +step step2InSamePackage() -> result: Int { + yield result = impureFunction(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/input.smltest new file mode 100644 index 000000000..a6dda93d0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/input.smltest @@ -0,0 +1,25 @@ +package tests.generator.imports + +import tests.generator.differentPackage.function1InDifferentPackage +import tests.generator.differentPackage.function2InDifferentPackage as g +import tests.generator.withPythonModule.function1InCompilationUnitWithPythonModule +import tests.generator.withPythonModule.function2InCompilationUnitWithPythonModule as h + +fun f(param: Any?) + +workflow test { + f(step1InSamePackage()); + f(step1InSamePackage()); + f(step2InSamePackage()); + f(step2InSamePackage()); + + f(function1InDifferentPackage()); + f(function1InDifferentPackage()); + f(g()); + f(g()); + + f(function1InCompilationUnitWithPythonModule()); + f(function1InCompilationUnitWithPythonModule()); + f(h()); + f(h()); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/output/tests/generator/imports/gen__skip__context_same_package.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/output/tests/generator/imports/gen__skip__context_same_package.py new file mode 100644 index 000000000..4f69c70b4 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/output/tests/generator/imports/gen__skip__context_same_package.py @@ -0,0 +1,9 @@ +# Steps ------------------------------------------------------------------------ + +def step1InSamePackage(): + result = impureFunction() + return result + +def step2InSamePackage(): + result = impureFunction() + return result diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/output/tests/generator/imports/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/output/tests/generator/imports/gen_input.py new file mode 100644 index 000000000..ea3319bd1 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/output/tests/generator/imports/gen_input.py @@ -0,0 +1,21 @@ +# Imports ---------------------------------------------------------------------- + +from special_location import function1InCompilationUnitWithPythonModule, function2InCompilationUnitWithPythonModule as h +from tests.generator.differentPackage import function1InDifferentPackage, function2InDifferentPackage as g +from tests.generator.imports.gen__skip__context_same_package import step1InSamePackage, step2InSamePackage + +# Workflows -------------------------------------------------------------------- + +def test(): + f(step1InSamePackage()) + f(step1InSamePackage()) + f(step2InSamePackage()) + f(step2InSamePackage()) + f(function1InDifferentPackage()) + f(function1InDifferentPackage()) + f(g()) + f(g()) + f(function1InCompilationUnitWithPythonModule()) + f(function1InCompilationUnitWithPythonModule()) + f(h()) + f(h()) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/output/tests/generator/imports/gen_input_test.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/output/tests/generator/imports/gen_input_test.py new file mode 100644 index 000000000..30abe031d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/imports/output/tests/generator/imports/gen_input_test.py @@ -0,0 +1,4 @@ +from gen_input import test + +if __name__ == '__main__': + test() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/python module/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/python module/input.smltest new file mode 100644 index 000000000..0901c1873 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/python module/input.smltest @@ -0,0 +1,9 @@ +@PythonModule("special_module") + +package tests.generator.pythonModule + +fun f() + +step test() { + f(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/python module/output/special_module/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/python module/output/special_module/gen_input.py new file mode 100644 index 000000000..5c10c6069 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/python module/output/special_module/gen_input.py @@ -0,0 +1,4 @@ +# Steps ------------------------------------------------------------------------ + +def test(): + f() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/assignment/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/assignment/input.smltest new file mode 100644 index 000000000..398070c56 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/assignment/input.smltest @@ -0,0 +1,30 @@ +package tests.generator.assignment + +fun f1(param: Any?) +fun f2(param: () -> r: Int?) + +fun g() -> (a: Int, b: Int, c: Int) + +workflow testFlow { + _, _ ,_ = g(); + val a, _, _ = g(); + val x = g(); + f1(a); + f1(x); + + f2(() { + _, _ ,_ = g(); + val a, _, yield c = g(); + val x = g(); + f1(a); + f1(x); + }); +} + +step testStep() -> c: Int { + _, _ ,_ = g(); + val a, _, yield c = g(); + val x = g(); + f1(a); + f1(x); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/assignment/output/tests/generator/assignment/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/assignment/output/tests/generator/assignment/gen_input.py new file mode 100644 index 000000000..43ad3cf65 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/assignment/output/tests/generator/assignment/gen_input.py @@ -0,0 +1,32 @@ +# Imports ---------------------------------------------------------------------- + +import runtimeBridge + +# Steps ------------------------------------------------------------------------ + +def testStep(): + g() + a, _, c = g() + x, _, _ = g() + f1(a) + f1(x) + return c + +# Workflows -------------------------------------------------------------------- + +def testFlow(): + g() + a, _, _ = g() + runtimeBridge.save_placeholder('a', a) + x, _, _ = g() + runtimeBridge.save_placeholder('x', x) + f1(a) + f1(x) + def __block_lambda_0(): + g() + a, _, c = g() + x, _, _ = g() + f1(a) + f1(x) + return c + f2(__block_lambda_0) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/assignment/output/tests/generator/assignment/gen_input_testFlow.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/assignment/output/tests/generator/assignment/gen_input_testFlow.py new file mode 100644 index 000000000..b6a511954 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/assignment/output/tests/generator/assignment/gen_input_testFlow.py @@ -0,0 +1,4 @@ +from gen_input import testFlow + +if __name__ == '__main__': + testFlow() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/expression statement/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/expression statement/input.smltest new file mode 100644 index 000000000..783cd09a0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/expression statement/input.smltest @@ -0,0 +1,17 @@ +package tests.generator.expressionStatement + +fun f(param: () -> ()) + +fun g() -> result: Int + +workflow testFlow { + g(); + + f(() { + g(); + }); +} + +step testStep() { + g(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/expression statement/output/tests/generator/expressionStatement/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/expression statement/output/tests/generator/expressionStatement/gen_input.py new file mode 100644 index 000000000..f6e0030bc --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/expression statement/output/tests/generator/expressionStatement/gen_input.py @@ -0,0 +1,12 @@ +# Steps ------------------------------------------------------------------------ + +def testStep(): + g() + +# Workflows -------------------------------------------------------------------- + +def testFlow(): + g() + def __block_lambda_0(): + g() + f(__block_lambda_0) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/expression statement/output/tests/generator/expressionStatement/gen_input_testFlow.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/expression statement/output/tests/generator/expressionStatement/gen_input_testFlow.py new file mode 100644 index 000000000..b6a511954 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/expression statement/output/tests/generator/expressionStatement/gen_input_testFlow.py @@ -0,0 +1,4 @@ +from gen_input import testFlow + +if __name__ == '__main__': + testFlow() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/statement without effect/input.smltest b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/statement without effect/input.smltest new file mode 100644 index 000000000..0e49c4d72 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/statement without effect/input.smltest @@ -0,0 +1,18 @@ +package tests.generator.statementWithoutEffect + +fun f(param: () -> ()) + +workflow testFlow { + 1; + _ = 1; + + f(() { + 1; + _ = 1; + }); +} + +step testStep() { + 1; + _ = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py new file mode 100644 index 000000000..1e9d4b809 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input.py @@ -0,0 +1,11 @@ +# Steps ------------------------------------------------------------------------ + +def testStep(): + pass + +# Workflows -------------------------------------------------------------------- + +def testFlow(): + def __block_lambda_0(): + pass + f(__block_lambda_0) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input_testFlow.py b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input_testFlow.py new file mode 100644 index 000000000..b6a511954 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/generator/statements/statement without effect/output/tests/generator/statementWithoutEffect/gen_input_testFlow.py @@ -0,0 +1,4 @@ +from gen_input import testFlow + +if __name__ == '__main__': + testFlow() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/bad-annotation between package and import.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/bad-annotation between package and import.smltest new file mode 100644 index 000000000..088dbd456 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/bad-annotation between package and import.smltest @@ -0,0 +1,7 @@ +// syntax_error + +package test + +@AnnotationCall + +import someDeclaration diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/bad-duplicate package.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/bad-duplicate package.smltest new file mode 100644 index 000000000..19a1f0cae --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/bad-duplicate package.smltest @@ -0,0 +1,5 @@ +// syntax_error + +package test + +package test diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/bad-import before declaration b/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/bad-import before declaration new file mode 100644 index 000000000..e69de29bb diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/bad-import before package.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/bad-import before package.smltest new file mode 100644 index 000000000..9de5197b1 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/bad-import before package.smltest @@ -0,0 +1,5 @@ +// syntax_error + +import someDeclaration + +package test diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/good-complex example.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/good-complex example.smltest new file mode 100644 index 000000000..af5ecded2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/good-complex example.smltest @@ -0,0 +1,11 @@ +// no_syntax_error + +@AnnotationCall + +package test + +import someDeclaration +import somePackage.* + +@AnnotationCall +class C diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/good-empty.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/good-empty.smltest new file mode 100644 index 000000000..bde20dd35 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/good-empty.smltest @@ -0,0 +1 @@ +// no_syntax_error \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/good-lone annotation call.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/good-lone annotation call.smltest new file mode 100644 index 000000000..a852b5bda --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/compilationUnits/good-lone annotation call.smltest @@ -0,0 +1,3 @@ +// no_syntax_error + +@AnnotationCall diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/annotation.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/annotation.smltest new file mode 100644 index 000000000..34c202c09 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/annotation.smltest @@ -0,0 +1,8 @@ +// no_syntax_error + +annotation MySimpleAnnotation + +@AnnotationUse annotation MyComplexAnnotation( + @AnnotationUse a: Int, + vararg b: Int = 3 +) where T2 super Number, T3 sub Number diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/annotationUse.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/annotationUse.smltest new file mode 100644 index 000000000..26f675f7d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/annotationUse.smltest @@ -0,0 +1,3 @@ +// no_syntax_error + +@AnnotationUse(1, b = 2, A.B) class MyClass diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/class.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/class.smltest new file mode 100644 index 000000000..630fa04a1 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/class.smltest @@ -0,0 +1,16 @@ +// no_syntax_error + +class MySimpleClass + +@AnnotationUse +class MyComplexClass + <@AnnotationUse T1, in T2, out T3> + (@AnnotationUse a: Int, vararg b: Int = 3) + sub SuperClass1, SuperClass2 + where T2 super Number, T3 sub Number +{ + @AnnotationUse static attr myAttribute: Int + @AnnotationUse class MyClass + @AnnotationUse enum MyEnum + @AnnotationUse static fun MyFunction() +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/enum.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/enum.smltest new file mode 100644 index 000000000..68ec00a17 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/enum.smltest @@ -0,0 +1,10 @@ +// no_syntax_error +enum MySimpleEnum + +@AnnotationUse enum MyComplexEnum { + MySimpleVariant + @AnnotationUse MyComplexVariant + <@AnnotationUse T1, in T2, out T3> + (@AnnotationUse a: Int, vararg b: Int = 3) + where T2 super Number, T3 sub Number +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/function.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/function.smltest new file mode 100644 index 000000000..8352f1676 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/function.smltest @@ -0,0 +1,12 @@ +// no_syntax_error + +fun mySimpleFunction() + +fun myFunctionWithOneResult() -> a : Int + +@MyAnnotation +fun myComplexFunction + <@AnnotationUse T1, in T2, out T3> + (@AnnotationUse a: Int, vararg b: Int = 3) + -> (@AnnotationUse a: Int, b: Int) + where T2 super Number, T3 sub Number diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-annotationUsesWithImports.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-annotationUsesWithImports.smltest new file mode 100644 index 000000000..f84e37c9a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-annotationUsesWithImports.smltest @@ -0,0 +1,5 @@ +// no_syntax_error + +@AnnotationUse + +import myPackage.MyClass diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-annotationUsesWithName.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-annotationUsesWithName.smltest new file mode 100644 index 000000000..d6703a025 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-annotationUsesWithName.smltest @@ -0,0 +1,5 @@ +// no_syntax_error + +@AnnotationUse + +package myPackage diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-full.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-full.smltest new file mode 100644 index 000000000..e49ae47f4 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-full.smltest @@ -0,0 +1,11 @@ +// no_syntax_error + +@AnnotationUse + +package myPackage + +import myPackage.MyClass +import myPackage.MyClass as Class +import myPackage.* + +workflow w1 {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-importsOnly.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-importsOnly.smltest new file mode 100644 index 000000000..e33ca9bcf --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-importsOnly.smltest @@ -0,0 +1,5 @@ +// no_syntax_error + +import myPackage.MyClass +import myPackage.MyClass as Class +import myPackage.* diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-nameOnly.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-nameOnly.smltest new file mode 100644 index 000000000..b2c704934 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/package/good-nameOnly.smltest @@ -0,0 +1,3 @@ +// no_syntax_error + +package myPackage diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with alternative as reference list.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with alternative as reference list.smltest new file mode 100644 index 000000000..f3726d62d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with alternative as reference list.smltest @@ -0,0 +1,7 @@ +// syntax_error + +class C { + protocol { + [^ f | g] + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with alternative as universe.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with alternative as universe.smltest new file mode 100644 index 000000000..92dff8028 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with alternative as universe.smltest @@ -0,0 +1,7 @@ +// syntax_error + +class C { + protocol { + [f | g^ f] + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with complement as reference list.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with complement as reference list.smltest new file mode 100644 index 000000000..073eb2be4 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with complement as reference list.smltest @@ -0,0 +1,7 @@ +// syntax_error + +class C { + protocol { + [^ [^ f]] + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with complement as universe.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with complement as universe.smltest new file mode 100644 index 000000000..68b59db9d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with complement as universe.smltest @@ -0,0 +1,7 @@ +// syntax_error + +class C { + protocol { + [[^ f]^ f] + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with parenthesized term as reference list.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with parenthesized term as reference list.smltest new file mode 100644 index 000000000..bd517810c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with parenthesized term as reference list.smltest @@ -0,0 +1,7 @@ +// syntax_error + +class C { + protocol { + [^ (f)] + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with parenthesized term as universe.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with parenthesized term as universe.smltest new file mode 100644 index 000000000..f93924ad9 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with parenthesized term as universe.smltest @@ -0,0 +1,7 @@ +// syntax_error + +class C { + protocol { + [(f)^ f] + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with sequence as universe.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with sequence as universe.smltest new file mode 100644 index 000000000..3da9758ea --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with sequence as universe.smltest @@ -0,0 +1,7 @@ +// syntax_error + +class C { + protocol { + [f g^ f] + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with token as universe.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with token as universe.smltest new file mode 100644 index 000000000..eb8defb85 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with token as universe.smltest @@ -0,0 +1,7 @@ +// syntax_error + +class C { + protocol { + [f^ f] + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with token class as reference list.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with token class as reference list.smltest new file mode 100644 index 000000000..e4003a7cd --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-complement with token class as reference list.smltest @@ -0,0 +1,7 @@ +// syntax_error + +class C { + protocol { + [^ \a] + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-subterm after term.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-subterm after term.smltest new file mode 100644 index 000000000..7e88ae244 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-subterm after term.smltest @@ -0,0 +1,9 @@ +// syntax_error + +class C { + protocol { + f + + subterm term = f; + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-subterm without semicolon.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-subterm without semicolon.smltest new file mode 100644 index 000000000..16b628917 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/bad-subterm without semicolon.smltest @@ -0,0 +1,7 @@ +// syntax_error + +class C { + protocol { + subterm term = f + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-alternative.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-alternative.smltest new file mode 100644 index 000000000..78d5d8689 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-alternative.smltest @@ -0,0 +1,11 @@ +// no_syntax_error + +class C { + protocol { + f | g + } + + protocol { + f | g | h + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-complement without reference list.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-complement without reference list.smltest new file mode 100644 index 000000000..6be305b3c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-complement without reference list.smltest @@ -0,0 +1,7 @@ +// no_syntax_error + +class C { + protocol { + [^] + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-complement.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-complement.smltest new file mode 100644 index 000000000..8cc1c98f9 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-complement.smltest @@ -0,0 +1,15 @@ +// no_syntax_error + +class C { + protocol { + [^ f] + [.^ f] + [\a^ a] + [\f^ f] + + [^ f g] + [.^ f h] + [\a^ a b] + [\f^ f h] + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-empty.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-empty.smltest new file mode 100644 index 000000000..d0e9d9a15 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-empty.smltest @@ -0,0 +1,5 @@ +// no_syntax_error + +class C { + protocol {} +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-parenthesizedTerm.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-parenthesizedTerm.smltest new file mode 100644 index 000000000..42b2c4401 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-parenthesizedTerm.smltest @@ -0,0 +1,7 @@ +// no_syntax_error + +class C { + protocol { + (f) + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-quantifiedTerm.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-quantifiedTerm.smltest new file mode 100644 index 000000000..9dc3c5539 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-quantifiedTerm.smltest @@ -0,0 +1,29 @@ +// no_syntax_error + +class C { + protocol { + [^ f]? + [^ f]* + [^ f]+ + + (f)? + (f)* + (f)+ + + f? + f* + f+ + + .? + .* + .+ + + \a? + \a* + \a+ + + \f? + \f* + \f+ + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-sequence.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-sequence.smltest new file mode 100644 index 000000000..afd3bbcf2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-sequence.smltest @@ -0,0 +1,11 @@ +// no_syntax_error + +class C { + protocol { + f g + } + + protocol { + f g h + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-subterms.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-subterms.smltest new file mode 100644 index 000000000..d3f8a7997 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-subterms.smltest @@ -0,0 +1,10 @@ +// no_syntax_error + +class C { + protocol { + subterm term1 = f; + subterm term2 = g; + + term1 | term2 + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-token.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-token.smltest new file mode 100644 index 000000000..4885e63ed --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-token.smltest @@ -0,0 +1,7 @@ +// no_syntax_error + +class C { + protocol { + f + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-tokenClasses.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-tokenClasses.smltest new file mode 100644 index 000000000..376e6c9b6 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/protocols/good-tokenClasses.smltest @@ -0,0 +1,9 @@ +// no_syntax_error + +class C { + protocol { + . + \a + \f + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/steps/bad-two visibilities (internal, private).smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/steps/bad-two visibilities (internal, private).smltest new file mode 100644 index 000000000..e41b812da --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/steps/bad-two visibilities (internal, private).smltest @@ -0,0 +1,3 @@ +// syntax_error + +internal private step myStep() {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/steps/bad-two visibilities (private, internal).smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/steps/bad-two visibilities (private, internal).smltest new file mode 100644 index 000000000..efd0f3f55 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/steps/bad-two visibilities (private, internal).smltest @@ -0,0 +1,3 @@ +// syntax_error + +private internal step myStep() {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/steps/good-step in compilation unit.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/steps/good-step in compilation unit.smltest new file mode 100644 index 000000000..df7fe02b1 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/steps/good-step in compilation unit.smltest @@ -0,0 +1,12 @@ +// no_syntax_error + +step mySimpleStep() {} + +internal step myInternalStep() {} + +step myStepWithOneResult() -> a: Int {} + +@AnnotationUse +private step myComplexStep(@AnnotationUse a: Int, vararg b: Int = 3) -> (a: Int, b: Int) { + val a = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/steps/good-step in package.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/steps/good-step in package.smltest new file mode 100644 index 000000000..2b94e0b8d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/steps/good-step in package.smltest @@ -0,0 +1,14 @@ +// no_syntax_error + +package myPackage + +step mySimpleStep() {} + +internal step myInternalStep() {} + +step myStepWithOneResult() -> a: Int {} + +@AnnotationUse +private step myComplexStep(@AnnotationUse a: Int, vararg b: Int = 3) -> (a: Int, b: Int) { + val a = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/workflow.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/workflow.smltest new file mode 100644 index 000000000..8a8e58628 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/declarations/workflow.smltest @@ -0,0 +1,7 @@ +// no_syntax_error + +workflow mySimpleWorkflow {} + +@AnnotationUse workflow myComplexWorkflow { + val a = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/arithmeticOperator.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/arithmeticOperator.smltest new file mode 100644 index 000000000..29cd09688 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/arithmeticOperator.smltest @@ -0,0 +1,11 @@ +// no_syntax_error + +workflow myWorkflow { + 1 + 2; + 1 - 2; + 1 * 2; + 1 / 2; + -1; + + (1 + 2) * -3 / (1 - 4) + 5; +} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/blockLambda.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/blockLambda.smltest new file mode 100644 index 000000000..feeedbf15 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/blockLambda.smltest @@ -0,0 +1,9 @@ +// no_syntax_error + +workflow myWorkflow { + () {}; + + (a, vararg b = 1) { + val a = 1; + }; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/call.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/call.smltest new file mode 100644 index 000000000..e2c56d23d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/call.smltest @@ -0,0 +1,6 @@ +// no_syntax_error + +workflow myWorkflow { + f(); + f<*, in Number, out Number, T = Number>(1, b = 2); +} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/chainedExpression.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/chainedExpression.smltest new file mode 100644 index 000000000..8383c8ede --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/chainedExpression.smltest @@ -0,0 +1,5 @@ +// no_syntax_error + +workflow myWorkflow { + a.member?.f()[1]; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/comparisonOperator.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/comparisonOperator.smltest new file mode 100644 index 000000000..dfeb6a4f9 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/comparisonOperator.smltest @@ -0,0 +1,12 @@ +// no_syntax_error + +workflow myWorkflow { + 1 < 2; + 1 <= 2; + 1 == 2; + 1 === 2; + 1 != 2; + 1 !== 2; + 1 >= 2; + 1 > 2; +} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/expressionLambda.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/expressionLambda.smltest new file mode 100644 index 000000000..2eb86daf6 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/expressionLambda.smltest @@ -0,0 +1,7 @@ +// no_syntax_error + +workflow myWorkflow { + () -> 1; + + (a, vararg b = 1) -> 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/indexedAccess/bad-indexed access without index.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/indexedAccess/bad-indexed access without index.smltest new file mode 100644 index 000000000..7fa27d236 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/indexedAccess/bad-indexed access without index.smltest @@ -0,0 +1,5 @@ +// syntax_error + +workflow test { + a[]; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/indexedAccess/good-indexed access.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/indexedAccess/good-indexed access.smltest new file mode 100644 index 000000000..0ecbcd6ca --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/indexedAccess/good-indexed access.smltest @@ -0,0 +1,6 @@ +// no_syntax_error + +workflow test { + a[1]; + a[b]; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/literals/booleanLiteral.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/literals/booleanLiteral.smltest new file mode 100644 index 000000000..e869f13eb --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/literals/booleanLiteral.smltest @@ -0,0 +1,6 @@ +// no_syntax_error + +workflow myWorkflow { + true; + false; +} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/literals/nullLiteral.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/literals/nullLiteral.smltest new file mode 100644 index 000000000..106b38c2e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/literals/nullLiteral.smltest @@ -0,0 +1,5 @@ +// no_syntax_error + +workflow myWorkflow { + null; +} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/literals/numberLiteral.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/literals/numberLiteral.smltest new file mode 100644 index 000000000..59560d1de --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/literals/numberLiteral.smltest @@ -0,0 +1,16 @@ +// no_syntax_error + +workflow myWorkflow { + 1; + 1.2; + + 1e10; + 1e+10; + 1e-10; + 1.2e2; + + 1E10; + 1E+10; + 1E-10; + 1.2E2; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/literals/stringLiteral.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/literals/stringLiteral.smltest new file mode 100644 index 000000000..b006fa7f9 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/literals/stringLiteral.smltest @@ -0,0 +1,19 @@ +// no_syntax_error + +workflow myWorkflow { + ""; + "myString"; + "'"; + "\n"; + "\u000c"; + " + multi + line + string + "; + "{"; + "{\n"; + "{ {"; + "not a template { ??? }"; + "not a template either \{{ ??? }}"; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/logicalOperator.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/logicalOperator.smltest new file mode 100644 index 000000000..68502aa5a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/logicalOperator.smltest @@ -0,0 +1,9 @@ +// no_syntax_error + +workflow myWorkflow { + false and true; + true or true; + not true; + + not true and false or true; +} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/memberAccess.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/memberAccess.smltest new file mode 100644 index 000000000..351660d69 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/memberAccess.smltest @@ -0,0 +1,6 @@ +// no_syntax_error + +workflow myWorkflow { + a.member; + a?.member; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/parenthesizedExpression.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/parenthesizedExpression.smltest new file mode 100644 index 000000000..ff6dbfcdc --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/parenthesizedExpression.smltest @@ -0,0 +1,5 @@ +// no_syntax_error + +workflow myWorkflow { + (1 + 2); +} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/reference.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/reference.smltest new file mode 100644 index 000000000..d5e5462d2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/reference.smltest @@ -0,0 +1,5 @@ +// no_syntax_error + +workflow myWorkflow { + a; +} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/templateString_bad.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/templateString_bad.smltest new file mode 100644 index 000000000..1dd540d28 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/templateString_bad.smltest @@ -0,0 +1,5 @@ +// syntax_error + +workflow myWorkflow { + "}} template {{ ??? }}"; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/templateString_good.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/templateString_good.smltest new file mode 100644 index 000000000..b5fd23130 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/expressions/templateString_good.smltest @@ -0,0 +1,5 @@ +// no_syntax_error + +workflow myWorkflow { + "}} template {{ a }}"; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedAnd.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedAnd.smltest new file mode 100644 index 000000000..a70b3af83 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedAnd.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class and diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedAnnotation.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedAnnotation.smltest new file mode 100644 index 000000000..6c540f85e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedAnnotation.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class annotation diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedAs.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedAs.smltest new file mode 100644 index 000000000..cf1d648b3 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedAs.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class as diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedAttr.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedAttr.smltest new file mode 100644 index 000000000..5554b7a21 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedAttr.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class attr diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedClass.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedClass.smltest new file mode 100644 index 000000000..0290889ae --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedClass.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class class diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedEnum.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedEnum.smltest new file mode 100644 index 000000000..bce83b376 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedEnum.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class enum diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedFalse.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedFalse.smltest new file mode 100644 index 000000000..3a1290779 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedFalse.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class false diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedFun.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedFun.smltest new file mode 100644 index 000000000..eb95af4a6 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedFun.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class fun diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedImport.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedImport.smltest new file mode 100644 index 000000000..0f8524db7 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedImport.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class import diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedIn.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedIn.smltest new file mode 100644 index 000000000..6cb827012 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedIn.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class in diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedNot.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedNot.smltest new file mode 100644 index 000000000..8586672b0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedNot.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class not diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedNull.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedNull.smltest new file mode 100644 index 000000000..188b1f197 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedNull.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class null diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedOr.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedOr.smltest new file mode 100644 index 000000000..b039f4573 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedOr.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class or diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedOut.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedOut.smltest new file mode 100644 index 000000000..294065ff8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedOut.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class out diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedPackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedPackage.smltest new file mode 100644 index 000000000..1459c2c9e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedPackage.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class package diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedStatic.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedStatic.smltest new file mode 100644 index 000000000..4a6709bd1 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedStatic.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class static diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedStep.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedStep.smltest new file mode 100644 index 000000000..a10945c6c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedStep.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class step diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedSub.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedSub.smltest new file mode 100644 index 000000000..80200c946 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedSub.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class sub diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedSuper.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedSuper.smltest new file mode 100644 index 000000000..8407db8b9 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedSuper.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class super diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedTrue.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedTrue.smltest new file mode 100644 index 000000000..a950a00fb --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedTrue.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class true diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedUnion.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedUnion.smltest new file mode 100644 index 000000000..1331679d0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedUnion.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class union diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedVal.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedVal.smltest new file mode 100644 index 000000000..5d87514d7 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedVal.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class val diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedVararg.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedVararg.smltest new file mode 100644 index 000000000..fa21df2ab --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedVararg.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class vararg diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedWhere.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedWhere.smltest new file mode 100644 index 000000000..20c59bdae --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedWhere.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class where diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedWorkflow.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedWorkflow.smltest new file mode 100644 index 000000000..7f3b2bf12 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedWorkflow.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class workflow diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedYield.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedYield.smltest new file mode 100644 index 000000000..4cb45092a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescapedYield.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class yield diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescaped_.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescaped_.smltest new file mode 100644 index 000000000..19e8906da --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/bad-unescaped_.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class _ diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/good-escapedKeywords.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/good-escapedKeywords.smltest new file mode 100644 index 000000000..80638fcf7 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/good-escapedKeywords.smltest @@ -0,0 +1,29 @@ +// no_syntax_error + +class `_` +class `and` +class `annotation` +class `as` +class `attr` +class `class` +class `enum` +class `false` +class `fun` +class `import` +class `in` +class `not` +class `null` +class `or` +class `out` +class `package` +class `static` +class `step` +class `sub` +class `super` +class `true` +class `union` +class `val` +class `vararg` +class `where` +class `workflow` +class `yield` diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/good-escapedNonKeyword.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/good-escapedNonKeyword.smltest new file mode 100644 index 000000000..948a94e10 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/keywordsAsNames/good-escapedNonKeyword.smltest @@ -0,0 +1,3 @@ +// no_syntax_error + +class `Bla` diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/parserShouldNotCrashOnSyntaxErrors/unclosedEnum.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/parserShouldNotCrashOnSyntaxErrors/unclosedEnum.smltest new file mode 100644 index 000000000..8a9492adb --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/parserShouldNotCrashOnSyntaxErrors/unclosedEnum.smltest @@ -0,0 +1,3 @@ +// syntax_error + +enum TestEnum { diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/parserShouldNotCrashOnSyntaxErrors/unclosedParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/parserShouldNotCrashOnSyntaxErrors/unclosedParameterList.smltest new file mode 100644 index 000000000..851ad5ba2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/parserShouldNotCrashOnSyntaxErrors/unclosedParameterList.smltest @@ -0,0 +1,3 @@ +// syntax_error + +fun f( diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/parserShouldNotCrashOnSyntaxErrors/unclosedResultList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/parserShouldNotCrashOnSyntaxErrors/unclosedResultList.smltest new file mode 100644 index 000000000..98ea819b8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/parserShouldNotCrashOnSyntaxErrors/unclosedResultList.smltest @@ -0,0 +1,3 @@ +// syntax_error + +fun s() -> ( diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/parserShouldNotCrashOnSyntaxErrors/unclosedTypeParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/parserShouldNotCrashOnSyntaxErrors/unclosedTypeParameterList.smltest new file mode 100644 index 000000000..1ff89a4f2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/parserShouldNotCrashOnSyntaxErrors/unclosedTypeParameterList.smltest @@ -0,0 +1,3 @@ +// syntax_error + +class C< diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/statements/assignment.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/statements/assignment.smltest new file mode 100644 index 000000000..e7471ca0c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/statements/assignment.smltest @@ -0,0 +1,11 @@ +// no_syntax_error + +workflow myWorkflow { + val a = 0; + val a, _, yield b = call(); + + () { + val a = 0; + val a, _, yield b = call(); + }; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/statements/expressionStatement.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/statements/expressionStatement.smltest new file mode 100644 index 000000000..742b1070c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/statements/expressionStatement.smltest @@ -0,0 +1,9 @@ +// no_syntax_error + +workflow myWorkflow { + call(); + + () { + call(); + }; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inAnnotationUse_ArgumentList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inAnnotationUse_ArgumentList.smltest new file mode 100644 index 000000000..76633ecb2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inAnnotationUse_ArgumentList.smltest @@ -0,0 +1,5 @@ +package tests.trailingComma.inAnnotationUseArgumentList + +// no_syntax_error + +@A(1, 2, ) class C \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inAnnotation_ConstraintList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inAnnotation_ConstraintList.smltest new file mode 100644 index 000000000..325d72551 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inAnnotation_ConstraintList.smltest @@ -0,0 +1,5 @@ +package tests.trailingComma.inAnnotationConstraintList + +// no_syntax_error + +annotation A where T sub Any, T super Int, diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inAnnotation_ParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inAnnotation_ParameterList.smltest new file mode 100644 index 000000000..db246b81c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inAnnotation_ParameterList.smltest @@ -0,0 +1,8 @@ +package tests.trailingComma.inAnnotationParameterList + +// no_syntax_error + +annotation A( + a: Int, + b: Int, +) \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inAssignment_AssigneeList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inAssignment_AssigneeList.smltest new file mode 100644 index 000000000..bc605657e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inAssignment_AssigneeList.smltest @@ -0,0 +1,11 @@ +package tests.trailingComma.inDoStatementAssigneeList + +// no_syntax_error + +workflow w { + val a, val b, = f(); + + () { + val a, val b, = f(); + }; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inBlockLambda_ParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inBlockLambda_ParameterList.smltest new file mode 100644 index 000000000..e60dfb498 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inBlockLambda_ParameterList.smltest @@ -0,0 +1,7 @@ +package tests.trailingComma.inBlockLambdaParameterList + +// no_syntax_error + +workflow w { + (first, second, ) {}; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inCall_ArgumentList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inCall_ArgumentList.smltest new file mode 100644 index 000000000..5e1817338 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inCall_ArgumentList.smltest @@ -0,0 +1,7 @@ +package tests.trailingComma.inCallArgumentList + +// no_syntax_error + +workflow w { + f(1, 2, ); +} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inCall_TypeArgumentList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inCall_TypeArgumentList.smltest new file mode 100644 index 000000000..841ce1141 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inCall_TypeArgumentList.smltest @@ -0,0 +1,7 @@ +package tests.trailingComma.inCallTypeArgumentList + +// no_syntax_error + +workflow w { + f(); +} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inClass_ConstraintList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inClass_ConstraintList.smltest new file mode 100644 index 000000000..5ce738372 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inClass_ConstraintList.smltest @@ -0,0 +1,5 @@ +package tests.trailingComma.inClassConstraintList + +// no_syntax_error + +class C where T sub Any, T super Int, diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inClass_ParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inClass_ParameterList.smltest new file mode 100644 index 000000000..d62eac695 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inClass_ParameterList.smltest @@ -0,0 +1,5 @@ +package tests.trailingComma.inClassParameterList + +// no_syntax_error + +class C(a: Int, b: Int, ) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inClass_SuperTypeList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inClass_SuperTypeList.smltest new file mode 100644 index 000000000..bb5a2ff59 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inClass_SuperTypeList.smltest @@ -0,0 +1,5 @@ +package tests.trailingComma.inClassSuperTypeList + +// no_syntax_error + +class C sub D, E, \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inClass_TypeParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inClass_TypeParameterList.smltest new file mode 100644 index 000000000..be15d9cfd --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inClass_TypeParameterList.smltest @@ -0,0 +1,5 @@ +package tests.trailingComma.inClassTypeParameterList + +// no_syntax_error + +class C \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inEnumVariant_ConstraintList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inEnumVariant_ConstraintList.smltest new file mode 100644 index 000000000..8b243c4df --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inEnumVariant_ConstraintList.smltest @@ -0,0 +1,7 @@ +package tests.trailingComma.inEnumVariantConstraintList + +// no_syntax_error + +enum E { + A +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inEnumVariant_ParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inEnumVariant_ParameterList.smltest new file mode 100644 index 000000000..f9d56f56d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inEnumVariant_ParameterList.smltest @@ -0,0 +1,10 @@ +package tests.trailingComma.inEnumVariantParameterList + +// no_syntax_error + +enum E { + A( + a: Int, + b: Int, + ) +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inEnumVariant_TypeParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inEnumVariant_TypeParameterList.smltest new file mode 100644 index 000000000..839a01195 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inEnumVariant_TypeParameterList.smltest @@ -0,0 +1,7 @@ +package tests.trailingComma.inEnumVariantTypeParameterList + +// no_syntax_error + +enum E { + A where T sub Any, T super Int, +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inExpressionLambda_ParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inExpressionLambda_ParameterList.smltest new file mode 100644 index 000000000..594e60053 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inExpressionLambda_ParameterList.smltest @@ -0,0 +1,7 @@ +package tests.trailingComma.inExpressionLambdaParameterList + +// no_syntax_error + +workflow w { + (first, second, ) -> 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunctionType_ParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunctionType_ParameterList.smltest new file mode 100644 index 000000000..d82a8a409 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunctionType_ParameterList.smltest @@ -0,0 +1,7 @@ +package tests.trailingComma.inFunctionTypeParameterList + +// no_syntax_error + +step s( + f: (x: Int, y: Int, ) -> () +) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunctionType_ResultList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunctionType_ResultList.smltest new file mode 100644 index 000000000..b76427faf --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunctionType_ResultList.smltest @@ -0,0 +1,7 @@ +package tests.trailingComma.inFunctionTypeResultList + +// no_syntax_error + +step s( + f: () -> (x: Int, y: Int, ) +) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunction_ParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunction_ParameterList.smltest new file mode 100644 index 000000000..118106648 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunction_ParameterList.smltest @@ -0,0 +1,8 @@ +package tests.trailingComma.inFunctionParameterList + +// no_syntax_error + +fun f( + a: Int, + b: Int, +) \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunction_ResultList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunction_ResultList.smltest new file mode 100644 index 000000000..cc028bb4c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunction_ResultList.smltest @@ -0,0 +1,8 @@ +package tests.trailingComma.inFunctionResultList + +// no_syntax_error + +fun f() -> ( + first: Int, + second: Int, +) \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunction_TypeParameterConstraintList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunction_TypeParameterConstraintList.smltest new file mode 100644 index 000000000..1197221af --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunction_TypeParameterConstraintList.smltest @@ -0,0 +1,5 @@ +package tests.trailingComma.inFunctionTypeParameterConstraintList + +// no_syntax_error + +fun f() where T sub Any, T super Int, \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunction_TypeParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunction_TypeParameterList.smltest new file mode 100644 index 000000000..9fc8de059 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inFunction_TypeParameterList.smltest @@ -0,0 +1,5 @@ +package tests.trailingComma.inFunctionTypeParameterList + +// no_syntax_error + +fun f() \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inNamedType_TypeArgumentList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inNamedType_TypeArgumentList.smltest new file mode 100644 index 000000000..703df8112 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inNamedType_TypeArgumentList.smltest @@ -0,0 +1,7 @@ +package tests.trailingComma.inNamedTypeTypeArgumentList + +// no_syntax_error + +step s( + f: Type +) {} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inWorkflowStep_ParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inWorkflowStep_ParameterList.smltest new file mode 100644 index 000000000..2e82501f4 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/trailingCommas/inWorkflowStep_ParameterList.smltest @@ -0,0 +1,4 @@ +package tests.trailingComma.inWorkflowStepParameterList + +// no_syntax_error +step s(a: Int, b: Int, ) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/callableType.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/callableType.smltest new file mode 100644 index 000000000..d534a7dce --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/callableType.smltest @@ -0,0 +1,13 @@ +// no_syntax_error + +step s1( + f: () -> () +) {} + +step s2( + f: () -> result: Int +) {} + +step s3( + f: (@AnnotationUse a: Int, vararg b: Int = 3) -> (@AnnotationUse a: Int, b: Int) +) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/memberType.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/memberType.smltest new file mode 100644 index 000000000..fc7b2d8da --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/memberType.smltest @@ -0,0 +1,9 @@ +// no_syntax_error + +step s1( + f: OuterClass.InnerClass +) {} + +step s2( + f: (OuterClass?.MiddleClass?).InnerClass +) {} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/namedType.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/namedType.smltest new file mode 100644 index 000000000..77cf9efbd --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/namedType.smltest @@ -0,0 +1,9 @@ +// no_syntax_error + +step s1( + f: Int +) {} + +step s2( + f: Int<*, in Number, out Number, T = Number>? +) {} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/parenthesizedType.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/parenthesizedType.smltest new file mode 100644 index 000000000..8687eb6eb --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/parenthesizedType.smltest @@ -0,0 +1,5 @@ +// no_syntax_error + +step s( + f: (Int) +) {} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/unionType.smltest b/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/unionType.smltest new file mode 100644 index 000000000..a2cb44fff --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/grammar/types/unionType.smltest @@ -0,0 +1,9 @@ +// no_syntax_error + +step s1( + f: union<> +) {} + +step s2( + f: union +) {} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/junit-platform.properties b/DSL/de.unibonn.simpleml/src/test/resources/junit-platform.properties new file mode 100644 index 000000000..e6d55f8bd --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.testinstance.lifecycle.default = per_class \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/partialEvaluation/callables.smltest b/DSL/de.unibonn.simpleml/src/test/resources/partialEvaluation/callables.smltest new file mode 100644 index 000000000..5154f791e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/partialEvaluation/callables.smltest @@ -0,0 +1,42 @@ +package tests.partialEvaluation.callables + +// Preparation ----------------------------------------------------------------- + +fun impureFunction() -> result: Int + +@Pure +fun pureFunction() -> result: Int + +// Test data ------------------------------------------------------------------- + +workflow workflowWithImpureAndPureLambdas { + val impureBlockLambda = () { + impureFunction(); + }; + + val pureBlockLambda = () { + pureFunction(); + }; + + val recursiveBlockLambda = () { + recursiveStep(); + }; + + val impureExpressionLambda = () -> impureFunction(); + + val pureExpressionLambda = () -> pureFunction(); + + val recursiveExpressionLambda = () -> recursiveStep(); +} + +step impureStep() { + impureFunction(); +} + +step pureStep() { + pureFunction(); +} + +step recursiveStep() -> result: Int { + yield result = recursiveStep(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/partialEvaluation/calls.smltest b/DSL/de.unibonn.simpleml/src/test/resources/partialEvaluation/calls.smltest new file mode 100644 index 000000000..3af6f8d08 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/partialEvaluation/calls.smltest @@ -0,0 +1,56 @@ +package tests.partialEvaluation.higherOrder + +workflow callToBlockLambda { + val lambda = (a) { yield result = a; }; + lambda(1); +} + +workflow callToExpressionLambda { + val lambda = (a) -> a; + lambda(1); +} + +step myStep1(a: Int) -> result: Int { + yield result = a; +} + +workflow callToStep { + myStep1(1); +} + +step myStep2(vararg params: Int) -> result: Int { + yield result = params; +} + +workflow callToStepWithVariadicParameter { + myStep2(1); +} + +step myStep3(vararg params: Int) -> result: Int { + yield result = params[0]; +} + +workflow callToStepWithIndexedVariadicParameter { + myStep3(1); +} + +workflow parameterAssignedDuringCall { + ((a, b) { + val d = b; + yield result = ((b, c) -> a + b + c + d)(1, 2); + })(3, 4); +} + +step myStep4(param: Int) -> f: () -> (result: Int) { + yield f = () -> param; +} + +workflow parameterAssignedDuringCreationOfLambda { + myStep4(1)(); +} + +workflow lambdaAsParameter { + val apply = (f) -> f(); + + apply(() -> 1); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/partialEvaluation/memberAccesses.smltest b/DSL/de.unibonn.simpleml/src/test/resources/partialEvaluation/memberAccesses.smltest new file mode 100644 index 000000000..22454d8cd --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/partialEvaluation/memberAccesses.smltest @@ -0,0 +1,17 @@ +package tests.partialEvaluation.memberAccesses + +workflow successfulResultAccess { + val lambda = () { + yield result = 1; + }; + + lambda().result; +} + +workflow failedResultAccess { + val lambda = () { + yield result = 1; + }; + + lambda().result1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/partialEvaluation/references.smltest b/DSL/de.unibonn.simpleml/src/test/resources/partialEvaluation/references.smltest new file mode 100644 index 000000000..05d7ffbcd --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/partialEvaluation/references.smltest @@ -0,0 +1,51 @@ +package tests.partialEvaluation.references + +workflow successfulRecordAssignment { + val lambda = () { + yield result = 1; + }; + val placeholder = lambda(); + + placeholder; +} + +workflow failedRecordAssignment { + val lambda = () { + yield result = 1; + }; + _, val placeholder = lambda(); + + placeholder; +} + +step myStep1() -> (a: Int, b: Int) { + yield b = 1; + yield a = 2; +} + +workflow recordAssignmentWithDifferentYieldOrder { + val placeholder1, val placeholder2 = myStep1(); + + placeholder1 - placeholder2; +} + +step myStep2() -> (a: Int, b: Int) { + yield b = 1; +} + +workflow recordAssignmentWithMissingYield { + _, val placeholder = myStep2(); + + placeholder; +} + +step myStep3() -> (a: Int) { + yield b = 2; + yield a = 1; +} + +workflow recordAssignmentWithAdditionalYield { + val placeholder = myStep3(); + + placeholder; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/annotationCall/externalsInOtherPackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/annotationCall/externalsInOtherPackage.smltest new file mode 100644 index 000000000..7a863ebd4 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/annotationCall/externalsInOtherPackage.smltest @@ -0,0 +1,4 @@ +package tests.languageTests.scoping.annotationCall2 + +annotation AnnotationInOtherPackage1 +annotation AnnotationInOtherPackage2 diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/annotationCall/externalsInSamePackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/annotationCall/externalsInSamePackage.smltest new file mode 100644 index 000000000..030e9c07f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/annotationCall/externalsInSamePackage.smltest @@ -0,0 +1,3 @@ +package tests.languageTests.scoping.annotationCall1 + +annotation AnnotationInSamePackage diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/annotationCall/main.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/annotationCall/main.smltest new file mode 100644 index 000000000..1a1ca0924 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/annotationCall/main.smltest @@ -0,0 +1,15 @@ +package tests.languageTests.scoping.annotationCall1 + +import tests.languageTests.scoping.annotationCall2.AnnotationInOtherPackage1 + +annotation AnnotationInSameFile + +class NotAnAnnotation + +@AnnotationInSameFile +@AnnotationInSamePackage +@AnnotationInOtherPackage1 +@AnnotationInOtherPackage2 +@UnresolvedAnnotation +@NotAnAnnotation +class MyClass diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/argument/externalsInOtherPackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/argument/externalsInOtherPackage.smltest new file mode 100644 index 000000000..ed585b62d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/argument/externalsInOtherPackage.smltest @@ -0,0 +1,4 @@ +package tests.languageTests.scoping.argument2 + +fun functionInOtherPackage1(parameterInOtherPackage1: Int) +fun functionInOtherPackage2(parameterInOtherPackage2: Int) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/argument/externalsInSamePackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/argument/externalsInSamePackage.smltest new file mode 100644 index 000000000..7339df028 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/argument/externalsInSamePackage.smltest @@ -0,0 +1,3 @@ +package tests.languageTests.scoping.argument1 + +fun functionInSamePackage(parameterInSamePackage: Int) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/argument/main.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/argument/main.smltest new file mode 100644 index 000000000..b16927fdf --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/argument/main.smltest @@ -0,0 +1,40 @@ +package tests.languageTests.scoping.argument1 + +import tests.languageTests.scoping.argument2.functionInOtherPackage1 + +annotation AnnotationInSameFile(parameterInAnnotationInSameFile: Int) + +class ClassInSameFile(parameterInClassInSameFile: Int) + +enum EnumInSameFile { + EnumVariantInSameFile(parameterInEnumVariantInSameFile: Int) +} + +@AnnotationInSameFile(parameterInAnnotationInSameFile = 1) +fun functionInSameFile(parameterInFunctionSameFile: Int) + +step stepInSameFile(parameterInStepInSameFile: Int) {} + +enum notAParameter + +step stepForArgument(callableInSameStep: (parameterInCallableInSameStep: Int) -> ()) { + val blockLambdaInSameStep = (parameterInBlockLambdaInSameStep) {}; + val expressionLambdaInSameStep = (parameterInExpressionLambdaInSameStep) -> 1; + + blockLambdaInSameStep(parameterInBlockLambdaInSameStep = 1); + callableInSameStep(parameterInCallableInSameStep = 1); + ClassInSameFile(parameterInClassInSameFile = 1); + EnumInSameFile.EnumVariantInSameFile(parameterInEnumVariantInSameFile = 1); + expressionLambdaInSameStep(parameterInExpressionLambdaInSameStep = 1); + functionInSameFile(parameterInFunctionSameFile = 1); + stepInSameFile(parameterInStepInSameFile = 1); + + functionInSamePackage(parameterInSamePackage = 1); + functionInOtherPackage1(parameterInOtherPackage1 = 1); + functionInOtherPackage2(parameterInOtherPackage2 = 1); + functionInSameFile(parameterInSamePackage = 1); + functionInSameFile(parameterInOtherPackage1 = 1); + functionInSameFile(parameterInOtherPackage2 = 1); + functionInSameFile(unresolvedParameter = 1); + functionInSameFile(notAParameter = 1); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/importWithAlias/externalsInOtherPackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/importWithAlias/externalsInOtherPackage.smltest new file mode 100644 index 000000000..67b1c36d0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/importWithAlias/externalsInOtherPackage.smltest @@ -0,0 +1,3 @@ +package tests.languageTests.scoping.importWithAlias2 + +class ClassInOtherPackage diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/importWithAlias/externalsInSamePackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/importWithAlias/externalsInSamePackage.smltest new file mode 100644 index 000000000..f72f3ecb7 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/importWithAlias/externalsInSamePackage.smltest @@ -0,0 +1,3 @@ +package tests.languageTests.scoping.importWithAlias1 + +class ClassInSamePackage diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/importWithAlias/main.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/importWithAlias/main.smltest new file mode 100644 index 000000000..9da5c6bff --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/importWithAlias/main.smltest @@ -0,0 +1,18 @@ +package tests.languageTests.scoping.importWithAlias1 + +import tests.languageTests.scoping.importWithAlias1.ClassInSameFile as Cls1 +import tests.languageTests.scoping.importWithAlias1.ClassInSamePackage as Cls2 +import tests.languageTests.scoping.importWithAlias2.ClassInOtherPackage as Cls3 + +class ClassInSameFile + +fun importWithAlias( + aliasNameInSameFile: Cls1, + originalNameInSameFile: ClassInSameFile, + + aliasNameInSamePackage: Cls2, + originalNameInSamePackage: ClassInSamePackage, + + aliasNameInOtherPackage: Cls3, + originalNameInOtherPackage: ClassInOtherPackage +) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/namedType/externalsInOtherPackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/namedType/externalsInOtherPackage.smltest new file mode 100644 index 000000000..1e988aef5 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/namedType/externalsInOtherPackage.smltest @@ -0,0 +1,6 @@ +package tests.languageTests.scoping.namedType2 + +class ClassInOtherPackage1 +class ClassInOtherPackage2 +enum EnumInOtherPackage1 +enum EnumInOtherPackage2 diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/namedType/externalsInSamePackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/namedType/externalsInSamePackage.smltest new file mode 100644 index 000000000..b08f69452 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/namedType/externalsInSamePackage.smltest @@ -0,0 +1,4 @@ +package tests.languageTests.scoping.namedType1 + +class ClassInSamePackage +enum EnumInSamePackage diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/namedType/main.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/namedType/main.smltest new file mode 100644 index 000000000..3b135cc06 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/namedType/main.smltest @@ -0,0 +1,65 @@ +package tests.languageTests.scoping.namedType1 + +import tests.languageTests.scoping.namedType2.ClassInOtherPackage1 +import tests.languageTests.scoping.namedType2.EnumInOtherPackage1 + +class SuperClass { + class ClassInSuperClass + enum EnumInSuperClass +} +class ClassInSameFile sub SuperClass { + class ClassInClassInSameFile + enum EnumInClassInSameFile +} + +enum EnumInSameFile { + EnumVariantInSameFile +} + +fun NotANamedTypeDeclaration() + +fun directNamedTypes( + paramClassInSameFile: ClassInSameFile<*>, + paramEnumInSameFile: EnumInSameFile, + + paramClassInSamePackage: ClassInSamePackage<*>, + paramEnumInSamePackage: EnumInSamePackage, + + paramClassInOtherPackage1: ClassInOtherPackage1<*>, + paramEnumInOtherPackage1: EnumInOtherPackage1, + + paramClassInOtherPackage2: ClassInOtherPackage2<*>, + paramEnumInOtherPackage2: EnumInOtherPackage2, + + paramTypeParameterInSameFunction: TYPE_PARAMETER_IN_SAME_FUNCTION, + paramTypeParameterInSameFile: TYPE_PARAMETER_IN_SAME_FILE, + paramTypeParameterInSamePackage: TYPE_PARAMETER_IN_SAME_PACKAGE, + paramTypeParameterInOtherPackage: TYPE_PARAMETER_IN_OTHER_PACKAGE, + + paramUnresolvedNamedTypeDeclaration: UnresolvedNamedTypeDeclaration, + paramNotANamedTypeDeclaration: NotANamedTypeDeclaration +) + +fun memberTypes( + paramClassInClassInSameFile: ClassInSameFile<*>.ClassInClassInSameFile, + paramEnumInClassInSameFile: ClassInSameFile<*>.EnumInClassInSameFile, + paramEnumVariantInSameFile: EnumInSameFile.EnumVariantInSameFile, + + paramUnqualifiedClassInClassInSameFile: ClassInClassInSameFile, + paramUnqualifiedEnumInClassInSameFile: EnumInClassInSameFile, + paramUnqualifiedEnumVariantInSameFile: EnumVariantInSameFile, +) + +fun inheritedMemberTypes( + paramClassInSuperClass: ClassInSameFile<*>.ClassInSuperClass, + paramEnumInSuperClass: ClassInSameFile<*>.EnumInSuperClass, +) + +class ClassWithTypeParameter { + attr attributeInClassWithTypeParameter: TYPE_PARAMETER_IN_OUTER_CLASS + class NestedClass(paramClassInClassWithTypeParameter: TYPE_PARAMETER_IN_OUTER_CLASS) + enum NestedEnum { + Variant(paramEnumInClassWithTypeParameter: TYPE_PARAMETER_IN_OUTER_CLASS) + } + fun method(paramMethodInClassWithTypeParameter: TYPE_PARAMETER_IN_OUTER_CLASS) +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/protocolReference/main.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/protocolReference/main.smltest new file mode 100644 index 000000000..e7258f804 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/protocolReference/main.smltest @@ -0,0 +1,55 @@ +class SuperClass { + static attr superClassStaticAttribute: Int + attr superClassInstanceAttribute: Int + + static fun superClassStaticMethod() + fun superClassInstanceMethod() + + fun overridden() +} + +class ContainerClass { + static attr containerClassStaticAttribute: Int + attr containerClassInstanceAttribute: Int + + static fun containerClassStaticMethod() + fun containerClassInstanceMethod() + + class SubClass sub SuperClass { + static attr subClassStaticAttribute: Int + attr subClassInstanceAttribute: Int + + static fun subClassStaticMethod() + fun subClassInstanceMethod() + + fun overridden() + attr shadowed: Int + + enum NotAProtocolToken + + protocol { + subterm superClassStaticAttributeReference = superClassStaticAttribute; + subterm superClassInstanceAttributeReference = superClassInstanceAttribute; + subterm superClassStaticMethodReference = superClassStaticMethod; + subterm superClassInstanceMethodReference = superClassInstanceMethod; + subterm containerClassStaticAttributeReference = containerClassStaticAttribute; + subterm containerClassInstanceAttributeReference = containerClassInstanceAttribute; + subterm containerClassStaticMethodReference = containerClassStaticMethod; + subterm containerClassInstanceMethodReference = containerClassInstanceMethod; + subterm subClassStaticAttributeReference = subClassStaticAttribute; + subterm subClassInstanceAttributeReference = subClassInstanceAttribute; + subterm subClassStaticMethodReference = subClassStaticMethod; + subterm subClassInstanceMethodReference = subClassInstanceMethod; + subterm notAProtocolTokenReference = NotAProtocolToken; + subterm unresolvedReference = unresolved; + + subterm forwardReference = subtermReference; + subterm subtermReference = forwardReference; + + subterm overriddenReference = overridden; + + subterm shadowed = shadowed; + subterm shadowedReference = shadowed; + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/reference/externalsInOtherPackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/reference/externalsInOtherPackage.smltest new file mode 100644 index 000000000..7c3c3cae8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/reference/externalsInOtherPackage.smltest @@ -0,0 +1,21 @@ +package tests.languageTests.scoping.reference2 + +annotation AnnotationInOtherPackage1 +annotation AnnotationInOtherPackage2 + +class ClassInOtherPackage1 +class ClassInOtherPackage2 + +enum EnumInOtherPackage1 +enum EnumInOtherPackage2 + +fun globalFunctionInOtherPackage1() +fun globalFunctionInOtherPackage2() + +step stepInOtherPackage1() {} +step stepInOtherPackage2() {} +internal step internalStepInOtherPackage() {} +private step privateStepInOtherPackage() {} + +workflow workflowInOtherPackage1 {} +workflow workflowInOtherPackage2 {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/reference/externalsInSamePackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/reference/externalsInSamePackage.smltest new file mode 100644 index 000000000..b39b87246 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/reference/externalsInSamePackage.smltest @@ -0,0 +1,12 @@ +package tests.languageTests.scoping.reference1 + +annotation AnnotationInSamePackage +class ClassInSamePackage +enum EnumInSamePackage +fun globalFunctionInSamePackage() + +step stepInSamePackage() {} +internal step internalStepInSamePackage() {} +private step privateStepInSamePackage() {} + +workflow workflowInSamePackage {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/reference/main.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/reference/main.smltest new file mode 100644 index 000000000..0c26d7f56 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/reference/main.smltest @@ -0,0 +1,385 @@ +package tests.languageTests.scoping.reference1 + +import tests.languageTests.scoping.reference2.AnnotationInOtherPackage1 +import tests.languageTests.scoping.reference2.ClassInOtherPackage1 +import tests.languageTests.scoping.reference2.EnumInOtherPackage1 +import tests.languageTests.scoping.reference2.globalFunctionInOtherPackage1 +import tests.languageTests.scoping.reference2.stepInOtherPackage1 +import tests.languageTests.scoping.reference2.internalStepInOtherPackage +import tests.languageTests.scoping.reference2.privateStepInOtherPackage +import tests.languageTests.scoping.reference2.workflowInOtherPackage1 + +annotation AnnotationInSameFile + +class SuperClass { + static attr superClassStaticAttribute: Int + attr superClassInstanceAttribute: Int + + class ClassInSuperClass + + enum EnumInSuperClass + + static fun superClassStaticMethod() + fun superClassInstanceMethod() +} +class ClassInSameFile() sub SuperClass { + static attr classStaticAttributeInSameFile: Int + attr classInstanceAttributeInSameFile: Int + + class ClassInClassInSameFile + + enum EnumInClassInSameFile + + static fun classStaticMethodInSameFile() -> classStaticMethodResultInSameFile: Int + fun classInstanceMethodInSameFile() -> classInstanceMethodResultInSameFile: Int +} + +enum EnumInSameFile { + EnumVariantInSameFile(enumVariantParameterInSameFile: Int) +} + +fun globalFunctionInSameFile(globalFunctionParameterInSameFile: Int) -> globalFunctionResultInSameFile: Int +fun globalFunctionWithOneResultWithIdenticalMember() -> result: ClassForResultMemberAccess +fun globalFunctionWithTwoResults() -> (result1: Int, result2: Int) + + +step stepInSameFile(stepParameterInSameFile: Int) -> stepResultInSameFile: Int { + val placeholderInSameFile = 1; +} +step stepWithOneResultWithIdenticalMember() -> result: ClassForResultMemberAccess {} +step stepWithTwoResults() -> (result1: Int, result2: Int) {} + +step internalStepInSameFile() {} +step privateStepInSameFile() {} + +workflow workflowInSameFile {} + +class SuperClassForOverriding() { + attr instanceAttributeForOverriding: Int + fun instanceMethodForOverriding() +} +class SubClassForOverriding() +sub SuperClassForOverriding { + attr instanceAttributeForOverriding: Int + fun instanceMethodForOverriding() +} + +class SuperClassForHiding { + static attr staticAttributeForHiding: Int + class NestedClassForHiding + enum NestedEnumForHiding + static fun staticMethodForHiding() +} +class SubClassForHiding sub SuperClassForHiding { + static attr staticAttributeForHiding: Int + class NestedClassForHiding + enum NestedEnumForHiding + static fun staticMethodForHiding() +} + +class ClassForResultMemberAccess() { + attr result: Int +} +enum EnumForResultMemberAccess { + result +} + + +// Direct references ----------------------------------------------------------- + +step directReferencesToAnnotations() { + AnnotationInSameFile; + AnnotationInSamePackage; + AnnotationInOtherPackage1; + AnnotationInOtherPackage2; +} + +step directReferencesToClasses() { + ClassInSameFile; + ClassInSamePackage; + ClassInOtherPackage1; + ClassInOtherPackage2; +} + +step directReferencesToEnums() { + EnumInSameFile; + EnumInSamePackage; + EnumInOtherPackage1; + EnumInOtherPackage2; +} + +step directReferencesToGlobalFunctions() { + globalFunctionInSameFile; + globalFunctionInSamePackage; + globalFunctionInOtherPackage1; + globalFunctionInOtherPackage2; +} + +step directReferencesToLambdaResults() { + val f = () { + yield lambdaResult = 1; + lambdaResult; + }; +} + +step directReferencesToParameters(parameterInStep: Int) { + parameterInStep; + + val f = (parameterInBlockLambda) { + parameterInStep; + parameterInBlockLambda; + + val f = () { + parameterInStep; + parameterInBlockLambda; + }; + }; + + val g = (parameterInExpressionLambda) -> parameterInExpressionLambda; +} + +step directReferencesToPlaceholders() { + val placeholderInStep = 1; + placeholderInStep; + + val f = () { + val placeholderInLambda = 1; + placeholderInStep; + placeholderInLambda; + + val f = () { + placeholderInStep; + placeholderInLambda; + }; + }; +} + +step directReferencesToTypeParameters() { + TYPE_PARAMETER_IN_SAME_FILE; +} + +step directReferencesToSteps() { + stepInSameFile; + stepInSamePackage; + stepInOtherPackage1; + stepInOtherPackage2; + + internalStepInSameFile; + privateStepInSameFile; + internalStepInSamePackage; + privateStepInSamePackage; + internalStepInOtherPackage; + privateStepInOtherPackage; +} + +step directReferencesToWorkflows() { + workflowInSameFile; + workflowInSamePackage; + workflowInOtherPackage1; + workflowInOtherPackage2; +} + +step forwardReferences() { + a; + val f = () { + a; + f; + }; + + val a = 1; +} + +step shadowedReferences(ClassInSameFile: Int) { + ClassInSameFile; + + val ClassInSameFile = 1; + ClassInSameFile; + + val f = (ClassInSameFile) { + ClassInSameFile; + + val ClassInSameFile = 1; + ClassInSameFile; + }; +} + +// Access to own members ------------------------------------------------------- + +step referencesToClassMembers() { + ClassInSameFile.classStaticAttributeInSameFile; + ClassInSameFile<*>().classInstanceAttributeInSameFile; + ClassInSameFile.ClassInClassInSameFile; + ClassInSameFile.EnumInClassInSameFile; + ClassInSameFile.classStaticMethodInSameFile; + ClassInSameFile<*>().classInstanceMethodInSameFile(); +} + +@Target(EnumInSameFile.EnumVariantInSameFile) +step referencesToEnumVariants(@Target(EnumInSameFile.EnumVariantInSameFile) referenceToEnumVariantFromParameterAnnotation: Int) { + EnumInSameFile.EnumVariantInSameFile; +} + +class ReferencesToEnumVariants { + @Target(EnumInSameClass.EnumVariantInSameClass) + @Target(EnumInSameFile.EnumVariantInSameFile) + class ReferencesToEnumVariantsInnerClass + + enum EnumInSameClass { + EnumVariantInSameClass + } +} + +step referencesToEnumVariantParameters() { + EnumInSameFile.EnumVariantInSameFile.enumVariantParameterInSameFile; +} + + +// Access to inherited members ------------------------------------------------- + +step referencesToInheritedClassMembers() { + ClassInSameFile.superClassStaticAttribute; + ClassInSameFile<*>().superClassInstanceAttribute; + ClassInSameFile.ClassInSuperClass; + ClassInSameFile.EnumInSuperClass; + ClassInSameFile.superClassStaticMethod; + ClassInSameFile<*>().superClassInstanceMethod(); +} + + +// Overriding ------------------------------------------------------------------ +step referencesToOverriddenMembers() { + SuperClassForOverriding().instanceAttributeForOverriding; + SuperClassForOverriding().instanceMethodForOverriding(); + + SubClassForOverriding().instanceAttributeForOverriding; + SubClassForOverriding().instanceMethodForOverriding(); +} + +// Hiding ---------------------------------------------------------------------- +step referencesToHiddenMembers() { + SubClassForHiding.staticAttributeForHiding; + SubClassForHiding.NestedClassForHiding; + SubClassForHiding.NestedEnumForHiding; + SubClassForHiding.staticMethodForHiding; +} + +// Access to static members from instance -------------------------------------- + +step referencesToStaticClassMembersFromInstance() { + ClassInSameFile<*>().classStaticAttributeInSameFile; + ClassInSameFile<*>().ClassInClassInSameFile; + ClassInSameFile<*>().EnumInClassInSameFile; + ClassInSameFile<*>().classStaticMethodInSameFile; + + ClassInSameFile<*>().superClassStaticAttribute; + ClassInSameFile<*>().ClassInSuperClass; + ClassInSameFile<*>().EnumInSuperClass; + ClassInSameFile<*>().superClassStaticMethod; +} + + +// Access to instance members from class --------------------------------------- + +step referencesToInstanceClassMembersFromClass() { + ClassInSameFile.classInstanceAttributeInSameFile; + ClassInSameFile.classInstanceMethodInSameFile(); + + ClassInSameFile.superClassInstanceAttribute; + ClassInSameFile.superClassInstanceMethod(); +} + + +// Access to results of callable ----------------------------------------------- + +step referencesToCallableTypeResults( + callableWithOneResult: () -> (singleResult: Int), + callableWithOneResultWithIdenticalClassAttribute: () -> (result: ClassForResultMemberAccess), + callableWithOneResultWithIdenticalEnumVariant: () -> (result: EnumForResultMemberAccess), + callableWithTwoResults: () -> (result1: Int, result2: Int) +) { + callableWithOneResult().singleResult; + callableWithOneResultWithIdenticalClassAttribute().result; + callableWithOneResultWithIdenticalEnumVariant().result; + callableWithTwoResults().result1; +} + +step referencesToFunctionResults() { + globalFunctionInSameFile(1).globalFunctionResultInSameFile; + globalFunctionWithOneResultWithIdenticalMember().result; + globalFunctionWithTwoResults().result1; +} + +step referencesToLambdaResults() { + val lambdaWithOneResult = () { + yield singleResult = 1; + }; + val lambdaWithOneResultWithIdenticalMember = () { + yield result = ClassForResultMemberAccess(); + }; + val lambdaWithTwoResults = () { + yield result1 = 1; + yield result2 = 1; + }; + + lambdaWithOneResult().singleResult; + lambdaWithOneResultWithIdenticalMember().result; + lambdaWithTwoResults().result1; +} + +step referencesToStepResults() { + stepInSameFile(1).stepResultInSameFile; + stepWithOneResultWithIdenticalMember().result; + stepWithTwoResults().result1; +} + +// Access to locals from outside ----------------------------------------------- + +step referencesToFunctionLocals() { + globalFunctionParameterInSameFile; + globalFunctionResultInSameFile; +} + +step referencesToLambdaLocals() { + val f = (lambdaParameter) { + val lambdaPlaceholder = 1; + yield lambdaYield = 1; + }; + + lambdaParameter; + lambdaPlaceholder; + lambdaYield; +} + +step referencesToStepLocals() { + stepParameterInSameFile; + stepResultInSameFile; + placeholderInSameFile; +} + +// Unqualified access to members ----------------------------------------------- + +step unqualifiedReferencesToClassMembers() { + classStaticAttributeInSameFile; + classInstanceAttributeInSameFile; + + ClassInClassInSameFile; + EnumInClassInSameFile; + + classStaticMethodInSameFile; + classInstanceMethodInSameFile; +} + +step unqualifiedReferencesToEnumVariants() { + EnumVariantInSameFile; +} + +step unqualifiedReferencesToEnumVariantParameters() { + enumVariantParameterInSameFile; +} + + +// Other unresolved references ------------------------------------------------- + +step unresolvedReferences() { + unresolvedReference; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeArgument/externalsInOtherPackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeArgument/externalsInOtherPackage.smltest new file mode 100644 index 000000000..13caa0468 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeArgument/externalsInOtherPackage.smltest @@ -0,0 +1,4 @@ +package tests.languageTests.scoping.typeArgument2 + +fun functionInOtherPackage1() +fun functionInOtherPackage2() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeArgument/externalsInSamePackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeArgument/externalsInSamePackage.smltest new file mode 100644 index 000000000..1d3965577 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeArgument/externalsInSamePackage.smltest @@ -0,0 +1,3 @@ +package tests.languageTests.scoping.typeArgument1 + +fun functionInSamePackage() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeArgument/main.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeArgument/main.smltest new file mode 100644 index 000000000..8525cb645 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeArgument/main.smltest @@ -0,0 +1,28 @@ +package tests.languageTests.scoping.typeArgument1 + +import tests.languageTests.scoping.typeArgument2.functionInOtherPackage1 + +class ClassInSameFile + +enum EnumInSameFile{ + EnumVariantInSameFile() +} + +fun functionInSameFile() + +step NOT_A_TYPE_PARAMETER1() {} + +workflow workflowForTypeArgument { + ClassInSameFile(); + EnumInSameFile.EnumVariantInSameFile(); + functionInSameFile(); + functionInSamePackage(); + functionInOtherPackage1(); + functionInOtherPackage2(); + + functionInSameFile(); + functionInSameFile(); + functionInSameFile(); + functionInSameFile(); + functionInSameFile(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeParameterConstraint/externalsInOtherPackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeParameterConstraint/externalsInOtherPackage.smltest new file mode 100644 index 000000000..5bb030086 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeParameterConstraint/externalsInOtherPackage.smltest @@ -0,0 +1,4 @@ +package tests.languageTests.scoping.typeParameterConstraint2 + +fun functionInOtherPackage1() +fun functionInOtherPackage2() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeParameterConstraint/externalsInSamePackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeParameterConstraint/externalsInSamePackage.smltest new file mode 100644 index 000000000..8f9dd51ac --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeParameterConstraint/externalsInSamePackage.smltest @@ -0,0 +1,3 @@ +package tests.languageTests.scoping.typeParameterConstraint1 + +fun functionInSamePackage() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeParameterConstraint/main.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeParameterConstraint/main.smltest new file mode 100644 index 000000000..6f04f61c8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/typeParameterConstraint/main.smltest @@ -0,0 +1,24 @@ +package tests.languageTests.scoping.typeParameterConstraint1 + +import tests.languageTests.scoping.typeParameterConstraint2.functionInOtherPackage1 + +fun functionInSameFile() + +annotation NOT_A_TYPE_PARAMETER + +class TestClass +where TYPE_PARAMETER_IN_SAME_CLASS sub Int + +enum TestEnum { + TestEnumVariant + where TYPE_PARAMETER_IN_SAME_ENUM_VARIANT sub Int +} + +fun testFunction() +where TYPE_PARAMETER_IN_SAME_FUNCTION sub Int, + TYPE_PARAMETER_IN_SAME_FILE sub Int, + TYPE_PARAMETER_IN_SAME_PACKAGE sub Int, + TYPE_PARAMETER_IN_OTHER_PACKAGE1 sub Int, + TYPE_PARAMETER_IN_OTHER_PACKAGE2 sub Int, + NOT_A_TYPE_PARAMETER sub Int, + UNRESOLVED_TYPE_PARAMETER sub Int diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/yield/externalsInOtherPackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/yield/externalsInOtherPackage.smltest new file mode 100644 index 000000000..bce63bb75 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/yield/externalsInOtherPackage.smltest @@ -0,0 +1,4 @@ +package tests.languageTests.scoping.yield2 + +step stepInOtherPackage1() -> resultInOtherPackage1: Int {} +step stepInOtherPackage2() -> resultInOtherPackage2: Int {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/yield/externalsInSamePackage.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/yield/externalsInSamePackage.smltest new file mode 100644 index 000000000..d56c8f4c7 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/yield/externalsInSamePackage.smltest @@ -0,0 +1,3 @@ +package tests.languageTests.scoping.yield1 + +step stepInSamePackage() -> resultInSamePackage: Int {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/scoping/yield/main.smltest b/DSL/de.unibonn.simpleml/src/test/resources/scoping/yield/main.smltest new file mode 100644 index 000000000..a61386a25 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/scoping/yield/main.smltest @@ -0,0 +1,17 @@ +package tests.languageTests.scoping.yield1 + +import tests.languageTests.scoping.yield2.stepInOtherPackage1 + +step stepInSameFile() -> resultInSameFile: Int {} + +class notAResult + +step stepInSameStep() -> resultInSameStep: Int { + yield resultInSameStep = 1; + yield resultInSameFile = 1; + yield resultInSamePackage = 1; + yield resultInOtherPackage1 = 1; + yield resultInOtherPackage2 = 1; + yield unresolvedResult = 1; + yield notAResult = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/serialization/extensionsTest.smltest b/DSL/de.unibonn.simpleml/src/test/resources/serialization/extensionsTest.smltest new file mode 100644 index 000000000..3f234796b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/serialization/extensionsTest.smltest @@ -0,0 +1 @@ +package tests class MyClass { attr myAttribute: Int } diff --git a/DSL/de.unibonn.simpleml/src/test/resources/staticAnalysis/recursion.smltest b/DSL/de.unibonn.simpleml/src/test/resources/staticAnalysis/recursion.smltest new file mode 100644 index 000000000..19e9248d8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/staticAnalysis/recursion.smltest @@ -0,0 +1,90 @@ +package tests.staticAnalysis.recursion + +// Positive examples ----------------------------------------------------------- + +annotation CallsShouldBeRecursive + +// Direct recursion + +@CallsShouldBeRecursive +step directRecursion(a: Any or directRecursion()) { + directRecursion(); + 1 + directRecursion(); + val a = directRecursion(); +} + +// Transitive recursion + +@CallsShouldBeRecursive +step transitiveRecursion1() { + transitiveRecursion2(); + val a = transitiveRecursion2(); +} + +@CallsShouldBeRecursive +step transitiveRecursion2() { + transitiveRecursion3(); + val a = transitiveRecursion3(); +} + +@CallsShouldBeRecursive +step transitiveRecursion3() { + transitiveRecursion2(); + val a = transitiveRecursion2(); +} + +// Deferred recursion in lambda + +@CallsShouldBeRecursive +step deferredRecursionInLambda() { + (() { directRecursion(); })(); + (() -> directRecursion())(); +} + +// Negative examples ----------------------------------------------------------- + +annotation CallsShouldNotBeRecursive + +// Normal calls + +@CallsShouldNotBeRecursive +step normalCall(f: () -> ()) { + f(); + (() {})(); + (() -> null)(); + + MyClass(); + MyEnum.Variant(); + myFun(); + myStep(); +} + +class MyClass() +enum MyEnum { + Variant() +} +fun myFun() +step myStep() {} + +// Uncalled lambda + +@CallsShouldNotBeRecursive +step uncalledLambda() { + () { uncalledLambda(); }; + () -> uncalledLambda(); +} + +// Lambda recursion (already handled by scoping) + +@CallsShouldNotBeRecursive +step lambdaRecursion() { + val a = () { a(); }; + val b = () -> b(); +} + +// Unresolved callable + +@CallsShouldNotBeRecursive +step unresolvedCallable() { + unresolved(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/staticAnalysis/sideEffects.smltest b/DSL/de.unibonn.simpleml/src/test/resources/staticAnalysis/sideEffects.smltest new file mode 100644 index 000000000..38ea9255a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/staticAnalysis/sideEffects.smltest @@ -0,0 +1,147 @@ +package tests.staticAnalysis.sideEffects + +// Positive examples ----------------------------------------------------------- + +annotation ShouldHaveNoSideEffects + +// Call to class constructor + +class C() + +@ShouldHaveNoSideEffects +step callOfClassConstructor() { + C(); +} + +// Call to enum variant constructor + +enum MyEnum { + Variant +} + +@ShouldHaveNoSideEffects +step callOfEnumVariantConstructor() { + MyEnum.Variant(); +} + +// Function without side effects + +@Pure +fun pureFunction() + +@NoSideEffects +fun functionWithoutSideEffects() + +@ShouldHaveNoSideEffects +step callToPureFunction() { + pureFunction(); + functionWithoutSideEffects(); +} + +// Lambdas without side effects + +@ShouldHaveNoSideEffects +step callToPureLambdas() { + (() {})(); + (() -> null)(); + + () { + (() {})(); + }; + + () -> (() -> null)(); +} + +// Steps without side effects + +step pureStep() {} + +@ShouldHaveNoSideEffects +step callToPureSteps() { + pureStep(); +} + +// Uncalled lambdas + +step pureStepWithUncalledLambdas() { + () -> impureFunction(); +} + +@ShouldHaveNoSideEffects +step uncalledLambdas() { + pureStepWithUncalledLambdas(); +} + +// Function as result + +@ShouldHaveNoSideEffects +step pureFunctionAsResult() { + (() -> pureFunction)()(); +} + +// Negative examples ----------------------------------------------------------- + +annotation ShouldHaveSideEffects + +// Callable type + +@ShouldHaveSideEffects +step callToCallableType(f: () -> ()) { + f(); +} + +// Function with side effects + +fun impureFunction() + +@ShouldHaveSideEffects +step callToImpureFunction() { + impureFunction(); +} + +// Lambdas with side effects + +@ShouldHaveSideEffects +step callToImpureLambdas() { + (() { impureFunction(); })(); + (() -> impureFunction())(); + + () { + (() { impureFunction(); })(); + }; + + () -> (() -> impureFunction())(); +} + +// Steps with side effects + +step impureStep() { + impureFunction(); +} + +@ShouldHaveSideEffects +step callToImpureSteps() { + impureStep(); +} + +// Recursion + +@ShouldHaveSideEffects +step recursion() { + recursion(); +} + +// Unresolved callable + +@ShouldHaveSideEffects +step unresolvedCallable() { + unresolved(); +} + +// Function as parameter + +@ShouldHaveSideEffects +step impureFunctionAsParameter() { + ((f) -> f())(pureFunction); // This is actually pure, but we match in a conservative manner. Can be improved later. + ((f) -> f())(impureFunction); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/deprecated.smltest b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/deprecated.smltest new file mode 100644 index 000000000..c0ddd0e16 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/deprecated.smltest @@ -0,0 +1,4 @@ +fun nonDeprecatedFunction() + +@Deprecated +fun deprecatedFunction() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/description.smltest b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/description.smltest new file mode 100644 index 000000000..ce4e0cac2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/description.smltest @@ -0,0 +1,8 @@ +fun functionWithoutDescription() + +@Description("Lorem ipsum") +fun functionWithUniqueDescription() + +@Description("Lorem ipsum 1") +@Description("Lorem ipsum 2") +fun functionWithMultipleDescriptions() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pure.smltest b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pure.smltest new file mode 100644 index 000000000..1abd4abea --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pure.smltest @@ -0,0 +1,4 @@ +fun impureFunction() + +@Pure +fun pureFunction() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pythonModule.smltest b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pythonModule.smltest new file mode 100644 index 000000000..3851d3f7e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pythonModule.smltest @@ -0,0 +1,2 @@ +@PythonModule("python_module") +package compilationUnitWithUniquePythonModule diff --git a/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pythonModuleMissing.smltest b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pythonModuleMissing.smltest new file mode 100644 index 000000000..da8f8932b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pythonModuleMissing.smltest @@ -0,0 +1 @@ +package compilationUnitWithoutPythonModule diff --git a/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pythonModuleMultipleAnnotations.smltest b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pythonModuleMultipleAnnotations.smltest new file mode 100644 index 000000000..22e3b3228 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pythonModuleMultipleAnnotations.smltest @@ -0,0 +1,3 @@ +@PythonModule("python_module_1") +@PythonModule("python_module_2") +package compilationUnitWithMultiplePythonModules diff --git a/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pythonName.smltest b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pythonName.smltest new file mode 100644 index 000000000..8a76a5ae9 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/pythonName.smltest @@ -0,0 +1,8 @@ +fun functionWithoutPythonName() + +@PythonName("function_with_python_name") +fun functionWithUniquePythonName() + +@PythonName("function_with_python_name_1") +@PythonName("function_with_python_name_2") +fun functionWithMultiplePythonNames() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/repeatable.smltest b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/repeatable.smltest new file mode 100644 index 000000000..e8dabdbee --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/repeatable.smltest @@ -0,0 +1,4 @@ +annotation UnrepeatableAnnotation + +@Repeatable +annotation RepeatableAnnotation diff --git a/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/since.smltest b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/since.smltest new file mode 100644 index 000000000..5320833f0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/since.smltest @@ -0,0 +1,8 @@ +fun functionWithoutSince() + +@Since("1.0.0") +fun functionWithUniqueSince() + +@Since("1.0.0") +@Since("2.0.0") +fun functionWithMultipleSinces() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/target.smltest b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/target.smltest new file mode 100644 index 000000000..50d198aa8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/stdlibAccess/annotations/target.smltest @@ -0,0 +1,8 @@ +annotation AnnotationWithoutTarget + +@Target(AnnotationTarget.Class) +annotation AnnotationWithUniqueTarget + +@Target(AnnotationTarget.Class) +@Target(AnnotationTarget.Enum) +annotation AnnotationWithMultipleTargets diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/assignees/blockLambdaResults.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/assignees/blockLambdaResults.smltest new file mode 100644 index 000000000..fb4a3b40e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/assignees/blockLambdaResults.smltest @@ -0,0 +1,10 @@ +package tests.typeComputer.assignees.blockLambdaResults + +fun f(p: Any) + +workflow myWorkflow { + f(() { + yield r = 1; + yield s = ""; + }); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/assignees/placeholders.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/assignees/placeholders.smltest new file mode 100644 index 000000000..ae945e98a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/assignees/placeholders.smltest @@ -0,0 +1,8 @@ +package tests.typeComputer.assignees.placeholders + +fun f(p: Any) + +workflow myWorkflow { + val a = 1; + val b = ""; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/assignees/yields.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/assignees/yields.smltest new file mode 100644 index 000000000..508e01368 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/assignees/yields.smltest @@ -0,0 +1,8 @@ +package tests.typeComputer.assignees.yields + +fun f(p: Any) + +step myStep() -> (r: Int, s: String) { + yield r = 1; + yield s = ""; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/attributes.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/attributes.smltest new file mode 100644 index 000000000..10372f386 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/attributes.smltest @@ -0,0 +1,5 @@ +package tests.typeComputer.declarations.attributes + +class C { + attr a: Int +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/classes.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/classes.smltest new file mode 100644 index 000000000..829a20168 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/classes.smltest @@ -0,0 +1,3 @@ +package tests.typeComputer.declarations.classes + +class C diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/enumVariants.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/enumVariants.smltest new file mode 100644 index 000000000..40fe94ee2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/enumVariants.smltest @@ -0,0 +1,7 @@ +package tests.typeComputer.declarations.enumVariants + +enum E { + V +} + +step myStep(v: E.V) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/enums.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/enums.smltest new file mode 100644 index 000000000..1a333a1e6 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/enums.smltest @@ -0,0 +1,5 @@ +package tests.typeComputer.declarations.enums + +enum E + +step myStep(e: E) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/functions.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/functions.smltest new file mode 100644 index 000000000..f58211dc9 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/functions.smltest @@ -0,0 +1,3 @@ +package tests.typeComputer.declarations.functions + +fun f(a: Int, b: String) -> (r: String, s: Int) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/parameters.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/parameters.smltest new file mode 100644 index 000000000..24cc01658 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/parameters.smltest @@ -0,0 +1,17 @@ +package tests.typeComputer.declarations.parameters + +fun f(parameter: (a: String) -> r: String) + +step myStepWithNormalParameter(a: Int, b: String) {} +step myStepWithVariadicParameter(vararg param: Int) {} + +step myStepWithLambdas() -> ( + r: (a: String) -> r: String, + s: (a: String) -> r: String +) { + f((a) -> ""); + f((b) { yield r = ""; }); + + yield r = (c) -> ""; + yield s = (d) { yield r = ""; }; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/results.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/results.smltest new file mode 100644 index 000000000..bfbd2f177 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/results.smltest @@ -0,0 +1,6 @@ +package tests.typeComputer.declarations.results + +step myStep() -> (r: Int, s: String) { + yield r = 1; + yield s = ""; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/steps.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/steps.smltest new file mode 100644 index 000000000..15f1d444e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/declarations/steps.smltest @@ -0,0 +1,6 @@ +package tests.typeComputer.declarations.steps + +step s(a: Int, b: String) -> (r: String, s: Int) { + yield r = ""; + yield s = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/arguments.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/arguments.smltest new file mode 100644 index 000000000..b6c0b6309 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/arguments.smltest @@ -0,0 +1,8 @@ +package tests.typeComputer.expressions.parenthesizedExpressions + +fun f(x: Any?) + +workflow myWorkflow { + f(1); + f(x = ""); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/blockLambdas.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/blockLambdas.smltest new file mode 100644 index 000000000..787c84a5e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/blockLambdas.smltest @@ -0,0 +1,49 @@ +package tests.typeComputer.expressions.blockLambdas + +fun f( + parameter: (a: String, b: Int) -> (r: String, s: Int) +) + +step lambdasWithExplicitParameterTypes() -> ( + result: (a: String, b: Int) -> (r: String, s: Int) +) { + val myLambda = (a: Int, b: String) { + yield r = 1; + yield s = ""; + }; + yield result = (a: Int, b: String) { + yield r = 1; + yield s = ""; + }; + f( + (a: Int, b: String) { + yield r = 1; + yield s = ""; + } + ); +} + +step lambdasWithExplicitVariadicType() { + val myLambda = (a: Int, vararg b: String) { + yield r = 1; + yield s = ""; + }; +} + +step yieldedLambda() -> ( + result: (a: String, b: Int) -> (r: String, s: Int) +) { + yield result = (a, b) { + yield r = 1; + yield s = ""; + }; +} + +step argumentLambda() { + f( + (a, b) { + yield r = 1; + yield s = ""; + } + ); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/calls.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/calls.smltest new file mode 100644 index 000000000..1ff1b8a70 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/calls.smltest @@ -0,0 +1,37 @@ +package tests.typeComputer.expressions.calls + +class C() +enum E { + V(a: Int) +} +fun f1() -> r: String +fun f2() -> (r: String, s: Int) +step s1() -> r: String{ + yield r = ""; +} +step s2() -> (r: String, s: Int) { + yield r = ""; + yield s = 1; +} + +step mySteps( + p1: () -> r: String, + p2: () -> (r: String, s: Int) +) { + val classCall = C(); + val callableTypeCall1 = p1(); + val callableTypeCall2 = p2(); + val enumVariantCall = E.V(1); + val functionCall1 = f1(); + val functionCall2 = f2(); + val blockLambdaCall1 = (() { + yield r = ""; + })(); + val blockLambdaCall2 = (() { + yield r = ""; + yield s = 1; + })(); + val expressionLambdaCall = (() -> 1)(); + val stepCall1 = s1(); + val stepCall2 = s2(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/expressionLambdas.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/expressionLambdas.smltest new file mode 100644 index 000000000..0de86f5b0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/expressionLambdas.smltest @@ -0,0 +1,27 @@ +package tests.typeComputer.expressions.expressionLambdas + +fun f( + parameter: (a: String, b: Int) -> r: String +) + +step lambdasWithExplicitParameterTypes() -> ( + result: (a: String, b: Int) -> r: String +) { + val myLambda = (a: Int, b: String) -> 1; + yield result = (a: Int, b: String) -> 1; + f((a: Int, b: String) -> 1); +} + +step lambdasWithExplicitVariadicType() { + val myLambda = (a: Int, vararg b: String) -> 1; +} + +step yieldedLambda() -> ( + result: (a: String, b: Int) -> r: String +) { + yield result = (a, b) -> 1; +} + +step argumentLambda() { + f((a, b) -> 1); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/indexedAccesses.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/indexedAccesses.smltest new file mode 100644 index 000000000..05c3fdcc9 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/indexedAccesses.smltest @@ -0,0 +1,17 @@ +package tests.typeComputer.expressions.indexedAccesses + +step myStep1(vararg params: Int) { + params[0]; +} + +step myStep2(vararg params: String) { + params[0]; +} + +step myStep3(params: String) { + params[0]; +} + +step myStep4() { + unresolved[0]; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/literals.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/literals.smltest new file mode 100644 index 000000000..6654b85b8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/literals.smltest @@ -0,0 +1,9 @@ +package tests.typeComputer.expressions.literals + +workflow myWorkflow { + val booleanLiteral = true; + val floatLiteral = 1.0; + val intLiteral = 1; + val nullLiteral = null; + val stringLiteral = "myString"; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/memberAccesses.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/memberAccesses.smltest new file mode 100644 index 000000000..1c4c6e58f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/memberAccesses.smltest @@ -0,0 +1,19 @@ +package tests.typeComputer.expressions.memberAccesses + +class C { + static attr a: Int + static attr b: String + static attr c: Any? +} + +workflow myWorkflow { + C.a; + C.b; + C.c; + C.unresolved; + + C?.a; + C?.b; + C?.c; + C?.unresolved; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/arithmetic.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/arithmetic.smltest new file mode 100644 index 000000000..4d113cf47 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/arithmetic.smltest @@ -0,0 +1,21 @@ +package tests.typeComputer.operations.arithmetic + +workflow myWorkflow { + val additionIntInt = (1 + 1); + val subtractionIntInt = (1 - 1); + val multiplicationIntInt = (1 * 1); + val divisionIntInt = (1 / 1); + val negationInt = (-1); + + val additionIntFloat = (1 + 1.0); + val subtractionIntFloat = (1 - 1.0); + val multiplicationIntFloat = (1 * 1.0); + val divisionIntFloat = (1 / 1.0); + val negationFloat = (-1.0); + + val additionInvalid = (true + true); + val subtractionInvalid = (true - true); + val multiplicationInvalid = (true * true); + val divisionInvalid = (true / true); + val negationInvalid = (-true); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/comparison.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/comparison.smltest new file mode 100644 index 000000000..414a3232f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/comparison.smltest @@ -0,0 +1,12 @@ +package tests.typeComputer.operations.comparison + +workflow myWorkflow { + val lessThan = (1 < 1); + val lessThanOrEquals = (1 <= 1); + val greaterThanOrEquals = (1 >= 1); + val greaterThan = (1 > 1); + val lessThanInvalid = (true < true); + val lessThanOrEqualsInvalid = (true <= true); + val greaterThanOrEqualsInvalid = (true >= true); + val greaterThanInvalid = (true > true); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/elvis.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/elvis.smltest new file mode 100644 index 000000000..443d3878c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/elvis.smltest @@ -0,0 +1,21 @@ +package tests.typeComputer.operations.elvis + +fun intOrNull() -> a: Int? +fun stringOrNull() -> s: String? + +workflow elvisWithNonNullableLeftOperand { + 1 ?: intOrNull(); + 1 ?: 1; + 1 ?: 1.0; + 1 ?: ""; + 1 ?: null; +} + +workflow elvisWithNullableLeftOperand { + val intOrNullElseIntOrNull = intOrNull() ?: intOrNull(); + val intOrNullElseNull = intOrNull() ?: null; + val intOrNullElseInt = intOrNull() ?: 1; + val intOrNullElseFloat = intOrNull() ?: 1.0; + val intOrNullElseString = intOrNull() ?: ""; + val intOrNullElseStringOrNull = intOrNull() ?: stringOrNull(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/equality.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/equality.smltest new file mode 100644 index 000000000..92324fbb2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/equality.smltest @@ -0,0 +1,6 @@ +package tests.typeComputer.operations.equality + +workflow myWorkflow { + val equals = (1 == 1); + val notEquals = (1 != 1); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/logical.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/logical.smltest new file mode 100644 index 000000000..cba5cd4d2 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/logical.smltest @@ -0,0 +1,10 @@ +package tests.typeComputer.operations.logical + +workflow myWorkflow { + val conjunction = (true and true); + val disjunction = (true or true); + val negation = (not true); + val conjunctionInvalid = (1 and 1); + val disjunctionInvalid = (1.0 or 1.0); + val negationInvalid = (not "true"); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/strictEquality.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/strictEquality.smltest new file mode 100644 index 000000000..64c1d3628 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/operations/strictEquality.smltest @@ -0,0 +1,6 @@ +package tests.typeComputer.operations.strictEquality + +workflow myWorkflow { + val strictlyEquals = (1 === 1); + val notStrictlyEquals = (1 !== 1); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/parenthesizedExpressions.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/parenthesizedExpressions.smltest new file mode 100644 index 000000000..5098eacd6 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/parenthesizedExpressions.smltest @@ -0,0 +1,6 @@ +package tests.typeComputer.expressions.parenthesizedExpressions + +workflow myWorkflow { + (1); + (""); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/references.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/references.smltest new file mode 100644 index 000000000..da0c68313 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/references.smltest @@ -0,0 +1,10 @@ +package tests.typeComputer.expressions.references + +workflow myWorkflow { + val a = 1; + val b = ""; + + a; + b; + unresolved; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/templateStrings.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/templateStrings.smltest new file mode 100644 index 000000000..70372580b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/expressions/templateStrings.smltest @@ -0,0 +1,5 @@ +package tests.typeComputer.expressions.templateStrings + +workflow myWorkflow { + val templateString = "1 + 2 = {{ 1 + 2 }}"; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/callableTypes.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/callableTypes.smltest new file mode 100644 index 000000000..68976fc82 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/callableTypes.smltest @@ -0,0 +1,3 @@ +package tests.typeComputer.types.callableTypes + +fun myFun(f: (p1: Int, p2: String) -> (r1: Int, r2: String)) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/memberTypes.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/memberTypes.smltest new file mode 100644 index 000000000..764c55458 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/memberTypes.smltest @@ -0,0 +1,9 @@ +package tests.typeComputer.types.memberTypes + +enum MyEnum { + MyVariant1 + MyVariant2 +} + +fun nonNullableMemberTypes(a: MyEnum.MyVariant1, b: MyEnum.MyVariant2) +fun nullableMemberTypes(a: MyEnum.MyVariant1?, b: MyEnum.MyVariant2?) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/namedTypes.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/namedTypes.smltest new file mode 100644 index 000000000..cc749e651 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/namedTypes.smltest @@ -0,0 +1,4 @@ +package tests.typeComputer.types.namedTypes + +fun nonNullableNamedTypes(a: Int, b: String) +fun nullableNamedTypes(a: Int?, b: String?) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/parenthesizedTypes.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/parenthesizedTypes.smltest new file mode 100644 index 000000000..071a5dabf --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/parenthesizedTypes.smltest @@ -0,0 +1,3 @@ +package tests.typeComputer.types.parenthesizedTypes + +fun myFun(a: (Int), b: (String)) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/unionTypes.smltest b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/unionTypes.smltest new file mode 100644 index 000000000..dad2e1510 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/typeComputer/types/unionTypes.smltest @@ -0,0 +1,3 @@ +package tests.typeComputer.types.unionTypes + +fun myFun(a: union) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/stub file (only annotations).smlstub b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/stub file (only annotations).smlstub new file mode 100644 index 000000000..d9d87a870 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/stub file (only annotations).smlstub @@ -0,0 +1,3 @@ +// no_semantic_error "A file with declarations must declare its package." + +@AnnotationCall diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/stub file (only imports).smlstub b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/stub file (only imports).smlstub new file mode 100644 index 000000000..bb7804749 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/stub file (only imports).smlstub @@ -0,0 +1,3 @@ +// no_semantic_error "A file with declarations must declare its package." + +import myPackage diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/stub file (with declarations).smlstub b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/stub file (with declarations).smlstub new file mode 100644 index 000000000..8825b18b1 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/stub file (with declarations).smlstub @@ -0,0 +1,5 @@ +// semantic_error "A file with declarations must declare its package." +class »C« + +// no_semantic_error "A file with declarations must declare its package." +class »D« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/workflow file (only annotations).smlflow b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/workflow file (only annotations).smlflow new file mode 100644 index 000000000..d9d87a870 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/workflow file (only annotations).smlflow @@ -0,0 +1,3 @@ +// no_semantic_error "A file with declarations must declare its package." + +@AnnotationCall diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/workflow file (only imports).smlflow b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/workflow file (only imports).smlflow new file mode 100644 index 000000000..bb7804749 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/workflow file (only imports).smlflow @@ -0,0 +1,3 @@ +// no_semantic_error "A file with declarations must declare its package." + +import myPackage diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/workflow file (with declarations).smlflow b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/workflow file (with declarations).smlflow new file mode 100644 index 000000000..22f368690 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/must declare package/workflow file (with declarations).smlflow @@ -0,0 +1,5 @@ +// semantic_error "A file with declarations must declare its package." +step »s«() {} + +// no_semantic_error "A file with declarations must declare its package." +step »t«() {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/stubFileMustNotDeclareWorkflowsOrSteps.smlstub b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/stubFileMustNotDeclareWorkflowsOrSteps.smlstub new file mode 100644 index 000000000..3482f6ced --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/stubFileMustNotDeclareWorkflowsOrSteps.smlstub @@ -0,0 +1,13 @@ +package tests.validation.declarations.compilationUnits.stubFileMustNotDeclareWorkflowOrSteps + +// semantic_error "A stub file must not declare workflows or steps." +step »myStep«() {} +// semantic_error "A stub file must not declare workflows or steps." +workflow »myWorkflow« {} + +// no_semantic_error "A stub file must not declare workflows or steps." +annotation »MyAnnotation« +// no_semantic_error "A stub file must not declare workflows or steps." +class »MyClass« +// no_semantic_error "A stub file must not declare workflows or steps." +enum »MyEnum« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/uniqueNamesAcrossFiles.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/uniqueNamesAcrossFiles.smltest new file mode 100644 index 000000000..ba8becccd --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/uniqueNamesAcrossFiles.smltest @@ -0,0 +1,19 @@ +package simpleml.lang + +// semantic_error "A declaration with qualified name 'simpleml.lang.Any' exists already." +annotation »Any« + +// semantic_error "A declaration with qualified name 'simpleml.lang.Any' exists already." +class »Any« + +// semantic_error "A declaration with qualified name 'simpleml.lang.Any' exists already." +enum »Any« + +// semantic_error "A declaration with qualified name 'simpleml.lang.Any' exists already." +fun »Any«() + +// semantic_error "A declaration with qualified name 'simpleml.lang.Any' exists already." +step »Any«() {} + +// semantic_error "A declaration with qualified name 'simpleml.lang.Any' exists already." +workflow »Any« {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/uniqueNamesInStubFile.smlstub b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/uniqueNamesInStubFile.smlstub new file mode 100644 index 000000000..57e424c74 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/uniqueNamesInStubFile.smlstub @@ -0,0 +1,47 @@ +package tests.uniqueNamesInStubFile + +// semantic_error "A declaration with name 'MyImport' exists already in this file." +import »somePackage.MyImport« +// semantic_error "A declaration with name 'MyImport' exists already in this file." +import »somePackage.MyImport« + +// semantic_error "A declaration with name 'MyAliasedImport' exists already in this file." +import somePackage.MyImport as »MyAliasedImport« +// semantic_error "A declaration with name 'MyAliasedImport' exists already in this file." +import somePackage.MyImport as »MyAliasedImport« + +// semantic_error "A declaration with name 'Bla' exists already in this file." +import »somePackage.Bla« +// semantic_error "A declaration with name 'Bla' exists already in this file." +import somePackage.Blup as »Bla« + + +// semantic_error "A declaration with name 'MyAnnotation' exists already in this file." +annotation »MyAnnotation« +// semantic_error "A declaration with name 'MyAnnotation' exists already in this file." +annotation »MyAnnotation« + +// semantic_error "A declaration with name 'MyClass' exists already in this file." +class »MyClass« +// semantic_error "A declaration with name 'MyClass' exists already in this file." +class »MyClass« + +// semantic_error "A declaration with name 'MyEnum' exists already in this file." +enum »MyEnum« +// semantic_error "A declaration with name 'MyEnum' exists already in this file." +enum »MyEnum« + +// semantic_error "A declaration with name 'myFun' exists already in this file." +fun »myFun«() +// semantic_error "A declaration with name 'myFun' exists already in this file." +fun »myFun«() + + +// semantic_error "A declaration with name 'Bla' exists already in this file." +annotation »Bla« +// semantic_error "A declaration with name 'Bla' exists already in this file." +class »Bla« +// semantic_error "A declaration with name 'Bla' exists already in this file." +enum »Bla« +// semantic_error "A declaration with name 'Bla' exists already in this file." +fun »Bla«() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/uniqueNamesInWorkflowFile.smlflow b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/uniqueNamesInWorkflowFile.smlflow new file mode 100644 index 000000000..fa0967bfb --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/uniqueNamesInWorkflowFile.smlflow @@ -0,0 +1,33 @@ +package tests.uniqueNamesInWorkflowFile + +// semantic_error "A declaration with name 'MyImport' exists already in this file." +import »somePackage.MyImport« +// semantic_error "A declaration with name 'MyImport' exists already in this file." +import »somePackage.MyImport« + +// semantic_error "A declaration with name 'MyAliasedImport' exists already in this file." +import somePackage.MyImport as »MyAliasedImport« +// semantic_error "A declaration with name 'MyAliasedImport' exists already in this file." +import somePackage.MyImport as »MyAliasedImport« + +// semantic_error "A declaration with name 'Bla' exists already in this file." +import »somePackage.Bla« +// semantic_error "A declaration with name 'Bla' exists already in this file." +import somePackage.Blup as »Bla« + + +// semantic_error "A declaration with name 's' exists already in this file." +step »s«() {} +// semantic_error "A declaration with name 's' exists already in this file." +step »s«() {} + +// semantic_error "A declaration with name 'w' exists already in this file." +workflow »w« {} +// semantic_error "A declaration with name 'w' exists already in this file." +workflow »w« {} + + +// semantic_error "A declaration with name 'Bla' exists already in this file." +step »Bla«() {} +// semantic_error "A declaration with name 'Bla' exists already in this file." +workflow »Bla« {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/workflowFileMustOnlyDeclareWorkflowsAndSteps.smlflow b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/workflowFileMustOnlyDeclareWorkflowsAndSteps.smlflow new file mode 100644 index 000000000..1e5bf84fe --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/compilationUnits/workflowFileMustOnlyDeclareWorkflowsAndSteps.smlflow @@ -0,0 +1,13 @@ +package tests.workflowFileMustOnlyDeclareWorkflowsAndSteps + +// semantic_error "A workflow file must only declare workflows and steps." +annotation »MyAnnotation« +// semantic_error "A workflow file must only declare workflows and steps." +class »MyClass« +// semantic_error "A workflow file must only declare workflows and steps." +enum »MyEnum« + +// no_semantic_error "A workflow file must only declare workflows and steps." +step »myStep«() {} +// no_semantic_error "A workflow file must only declare workflows and steps." +workflow »myWorkflow« {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/annotations/parameter types.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/annotations/parameter types.smltest new file mode 100644 index 000000000..2d8fc1062 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/annotations/parameter types.smltest @@ -0,0 +1,52 @@ +package tests.validation.declarations.annotations.parameterTypes + +class MyClass + +enum ConstantEnum { + Variant1 + Variant2 +} + +enum NonConstantEnum { + Variant1(param: Int) + Variant2 +} + +annotation MyAnnotation( + // no_semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + booleanParam: »Boolean«, + // no_semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + nullableBooleanParam: »Boolean?«, + // no_semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + floatParam: »Float«, + // no_semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + nullableFloatParam: »Float?«, + // no_semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + intParam: »Int«, + // no_semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + nullableIntParam: »Int?«, + // no_semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + stringParam: »String«, + // no_semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + nullableStringParam: »String?«, + // no_semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + constantEnumParam: »ConstantEnum«, + // no_semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + nullableConstantEnumParam: »ConstantEnum?«, + // semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + nonConstantEnumParam: »NonConstantEnum«, + // semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + nullableNonConstantEnumParam: »NonConstantEnum?«, + // semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + classParam: »MyClass«, + // semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + nullableClassParam: »MyClass?«, + // semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + unresolvedParam: »Unresolved«, + // semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + nullableUnresolvedParam: »Unresolved?«, + // semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + callableParam: »() -> ()«, + // no_semantic_error "Parameters of annotations must have type Boolean, Float, Int, String, or a constant enum." + vararg variadicParameter: »Int«, +) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/annotations/uniqueNames.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/annotations/uniqueNames.smltest new file mode 100644 index 000000000..63d76cbbc --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/annotations/uniqueNames.smltest @@ -0,0 +1,10 @@ +package tests.uniqueNames + +annotation A( + // semantic_error "A parameter with name 'duplicateParameter' exists already in this annotation." + »duplicateParameter«: Int, + // semantic_error "A parameter with name 'duplicateParameter' exists already in this annotation." + »duplicateParameter«: Int, + // no_semantic_error "A parameter with name 'uniqueParameter' exists already in this annotation." + »uniqueParameter«: Int +) \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/annotations/unnecessaryParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/annotations/unnecessaryParameterList.smltest new file mode 100644 index 000000000..c61548141 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/annotations/unnecessaryParameterList.smltest @@ -0,0 +1,7 @@ +package tests.validation.declarations.annotations.unnecessaryParameterList + +// semantic_info "Unnecessary parameter list." +annotation MyAnnotation1»()« + +// no_semantic_info "Unnecessary parameter list." +annotation MyAnnotation2»(a: Int)« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/attributes/mustHaveType.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/attributes/mustHaveType.smltest new file mode 100644 index 000000000..a5fc9c8de --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/attributes/mustHaveType.smltest @@ -0,0 +1,8 @@ +package tests.mustHaveType + +class MyClass { + // semantic_error "An attribute must have a type." + attr »myAttribute1« + // no_semantic_error "An attribute must have a type." + attr »myAttribute2«: Int +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/acyclicSuperTypes.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/acyclicSuperTypes.smltest new file mode 100644 index 000000000..97ef2fb19 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/acyclicSuperTypes.smltest @@ -0,0 +1,12 @@ +package tests.validation.declarations.classes.acyclicSuperTypes + +// semantic_error "A class must not directly or indirectly be a subtype of itself." +class MyClass1 sub »MyClass3« +// semantic_error "A class must not directly or indirectly be a subtype of itself." +class MyClass2 sub »MyClass1« +// semantic_error "A class must not directly or indirectly be a subtype of itself." +class MyClass3 sub »MyClass2« + +class MyClass4 +// no_semantic_error "A class must not directly or indirectly be a subtype of itself." +class MyClass5 sub »MyClass4« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/inheritedMembersMustHaveUniqueNames.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/inheritedMembersMustHaveUniqueNames.smltest new file mode 100644 index 000000000..789f73ed8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/inheritedMembersMustHaveUniqueNames.smltest @@ -0,0 +1,42 @@ +package tests.validation.declarations.classes.mustInheritOnlyOneClass + +class MyClass1 { + attr attribute1: Int + attr attribute2: Int + + fun method1() + fun method2() +} +class MyClass2 { + attr attribute1: Int + attr method2: Int + + fun method1() + fun attribute2() +} +class MyClass3 sub MyClass1 + + +// semantic_error "Inherits multiple members called 'attribute1'." +class »MyClass4« sub MyClass1, MyClass2 + +// semantic_error "Inherits multiple members called 'attribute2'." +class »MyClass5« sub MyClass1, MyClass2 + +// semantic_error "Inherits multiple members called 'method1'." +class »MyClass6« sub MyClass1, MyClass2 + +// semantic_error "Inherits multiple members called 'method2'." +class »MyClass7« sub MyClass1, MyClass2 + +// no_semantic_error "Inherits multiple members called 'attribute1'." +class »MyClass8« sub MyClass1, MyClass3 + +// no_semantic_error "Inherits multiple members called 'method1'." +class »MyClass9« sub MyClass1, MyClass3 + +// no_semantic_error "Inherits multiple members called 'attribute1'." +class »MyClass10« sub MyClass1 + +// no_semantic_error "Inherits multiple members called 'method1'." +class »MyClass11« sub MyClass1 diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/mustInheritOnlyClasses.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/mustInheritOnlyClasses.smltest new file mode 100644 index 000000000..50e26a84b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/mustInheritOnlyClasses.smltest @@ -0,0 +1,12 @@ +package tests.validation.declarations.classes.mustInheritOnlyClasses + +class MyClass +enum MyEnum + + +// no_semantic_error "A class must only inherit classes." +// semantic_error "A class must only inherit classes." +// no_semantic_error "A class must only inherit classes." +class TestClass sub »MyClass«, + »MyEnum«, + »UnresolvedClass«, diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/only one protocol per class.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/only one protocol per class.smltest new file mode 100644 index 000000000..88ecf550c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/only one protocol per class.smltest @@ -0,0 +1,15 @@ +package tests.validation.declarations.classes.onlyOneProtocolPerClass + +class MyClass1 { + + // semantic_error "A class must have only one protocol." + »protocol {}« + // semantic_error "A class must have only one protocol." + »protocol {}« +} + +class MyClass2 { + + // no_semantic_error "A class must have only one protocol." + »protocol {}« +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/protocol reference must point to instance member.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/protocol reference must point to instance member.smltest new file mode 100644 index 000000000..1f2580982 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/protocol reference must point to instance member.smltest @@ -0,0 +1,20 @@ +package tests.validation.declarations.classes.protocolReferenceMustPointToInstanceMember + +class MyClass { + static attr staticAttribute: Int + attr instanceAttribute: Int + + static fun staticMethod() + fun instanceMethod() + + protocol { + // semantic_error "Must only reference instance members." + subterm staticAttributeReference = »staticAttribute«; + // semantic_error "Must only reference instance members." + subterm staticMethodReference = »staticMethod«; + // no_semantic_error "Must only reference instance members." + subterm instanceAttributeReference = »instanceAttribute«; + // no_semantic_error "Must only reference instance members." + subterm instanceMethodReference = »instanceMethod«; + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/unique names for protocol subterms.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/unique names for protocol subterms.smltest new file mode 100644 index 000000000..b131fc6f4 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/unique names for protocol subterms.smltest @@ -0,0 +1,13 @@ +package tests.validation.declarations.classes.uniqueNamesForProtocolSubterms + +class MyClass { + + protocol { + // semantic_error "A subterm with name 'duplicateName' exists already in this protocol." + subterm »duplicateName« = .; + // semantic_error "A subterm with name 'duplicateName' exists already in this protocol." + subterm »duplicateName« = .; + // no_semantic_error "A subterm with name 'uniqueName' exists already in this protocol." + subterm »uniqueName« = .; + } +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/uniqueNames.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/uniqueNames.smltest new file mode 100644 index 000000000..e75ef047d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/uniqueNames.smltest @@ -0,0 +1,41 @@ +package tests.uniqueNames + +class MyClass( + // semantic_error "A parameter with name 'duplicateParameter' exists already in this class." + »duplicateParameter«: Int, + // semantic_error "A parameter with name 'duplicateParameter' exists already in this class." + »duplicateParameter«: Int, + // no_semantic_error "A parameter with name 'uniqueParameter' exists already in this class." + »uniqueParameter«: Int, +) { + + // semantic_error "A declaration with name 'myAttribute' exists already in this class." + attr »myAttribute«: Int + // semantic_error "A declaration with name 'myAttribute' exists already in this class." + attr »myAttribute«: Int + + // semantic_error "A declaration with name 'MyClass' exists already in this class." + class »MyClass« + // semantic_error "A declaration with name 'MyClass' exists already in this class." + class »MyClass« + + // semantic_error "A declaration with name 'myFun' exists already in this class." + fun »myFun«() + // semantic_error "A declaration with name 'myFun' exists already in this class." + fun »myFun«() + + // semantic_error "A declaration with name 'MyEnum' exists already in this class." + enum »MyEnum« + // semantic_error "A declaration with name 'MyEnum' exists already in this class." + enum »MyEnum« + + + // semantic_error "A declaration with name 'Bla' exists already in this class." + attr »Bla«: Int + // semantic_error "A declaration with name 'Bla' exists already in this class." + class »Bla« + // semantic_error "A declaration with name 'Bla' exists already in this class." + fun »Bla«() + // semantic_error "A declaration with name 'Bla' exists already in this class." + enum »Bla« +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/uniqueParentTypes.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/uniqueParentTypes.smltest new file mode 100644 index 000000000..88833efae --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/uniqueParentTypes.smltest @@ -0,0 +1,16 @@ +package tests.uniqueParentTypes + +class MyClass1 +class MyClass2 + +class MyOpenClass1 +class MyOpenClass2 + +// semantic_error "Parent types must be unique." +// semantic_error "Parent types must be unique." +// no_semantic_error "Parent types must be unique." +// semantic_error "Parent types must be unique." +// semantic_error "Parent types must be unique." +// no_semantic_error "Parent types must be unique." +class MyClass4 sub »MyClass1«, »MyClass1«, »MyClass2«, + »MyOpenClass1«, »MyOpenClass1«, »MyOpenClass2« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/unnecessaryBody.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/unnecessaryBody.smltest new file mode 100644 index 000000000..42f194034 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/unnecessaryBody.smltest @@ -0,0 +1,15 @@ +package tests.validation.declarations.classes.unnecessaryBody + + +// semantic_info "Unnecessary class body." +class MyClass1 »{}« + +// no_semantic_info "Unnecessary class body." +class MyClass2 »{ + fun f() +}« + +// no_semantic_info "Unnecessary class body." +class MyClass3 »{ + protocol {} +}« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/unnecessaryTypeParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/unnecessaryTypeParameterList.smltest new file mode 100644 index 000000000..4255cd0e1 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/classes/unnecessaryTypeParameterList.smltest @@ -0,0 +1,7 @@ +package tests.validation.declarations.classes.unnecessaryTypeParameterList + +// semantic_info "Unnecessary type parameter list." +class MyClass1»<>« + +// no_semantic_info "Unnecessary type parameter list." +class MyClass2»« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/deprecation.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/deprecation.smltest new file mode 100644 index 000000000..c07832c05 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/deprecation.smltest @@ -0,0 +1,176 @@ +package tests.validation.declarations.deprecation + +@Deprecated +annotation DeprecatedAnnotation +annotation ValidAnnotation + +@Deprecated +class DeprecatedClass +class ValidClass { + + @Deprecated + attr deprecatedAttribute: Int + attr validAttribute: Int +} + +@Deprecated +enum DeprecatedEnum +enum ValidEnum { + @Deprecated DeprecatedEnumVariant + ValidEnumVariant +} + +@Deprecated +fun deprecatedFunction() +fun validFunction( + @Deprecated deprecatedParameter: Int = 1, + validParameter: Int = 1 +) -> ( + @Deprecated deprecatedResult: Int, + validResult: Int +) + +fun functionWithTypeParameters<@Deprecated DeprecatedTypeParameter, ValidTypeParameter>() where + + /* + * Showing an error for yields is pointless, since constraints must stay. + * Deprecation is only relevant for callers. + */ + + // no_semantic_warning "The referenced declaration is deprecated." + »DeprecatedTypeParameter« sub Int, + // no_semantic_warning "The referenced declaration is deprecated." + »ValidTypeParameter« sub Int, + +@Deprecated step deprecatedStep() {} +step validStep() {} + +// semantic_warning "The used annotation is deprecated." +@»DeprecatedAnnotation« +// no_semantic_warning "The used annotation is deprecated." +@»ValidAnnotation« +// no_semantic_warning "The used annotation is deprecated." +@»Unresolved« +step testStep1( + + // semantic_warning "The referenced declaration is deprecated." + @Deprecated deprecatedParameter: »DeprecatedClass« = 1, + // no_semantic_warning "The referenced declaration is deprecated." + validParameter: »ValidClass« = 1, + + // semantic_warning "The referenced declaration is deprecated." + param3: »DeprecatedEnum« = 1, + // no_semantic_warning "The referenced declaration is deprecated." + param4: »ValidEnum« = 1, + // no_semantic_warning "The referenced declaration is deprecated." + param5: »Unresolved« = 1 +) +-> ( + @Deprecated deprecatedResult: Int, + validResult: Int +) { + + // no_semantic_warning "The referenced declaration is deprecated." + »deprecatedParameter«; + // no_semantic_warning "The referenced declaration is deprecated." + »validParameter«; + + // semantic_warning "The referenced declaration is deprecated." + validParameter.»deprecatedAttribute«; + // no_semantic_warning "The referenced declaration is deprecated." + validParameter.»validAttribute«; + + // semantic_warning "The referenced declaration is deprecated." + ValidEnum.»DeprecatedEnumVariant«; + // no_semantic_warning "The referenced declaration is deprecated." + ValidEnum.»ValidEnumVariant«; + + // semantic_warning "The referenced declaration is deprecated." + »deprecatedFunction«(); + // no_semantic_warning "The referenced declaration is deprecated." + »validFunction«(); + + validFunction( + // semantic_warning "The corresponding parameter is deprecated." + »deprecatedParameter = 1«, + // no_semantic_warning "The corresponding parameter is deprecated." + »validParameter = 1«, + // no_semantic_warning "The corresponding parameter is deprecated." + »unresolved = 1«, + ); + + validFunction( + // semantic_warning "The corresponding parameter is deprecated." + »1«, + // no_semantic_warning "The corresponding parameter is deprecated." + »1«, + // no_semantic_warning "The corresponding parameter is deprecated." + »1«, + ); + + // semantic_warning "The referenced declaration is deprecated." + validFunction().»deprecatedResult«; + + // no_semantic_warning "The referenced declaration is deprecated." + validFunction().»validResult«; + + functionWithTypeParameters< + // semantic_warning "The corresponding type parameter is deprecated." + »DeprecatedTypeParameter = Int«, + // no_semantic_warning "The corresponding type parameter is deprecated." + »ValidTypeParameter = Int«, + // no_semantic_warning "The corresponding type parameter is deprecated." + »Unresolved = Int« + >(); + + functionWithTypeParameters< + // semantic_warning "The corresponding type parameter is deprecated." + »Int«, + // no_semantic_warning "The corresponding type parameter is deprecated." + »Int«, + // no_semantic_warning "The corresponding type parameter is deprecated." + »Int« + >(); + + // semantic_warning "The referenced declaration is deprecated." + »deprecatedStep«(); + // no_semantic_warning "The referenced declaration is deprecated." + »validStep«(); + + // no_semantic_warning "The referenced declaration is deprecated." + »unresolved«; + + /* + * Showing an error for yields is pointless, since we must yield something. + * Deprecation is only relevant for callers. + */ + + // no_semantic_warning "The referenced declaration is deprecated." + yield »deprecatedResult« = 1; + // no_semantic_warning "The referenced declaration is deprecated." + yield »validResult« = 1; +} + +step testStep2() -> (result1: Int, result2: Int, result3: Int) { + // semantic_warning "The assigned declaration is deprecated." + // no_semantic_warning "The assigned declaration is deprecated." + // no_semantic_warning "The assigned declaration is deprecated." + »val a«, »val b«, »val c« = validFunction(); + + // semantic_warning "The assigned declaration is deprecated." + // no_semantic_warning "The assigned declaration is deprecated." + // no_semantic_warning "The assigned declaration is deprecated." + »yield result1«, »yield result2«, »yield result3« = validFunction(); + + // no_semantic_warning "The assigned declaration is deprecated." + // no_semantic_warning "The assigned declaration is deprecated." + // no_semantic_warning "The assigned declaration is deprecated." + »_«, »_«, »_« = validFunction(); + + // no_semantic_warning "The assigned declaration is deprecated." + »val d« = a; + // no_semantic_warning "The assigned declaration is deprecated." + »val e« = b; + // no_semantic_warning "The assigned declaration is deprecated." + »val f« = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enumVariants/uniqueNames.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enumVariants/uniqueNames.smltest new file mode 100644 index 000000000..1de8ad7cd --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enumVariants/uniqueNames.smltest @@ -0,0 +1,12 @@ +package tests.validation.declarations.enumVariants.uniqueNames + +enum MyEnum { + MyEnumVariant( + // semantic_error "A parameter with name 'duplicateParameter' exists already in this enum variant." + »duplicateParameter«: Int, + // semantic_error "A parameter with name 'duplicateParameter' exists already in this enum variant." + »duplicateParameter«: Int, + // no_semantic_error "A parameter with name 'uniqueParameter' exists already in this enum variant." + »uniqueParameter«: Int + ) +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enumVariants/unnecessaryParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enumVariants/unnecessaryParameterList.smltest new file mode 100644 index 000000000..931bc68c7 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enumVariants/unnecessaryParameterList.smltest @@ -0,0 +1,10 @@ +package tests.validation.declarations.enumVariants.unnecessaryParameterList + +enum MyEnum { + // semantic_info "Unnecessary parameter list." + MyVariant1»()« + // semantic_info "Unnecessary parameter list." + MyVariant1»()« + // no_semantic_info "Unnecessary parameter list." + MyVariant2»(a: Int)« +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enumVariants/unnecessaryTypeParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enumVariants/unnecessaryTypeParameterList.smltest new file mode 100644 index 000000000..59085fdea --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enumVariants/unnecessaryTypeParameterList.smltest @@ -0,0 +1,8 @@ +package tests.validation.declarations.enumVariants.unnecessaryTypeParameterList + +enum MyEnum { + // semantic_info "Unnecessary type parameter list." + MyVariant1»<>« + // no_semantic_info "Unnecessary type parameter list." + MyVariant2»« +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enums/uniqueNames.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enums/uniqueNames.smltest new file mode 100644 index 000000000..9d3e07963 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enums/uniqueNames.smltest @@ -0,0 +1,10 @@ +package tests.uniqueNames + +enum MyEnum { + // semantic_error "A declaration with name 'INSTANCE1' exists already in this enum." + »INSTANCE1« + // semantic_error "A declaration with name 'INSTANCE1' exists already in this enum." + »INSTANCE1« + // no_semantic_error "A declaration with name 'INSTANCE2' exists already in this enum." + »INSTANCE2« +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enums/unnecessaryBody.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enums/unnecessaryBody.smltest new file mode 100644 index 000000000..717c38328 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/enums/unnecessaryBody.smltest @@ -0,0 +1,9 @@ +package tests.validation.declarations.enums.unnecessaryBody + +// semantic_info "Unnecessary enum body." +enum MyEnum1 »{}« + +// no_semantic_info "Unnecessary enum body." +enum MyEnum2 »{ + INSTANCE +}« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/nonStaticPropagates.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/nonStaticPropagates.smltest new file mode 100644 index 000000000..e7fd4d980 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/nonStaticPropagates.smltest @@ -0,0 +1,18 @@ +package tests.staticPropagates + +class MyOpenClass { + static fun staticFunction1() + fun nonStaticFunction1() +} + +class MyClass sub MyOpenClass { + // no_semantic_error "One of the supertypes of this class declares a non-static function with this name, so this must be non-static as well." + static fun »ownStaticFunction«() + // no_semantic_error "One of the supertypes of this class declares a non-static function with this name, so this must be non-static as well." + fun »ownNonStaticFunction«() + + // no_semantic_error "One of the supertypes of this class declares a non-static function with this name, so this must be non-static as well." + static fun »staticFunction1«() + // semantic_error "One of the supertypes of this class declares a non-static function with this name, so this must be non-static as well." + static fun »nonStaticFunction1«() +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/purePropagates.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/purePropagates.smltest new file mode 100644 index 000000000..cd2fdb22b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/purePropagates.smltest @@ -0,0 +1,21 @@ +package tests.validation.declarations.functions.purePropagates + +class MyOpenClass { + @Pure fun pureFunction1() + @Pure fun pureFunction2() + fun impureFunction1() +} + +class MyClass sub MyOpenClass { + // no_semantic_error "One of the supertypes of this class declares a pure function with this name, so this must be pure as well." + @Pure fun »ownPureFunction«() + // no_semantic_error "One of the supertypes of this class declares a pure function with this name, so this must be pure as well." + fun »ownImpureFunction«() + + // semantic_error "One of the supertypes of this class declares a pure function with this name, so this must be pure as well." + fun »pureFunction1«() + // no_semantic_error "One of the supertypes of this class declares a pure function with this name, so this must be pure as well." + @Pure fun »pureFunction2«() + // no_semantic_error "One of the supertypes of this class declares a pure function with this name, so this must be pure as well." + @Pure fun »impureFunction1«() +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/staticPropagates.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/staticPropagates.smltest new file mode 100644 index 000000000..549f0c7de --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/staticPropagates.smltest @@ -0,0 +1,21 @@ +package tests.validation.declarations.functions.staticPropagates + +class MyOpenClass { + static fun staticFunction1() + static fun staticFunction2() + fun nonStaticFunction1() +} + +class MyClass sub MyOpenClass { + // no_semantic_error "One of the supertypes of this class declares a static function with this name, so this must be static as well." + static fun »ownStaticFunction«() + // no_semantic_error "One of the supertypes of this class declares a static function with this name, so this must be static as well." + fun »ownNonStaticFunction«() + + // semantic_error "One of the supertypes of this class declares a static function with this name, so this must be static as well." + fun »staticFunction1«() + // no_semantic_error "One of the supertypes of this class declares a static function with this name, so this must be static as well." + static fun »staticFunction2«() + // no_semantic_error "One of the supertypes of this class declares a static function with this name, so this must be static as well." + static fun »nonStaticFunction1«() +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/uniqueNames.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/uniqueNames.smltest new file mode 100644 index 000000000..6043c7865 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/uniqueNames.smltest @@ -0,0 +1,21 @@ +package tests.uniqueNames + +fun f( + // semantic_error "A parameter or result with name 'duplicateParameter' exists already in this function." + »duplicateParameter«: Int, + // semantic_error "A parameter or result with name 'duplicateParameter' exists already in this function." + »duplicateParameter«: Int, + // no_semantic_error "A parameter or result with name 'uniqueParameter' exists already in this function." + »uniqueParameter«: Int, + // semantic_error "A parameter or result with name 'parameterAndResult' exists already in this function." + »parameterAndResult«: Int +) -> ( + // semantic_error "A parameter or result with name 'duplicateResult' exists already in this function." + »duplicateResult«: Int, + // semantic_error "A parameter or result with name 'duplicateResult' exists already in this function." + »duplicateResult«: Int, + // no_semantic_error "A parameter or result with name 'uniqueResult' exists already in this function." + »uniqueResult«: Int, + // semantic_error "A parameter or result with name 'parameterAndResult' exists already in this function." + »parameterAndResult«: Int +) \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/unnecessaryResultList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/unnecessaryResultList.smltest new file mode 100644 index 000000000..da681d41d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/unnecessaryResultList.smltest @@ -0,0 +1,7 @@ +package tests.validation.declarations.functions.unnecessaryResultList + +// semantic_info "Unnecessary result list." +fun myFun1() »-> ()« + +// no_semantic_info "Unnecessary result list." +fun myFun2() »-> (r: Int)« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/unnecessaryTypeParameterList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/unnecessaryTypeParameterList.smltest new file mode 100644 index 000000000..d403e7e68 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/functions/unnecessaryTypeParameterList.smltest @@ -0,0 +1,7 @@ +package tests.validation.declarations.functions.unnecessaryTypeParameterList + +// semantic_info "Unnecessary type parameter list." +fun myFun1»<>«() + +// no_semantic_info "Unnecessary type parameter list." +fun myFun2»«() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/imports/noWildcardImportWithAlias.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/imports/noWildcardImportWithAlias.smltest new file mode 100644 index 000000000..b97e9de89 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/imports/noWildcardImportWithAlias.smltest @@ -0,0 +1,6 @@ +package tests.languageTests.validation.declarations.imports.noWildcardImportWithAlias + +// semantic_error "A wildcard import must not have an alias." +import stuff.* »as Stuff« +// no_semantic_error "A wildcard import must not have an alias." +import stuff »as Stuff« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/imports/unresolvedNamespace.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/imports/unresolvedNamespace.smltest new file mode 100644 index 000000000..2de332ffe --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/imports/unresolvedNamespace.smltest @@ -0,0 +1,15 @@ +package tests.unresolvedNamespace + +// semantic_error "No declaration with qualified name 'simpleml.langs.Any' exists." +import »simpleml.langs.Any« +// no_semantic_error "No declaration with qualified name 'simpleml.lang.Any' exists." +import »simpleml.lang.Any« +// no_semantic_error "No declaration with qualified name 'tests.unresolvedNamespace.C' exists." +import »tests.unresolvedNamespace.C« + +// semantic_error "No package with qualified name 'simpleml.langs' exists." +import »simpleml.langs.*« +// no_semantic_error "No package with qualified name 'simpleml.model' exists." +import »simpleml.lang.*« + +class C diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/block lambda prefix.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/block lambda prefix.smltest new file mode 100644 index 000000000..c48a010bf --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/block lambda prefix.smltest @@ -0,0 +1,7 @@ +package tests.validation.declarations.blockLambdaPrefix + +// semantic_error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." +class »__block_lambda_0« + +// no_semantic_error "Names of declarations must not start with '__block_lambda_'. This is reserved for code generation of block lambdas." +class »_block_lambda_1« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/name convention.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/name convention.smltest new file mode 100644 index 000000000..ceca74c5d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/name convention.smltest @@ -0,0 +1,132 @@ + +package tests.validation.declarations.nameCasing + +// no_semantic_warning "Names of annotations should be UpperCamelCase." +annotation »AnnotationUppercase1« +// semantic_warning "Names of annotations should be UpperCamelCase." +annotation »annotationLowercase« +// semantic_warning "Names of annotations should be UpperCamelCase." +annotation »_annotationUnderscore« +// semantic_warning "Names of annotations should be UpperCamelCase." +annotation »Annotation_Snake_Case« + +// no_semantic_warning "Names of classes should be UpperCamelCase." +class »ClassUppercase1« +// semantic_warning "Names of classes should be UpperCamelCase." +class »classLowercase« +// semantic_warning "Names of classes should be UpperCamelCase." +class »_classUnderscore« +// semantic_warning "Names of classes should be UpperCamelCase." +class »Class_Snake_Case« { + // semantic_warning "Names of attributes should be lowerCamelCase." + attr »AttributeUppercase«: Int + // no_semantic_warning "Names of attributes should be lowerCamelCase." + attr »attributeLowercase1«: Int + // semantic_warning "Names of attributes should be lowerCamelCase." + attr »_attributeUnderscore«: Int + // semantic_warning "Names of attributes should be lowerCamelCase." + attr »attribute_snake_case«: Int + + protocol { + // semantic_warning "Names of protocol subterms should be lowerCamelCase." + subterm »SubtermUppercase« = .; + // no_semantic_warning "Names of protocol subterms should be lowerCamelCase." + subterm »subtermLowercase1« = .; + // semantic_warning "Names of protocol subterms should be lowerCamelCase." + subterm »_subtermUnderscore« = .; + // semantic_warning "Names of protocol subterms should be lowerCamelCase." + subterm »subterm_snake_case« = .; + } +} + +// no_semantic_warning "Names of enums should be UpperCamelCase." +enum »EnumUppercase1« +// semantic_warning "Names of enums should be UpperCamelCase." +enum »enumLowercase« +// semantic_warning "Names of enums should be UpperCamelCase." +enum »_enumUnderscore« +// semantic_warning "Names of enums should be UpperCamelCase." +enum »Enum_Snake_Case« { + // no_semantic_warning "Names of enum variants should be UpperCamelCase." + »EnumVariantUppercase1« + // semantic_warning "Names of enum variants should be UpperCamelCase." + »enumVariantLowercase« + // semantic_warning "Names of enum variants should be UpperCamelCase." + »_enumVariantUnderscore« + // semantic_warning "Names of enum variants should be UpperCamelCase." + »Enum_Variant_Snake_Case«< + // no_semantic_warning "Names of type parameters should be UpperCamelCase." + »TypeParameterUppercase«, + // semantic_warning "Names of type parameters should be UpperCamelCase." + »typeParameterLowercase1«, + // semantic_warning "Names of type parameters should be UpperCamelCase." + »_typeParameterUnderscore«, + // semantic_warning "Names of type parameters should be UpperCamelCase." + »typeParameter_snake_case« + > +} + +// semantic_warning "Names of functions should be lowerCamelCase." +fun »FunctionUppercase«() +// no_semantic_warning "Names of functions should be lowerCamelCase." +fun »functionLowercase1«() +// semantic_warning "Names of functions should be lowerCamelCase." +fun »_functionUnderscore«() +// semantic_warning "Names of functions should be lowerCamelCase." +fun »function_snake_case«( + // semantic_warning "Names of parameters should be lowerCamelCase." + »ParameterUppercase«: Int, + // no_semantic_warning "Names of parameters should be lowerCamelCase." + »parameterLowercase1«: Int, + // semantic_warning "Names of parameters should be lowerCamelCase." + »_parameterUnderscore«: Int, + // semantic_warning "Names of parameters should be lowerCamelCase." + »parameter_snake_case«: Int +) -> ( + // semantic_warning "Names of results should be lowerCamelCase." + »ResultUppercase«: Int, + // no_semantic_warning "Names of results should be lowerCamelCase." + »resultLowercase1«: Int, + // semantic_warning "Names of results should be lowerCamelCase." + »_resultUnderscore«: Int, + // semantic_warning "Names of results should be lowerCamelCase." + »result_snake_case«: Int +) + +// semantic_warning "Names of steps should be lowerCamelCase." +step »StepUppercase«() {} +// no_semantic_warning "Names of steps should be lowerCamelCase." +step »stepLowercase1«() {} +// semantic_warning "Names of steps should be lowerCamelCase." +step »_stepUnderscore«() {} +// semantic_warning "Names of steps should be lowerCamelCase." +step »step_snake_case«() {} + +// semantic_warning "Names of workflows should be lowerCamelCase." +workflow »WorkflowUppercase« {} +// no_semantic_warning "Names of workflows should be lowerCamelCase." +workflow »workflowLowercase1« {} +// semantic_warning "Names of workflows should be lowerCamelCase." +workflow »_workflowUnderscore« {} +// semantic_warning "Names of workflows should be lowerCamelCase." +workflow »workflow_snake_case« { + () { + // semantic_warning "Names of lambda results should be lowerCamelCase." + yield »LambdaResultUppercase« = 1; + // no_semantic_warning "Names of lambda results should be lowerCamelCase." + yield »lambdaResultLowercase1« = 1; + // semantic_warning "Names of lambda results should be lowerCamelCase." + yield »_lambdaResultUnderscore« = 1; + // semantic_warning "Names of lambda results should be lowerCamelCase." + yield »lambdaResult_snake_case« = 1; + }; + + // semantic_warning "Names of placeholders should be lowerCamelCase." + val »PlaceholderUppercase« = 1; + // no_semantic_warning "Names of placeholders should be lowerCamelCase." + val »placeholderLowercase1« = 1; + // semantic_warning "Names of placeholders should be lowerCamelCase." + val »_placeholderUnderscore« = 1; + // semantic_warning "Names of placeholders should be lowerCamelCase." + val »placeholder_snake_case« = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/package lowercase.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/package lowercase.smltest new file mode 100644 index 000000000..5d3822d63 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/package lowercase.smltest @@ -0,0 +1,2 @@ +// no_semantic_warning "All segments of the qualified name of a package should be lowerCamelCase." +package »tests.validation.declarations.lowercase1« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/package snake case.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/package snake case.smltest new file mode 100644 index 000000000..4dba6179e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/package snake case.smltest @@ -0,0 +1,2 @@ +// semantic_warning "All segments of the qualified name of a package should be lowerCamelCase." +package »tests.validation.declarations.snake_case« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/package underscore.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/package underscore.smltest new file mode 100644 index 000000000..5ed2b98df --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/package underscore.smltest @@ -0,0 +1,2 @@ +// semantic_warning "All segments of the qualified name of a package should be lowerCamelCase." +package »tests.validation.declarations._underscore« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/package uppercase.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/package uppercase.smltest new file mode 100644 index 000000000..3bc9f949a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/nameConvention/package uppercase.smltest @@ -0,0 +1,2 @@ +// semantic_warning "All segments of the qualified name of a package should be lowerCamelCase." +package »tests.validation.declarations.Uppercase« diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameterLists/no optional and variadic parameters.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameterLists/no optional and variadic parameters.smltest new file mode 100644 index 000000000..f97c7be03 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameterLists/no optional and variadic parameters.smltest @@ -0,0 +1,40 @@ +package tests.validation.declarations.parameterLists.noOptionalAndVariadicParameters + +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +// semantic_error "A callable with optional parameters must not have a variadic parameter." +annotation MyAnnotation1(»a«: Int, »b«: Int = 1, »c«: Int, »d«: Int = 2, vararg »e«: Int) + +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +annotation MyAnnotation2(»a«: Int) + + +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +// semantic_error "A callable with optional parameters must not have a variadic parameter." +class MyClass1(»a«: Int, »b«: Int = 1, »c«: Int, »d«: Int = 2, vararg »e«: Int) + +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +class MyClass2(»a«: Int) + + +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +// semantic_error "A callable with optional parameters must not have a variadic parameter." +fun myFunction1(»a«: Int, »b«: Int = 1, »c«: Int, »d«: Int = 2, vararg »e«: Int) + +// no_semantic_error "A callable with optional parameters must not have a variadic parameter." +fun myFunction2(»a«: Int) + + +workflow myWorkflow { + + // no_semantic_error "A callable with optional parameters must not have a variadic parameter." + (»a«) {}; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameterLists/noRequiredParametersAfterFirstOptionalParameter.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameterLists/noRequiredParametersAfterFirstOptionalParameter.smltest new file mode 100644 index 000000000..a24d6edbe --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameterLists/noRequiredParametersAfterFirstOptionalParameter.smltest @@ -0,0 +1,40 @@ +package tests.validation.declarations.parameterLists.noRequiredParameterAfterFirstOptionalParameter + +// no_semantic_error "After the first optional parameter all parameters must be optional." +// no_semantic_error "After the first optional parameter all parameters must be optional." +// semantic_error "After the first optional parameter all parameters must be optional." +// no_semantic_error "After the first optional parameter all parameters must be optional." +// no_semantic_error "After the first optional parameter all parameters must be optional." +annotation MyAnnotation1(»a«: Int, »b«: Int = 1, »c«: Int, »d«: Int = 2, vararg »e«: Int) + +// no_semantic_error "After the first optional parameter all parameters must be optional." +annotation MyAnnotation2(»a«: Int) + + +// no_semantic_error "After the first optional parameter all parameters must be optional." +// no_semantic_error "After the first optional parameter all parameters must be optional." +// semantic_error "After the first optional parameter all parameters must be optional." +// no_semantic_error "After the first optional parameter all parameters must be optional." +// no_semantic_error "After the first optional parameter all parameters must be optional." +class MyClass1(»a«: Int, »b«: Int = 1, »c«: Int, »d«: Int = 2, vararg »e«: Int) + +// no_semantic_error "After the first optional parameter all parameters must be optional." +class MyClass2(»a«: Int) + + +// no_semantic_error "After the first optional parameter all parameters must be optional." +// no_semantic_error "After the first optional parameter all parameters must be optional." +// semantic_error "After the first optional parameter all parameters must be optional." +// no_semantic_error "After the first optional parameter all parameters must be optional." +// no_semantic_error "After the first optional parameter all parameters must be optional." +fun myFunction1(»a«: Int, »b«: Int = 1, »c«: Int, »d«: Int = 2, vararg »e«: Int) + +// no_semantic_error "After the first optional parameter all parameters must be optional." +fun myFunction2(»a«: Int) + + +workflow myWorkflow { + + // no_semantic_error "After the first optional parameter all parameters must be optional." + (»a«) {}; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameterLists/vararg must be last parameter.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameterLists/vararg must be last parameter.smltest new file mode 100644 index 000000000..559abecad --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameterLists/vararg must be last parameter.smltest @@ -0,0 +1,12 @@ +package tests.validation.declarations.parameterLists.varargMustBeLastParameter + +// no_semantic_error "After a variadic parameter no more parameters must be specified." +// no_semantic_error "After a variadic parameter no more parameters must be specified." +// no_semantic_error "After a variadic parameter no more parameters must be specified." +fun validFunction(»a«: Int, »b«: Int = 1, vararg »c«: Int) + +// no_semantic_error "After a variadic parameter no more parameters must be specified." +// semantic_error "After a variadic parameter no more parameters must be specified." +// semantic_error "After a variadic parameter no more parameters must be specified." +// semantic_error "After a variadic parameter no more parameters must be specified." +fun invalidFunction(vararg »a«: Int, »b«: Int, »c«: Int = 1, vararg »d«: Int) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/default value must be constant.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/default value must be constant.smltest new file mode 100644 index 000000000..af43b7169 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/default value must be constant.smltest @@ -0,0 +1,12 @@ +package tests.validation.parameters.defaultValueMustBeConstant + +fun f() -> value: Int + +fun myFunction( + // no_semantic_error "Default values of parameters must be constant." + param1: Int = »1«, + // no_semantic_error "Default values of parameters must be constant." + param2: Int = »1 + 2«, + // semantic_error "Default values of parameters must be constant." + param3: Int = »f()« +) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/expert must be optional.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/expert must be optional.smltest new file mode 100644 index 000000000..6f990aa50 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/expert must be optional.smltest @@ -0,0 +1,32 @@ +package tests.validation.declarations.parameters.expertMustBeOptional + +// semantic_error "An expert parameter must be optional." +// no_semantic_error "An expert parameter must be optional." +annotation MyAnnotation(»@Expert« a: Int, »@Expert« b: Int = 3) + +// semantic_error "An expert parameter must be optional." +// no_semantic_error "An expert parameter must be optional." +class MyClass(»@Expert« a: Int, »@Expert« b: Int = 3) { + + // semantic_error "An expert parameter must be optional." + // no_semantic_error "An expert parameter must be optional." + class MyClass(»@Expert« a: Int, »@Expert« b: Int = 3) + + // semantic_error "An expert parameter must be optional." + // no_semantic_error "An expert parameter must be optional." + fun myFunction(»@Expert« a: Int, »@Expert« b: Int = 3) +} + +// semantic_error "An expert parameter must be optional." +// no_semantic_error "An expert parameter must be optional." +fun myFunction(»@Expert« a: Int, »@Expert« b: Int = 3) + +// semantic_error "An expert parameter must be optional." +// no_semantic_error "An expert parameter must be optional." +step myStep1(»@Expert« a: Int, »@Expert« b: Int = 3) {} + +// semantic_error "An expert parameter must be optional." +// no_semantic_error "An expert parameter must be optional." +step myStep2( + f: (»@Expert« a: Int, »@Expert« b: Int = 3) -> () +) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/mustHaveType.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/mustHaveType.smltest new file mode 100644 index 000000000..2d37e6a46 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/mustHaveType.smltest @@ -0,0 +1,36 @@ +package tests.validation.declarations.parameters.mustHaveType + +// semantic_error "A parameter must have a type." +// no_semantic_error "A parameter must have a type." +annotation MyAnnotation(»a«, »b«: Int) + +// semantic_error "A parameter must have a type." +// no_semantic_error "A parameter must have a type." +class MyClass(»a«, »b«: Int) { + // semantic_error "A parameter must have a type." + // no_semantic_error "A parameter must have a type." + class MyClass(»a«, »b«: Int) {} + + // semantic_error "A parameter must have a type." + // no_semantic_error "A parameter must have a type." + fun myFunction(»a«, »b«: Int) +} + +// semantic_error "A parameter must have a type." +// no_semantic_error "A parameter must have a type." +fun myFunction(»a«, »b«: Int) + +workflow myWorkflow { + // no_semantic_error "A parameter must have a type." + (»a«) {}; +} + +// semantic_error "A parameter must have a type." +// no_semantic_error "A parameter must have a type." +step myStep1(»a«, »b«: Int) {} + +// semantic_error "A parameter must have a type." +// no_semantic_error "A parameter must have a type." +step myStep2( + f: (»a«, »b«: Int) -> () +) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/mustNotDeprecateRequiredParameter.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/mustNotDeprecateRequiredParameter.smltest new file mode 100644 index 000000000..20439564c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/mustNotDeprecateRequiredParameter.smltest @@ -0,0 +1,32 @@ +package tests.validation.declarations.parameters.mustNotDeprecateRequiredParameter + +// semantic_error "A required parameter cannot be deprecated." +// no_semantic_error "A required parameter cannot be deprecated." +annotation MyAnnotation(»@Deprecated("")« a: Int, »@Deprecated("")« b: Int = 3) + +// semantic_error "A required parameter cannot be deprecated." +// no_semantic_error "A required parameter cannot be deprecated." +class MyClass(»@Deprecated("")« a: Int, »@Deprecated("")« b: Int = 3) { + + // semantic_error "A required parameter cannot be deprecated." + // no_semantic_error "A required parameter cannot be deprecated." + class MyClass(»@Deprecated("")« a: Int, »@Deprecated("")« b: Int = 3) + + // semantic_error "A required parameter cannot be deprecated." + // no_semantic_error "A required parameter cannot be deprecated." + fun myFunction(»@Deprecated("")« a: Int, »@Deprecated("")« b: Int = 3) +} + +// semantic_error "A required parameter cannot be deprecated." +// no_semantic_error "A required parameter cannot be deprecated." +fun myFunction(»@Deprecated("")« a: Int, »@Deprecated("")« b: Int = 3) + +// semantic_error "A required parameter cannot be deprecated." +// no_semantic_error "A required parameter cannot be deprecated." +step myStep1(»@Deprecated("")« a: Int, »@Deprecated("")« b: Int = 3) {} + +// semantic_error "A required parameter cannot be deprecated." +// no_semantic_error "A required parameter cannot be deprecated." +step myStep2( + f: (»@Deprecated("")« a: Int, »@Deprecated("")« b: Int = 3) -> () +) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/variadic parameters must not have default value.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/variadic parameters must not have default value.smltest new file mode 100644 index 000000000..44caf9142 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/parameters/variadic parameters must not have default value.smltest @@ -0,0 +1,5 @@ +package tests.validation.declarations.parameters.variadicParametersMustNotHaveDefaultValue + +// no_semantic_error "Variadic parameters must not have default values." +// semantic_error "Variadic parameters must not have default values." +fun f(»a«: Int = 1, vararg »b«: Int = 2) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/placeholders/renamingOfDeclaration.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/placeholders/renamingOfDeclaration.smltest new file mode 100644 index 000000000..5d1eaa100 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/placeholders/renamingOfDeclaration.smltest @@ -0,0 +1,38 @@ +package tests.validation.declarations.placeholders.renamingOfDeclaration + +annotation MyAnnotation + +class MyClass { + attr myAttribute: Int +} + +enum MyEnum { + MY_INSTANCE +} + +fun myFunction() + +workflow myWorkflow {} + +step test(myParameter: Int) { + // no_semantic_warning "This placeholder only provides another name for a declaration." + val »a« = 1; + // no_semantic_warning "This placeholder only provides another name for a declaration." + val »b« = MyAnnotation; + // semantic_warning "This placeholder only provides another name for a declaration." + val »c« = MyClass; + // no_semantic_warning "This placeholder only provides another name for a declaration." + val »d« = MyClass.myAttribute; + // semantic_warning "This placeholder only provides another name for a declaration." + val »e« = MyEnum; + // no_semantic_warning "This placeholder only provides another name for a declaration." + val »f« = MyEnum.MY_INSTANCE; + // semantic_warning "This placeholder only provides another name for a declaration." + val »g« = myFunction; + // no_semantic_warning "This placeholder only provides another name for a declaration." + val »h« = myWorkflow; + // semantic_warning "This placeholder only provides another name for a declaration." + val »i« = myParameter; + // semantic_warning "This placeholder only provides another name for a declaration." + val »j« = a; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/placeholders/unused.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/placeholders/unused.smltest new file mode 100644 index 000000000..c82dd251a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/placeholders/unused.smltest @@ -0,0 +1,45 @@ +package tests.validation.declarations.placeholders.unused + +step myFunction() { + call(unused); + + // semantic_warning "This placeholder is unused." + val »unused« = 1; + + // no_semantic_warning "This placeholder is unused." + val »used« = 1; + call(used); + + // no_semantic_warning "This placeholder is unused." + val »last« = 1; +} + +workflow myWorkflow1 { + call(unused); + + // semantic_warning "This placeholder is unused." + val »unused« = 1; + + // no_semantic_warning "This placeholder is unused." + val »used« = 1; + call(used); + + // no_semantic_warning "This placeholder is unused." + val »last« = 1; +} + +workflow myWorkflow2 { + () { + call(unused); + + // semantic_warning "This placeholder is unused." + val »unused« = 1; + + // no_semantic_warning "This placeholder is unused." + val »used« = 1; + call(used); + + // no_semantic_warning "This placeholder is unused." + val »last« = 1; + }; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/results/mustHaveType.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/results/mustHaveType.smltest new file mode 100644 index 000000000..e181db236 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/results/mustHaveType.smltest @@ -0,0 +1,18 @@ +package tests.mustHaveType + +class MyClass { + + // semantic_error "A result must have a type." + // no_semantic_error "A result must have a type." + fun myFunction() -> (»a«, »b«: Int) +} + +// semantic_error "A result must have a type." +// no_semantic_error "A result must have a type." +fun myFunction() -> (»a«, »b«: Int) + +// semantic_error "A result must have a type." +// no_semantic_error "A result must have a type." +step myStep( + f: () -> (»a«, »b«: Int) +) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/duplicateYield.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/duplicateYield.smltest new file mode 100644 index 000000000..f642ab696 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/duplicateYield.smltest @@ -0,0 +1,14 @@ +package tests.validation.declarations.steps.duplicateYield + +step testStep() -> (a: Int, b: Int) { + // no_semantic_error "This result is assigned multiple times." + yield »a« = 1; + // semantic_error "This result is assigned multiple times." + yield »b« = 1; + // semantic_error "This result is assigned multiple times." + yield »b« = 1; + // no_semantic_error "This result is assigned multiple times." + yield »c« = 1; + // no_semantic_error "This result is assigned multiple times." + yield »c« = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/unassignedResult.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/unassignedResult.smltest new file mode 100644 index 000000000..47857ddca --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/unassignedResult.smltest @@ -0,0 +1,12 @@ +package tests.validation.declarations.steps.unassignedResult + +// no_semantic_error "No value is assigned to this result." +// no_semantic_error "No value is assigned to this result." +// no_semantic_error "No value is assigned to this result." +// semantic_error "No value is assigned to this result." +step testStep() -> (»a«: Int, »b«: Int, »c«: Int, »d«: Int) { + yield b = 1; + yield a = 1; + yield c = 1; + yield c = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/uniqueNames.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/uniqueNames.smltest new file mode 100644 index 000000000..190876068 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/uniqueNames.smltest @@ -0,0 +1,36 @@ +package tests.uniqueNames + +step s( + // semantic_error "A parameter, result or placeholder with name 'duplicateParameter' exists already in this step." + »duplicateParameter«: Int, + // semantic_error "A parameter, result or placeholder with name 'duplicateParameter' exists already in this step." + »duplicateParameter«: Int, + // no_semantic_error "A parameter, result or placeholder with name 'uniqueParameter' exists already in this step." + »uniqueParameter«: Int, + // semantic_error "A parameter, result or placeholder with name 'parameterAndResult' exists already in this step." + »parameterAndResult«: Int, + // semantic_error "A parameter, result or placeholder with name 'parameterAndPlaceholder' exists already in this step." + »parameterAndPlaceholder«: Int +) -> ( + // semantic_error "A parameter, result or placeholder with name 'duplicateResult' exists already in this step." + »duplicateResult«: Int, + // semantic_error "A parameter, result or placeholder with name 'duplicateResult' exists already in this step." + »duplicateResult«: Int, + // no_semantic_error "A parameter, result or placeholder with name 'uniqueResult' exists already in this step." + »uniqueResult«: Int, + // semantic_error "A parameter, result or placeholder with name 'parameterAndResult' exists already in this step." + »parameterAndResult«: Int, + // semantic_error "A parameter, result or placeholder with name 'resultAndPlaceholder' exists already in this step." + »resultAndPlaceholder«: Int +) { + // semantic_error "A parameter, result or placeholder with name 'duplicatePlaceholder' exists already in this step." + val »duplicatePlaceholder« = 1; + // semantic_error "A parameter, result or placeholder with name 'duplicatePlaceholder' exists already in this step." + val »duplicatePlaceholder« = 1; + // no_semantic_error "A parameter, result or placeholder with name 'uniquePlaceholder' exists already in this step." + val »uniquePlaceholder« = 1; + // semantic_error "A parameter, result or placeholder with name 'parameterAndPlaceholder' exists already in this step." + val »parameterAndPlaceholder« = 1; + // semantic_error "A parameter, result or placeholder with name 'resultAndPlaceholder' exists already in this step." + val »resultAndPlaceholder« = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/unnecessaryResultList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/unnecessaryResultList.smltest new file mode 100644 index 000000000..3c7e0b582 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/unnecessaryResultList.smltest @@ -0,0 +1,6 @@ +package tests.validation.declarations.steps.unnecessaryResultList + +// semantic_info "Unnecessary result list." +step myStep1() »-> ()« {} +// no_semantic_info "Unnecessary result list." +step myStep2() »-> (r: Int)« {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/unusedParameter.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/unusedParameter.smltest new file mode 100644 index 000000000..5719607a5 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/steps/unusedParameter.smltest @@ -0,0 +1,8 @@ +step myStep1( + // semantic_warning "This parameter is unused." + »unused«: Int, + // no_semantic_warning "This parameter is unused." + »used«: Int +) { + call(used); +} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/workflows/noYield.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/workflows/noYield.smltest new file mode 100644 index 000000000..db3e2fe2b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/workflows/noYield.smltest @@ -0,0 +1,16 @@ +package tests.validation.declarations.workflows.noYield + +step f() { + // no_semantic_error "Yield must not be used in a workflow." + »yield a« = 1; +} + +workflow w { + // semantic_error "Yield must not be used in a workflow." + »yield a« = 1; + + () { + // no_semantic_error "Yield must not be used in a workflow." + »yield a« = 1; + }; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/workflows/uniqueNames.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/workflows/uniqueNames.smltest new file mode 100644 index 000000000..6d979a9f5 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/declarations/workflows/uniqueNames.smltest @@ -0,0 +1,8 @@ +package tests.validation.declarations.workflows.uniqueNames + +workflow w { + // semantic_error "A declaration with name 'myPlaceholder' exists already in this workflow." + val »myPlaceholder« = 1; + // semantic_error "A declaration with name 'myPlaceholder' exists already in this workflow." + val »myPlaceholder« = 1; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/arguments/must be constant.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/arguments/must be constant.smltest new file mode 100644 index 000000000..ffe342778 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/arguments/must be constant.smltest @@ -0,0 +1,33 @@ +package tests.validation.expressions.arguments.argumentsMustBeConstant + +fun myFunction() -> res: Int + +fun myFunctionWithConstantParameter( + @Constant constantParam: Int +) + +fun myFunctionWithNormalParameter( + param: Int +) + + +workflow testWorkflow { + // no_semantic_error "Arguments assigned to constant parameters must be constant." + myFunctionWithConstantParameter(»1«); + // no_semantic_error "Arguments assigned to constant parameters must be constant." + myFunctionWithConstantParameter(»1 + 2«); + // semantic_error "Arguments assigned to constant parameters must be constant." + myFunctionWithConstantParameter(»myFunction()«); + // semantic_error "Arguments assigned to constant parameters must be constant." + myFunctionWithConstantParameter(constantParam = »myFunction()«); + + // no_semantic_error "Arguments assigned to constant parameters must be constant." + myFunctionWithNormalParameter(»myFunction()«); + // no_semantic_error "Arguments assigned to constant parameters must be constant." + myFunctionWithNormalParameter(param = »myFunction()«); + + // no_semantic_error "Arguments assigned to constant parameters must be constant." + unresolved(»myFunction()«); + // no_semantic_error "Arguments assigned to constant parameters must be constant." + myFunctionWithConstantParameter(unresolved = »myFunction()«); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/arguments/variadic parameter assigned by name.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/arguments/variadic parameter assigned by name.smltest new file mode 100644 index 000000000..5f105b37c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/arguments/variadic parameter assigned by name.smltest @@ -0,0 +1,10 @@ +package tests.validation.arguments.variadicParameterAssignedByName + +fun f(a: Int, b: Int = 0, vararg c: Int) + +workflow test { + // no_semantic_error "A variadic parameter must not be assigned by name." + // no_semantic_error "A variadic parameter must not be assigned by name." + // semantic_error "A variadic parameter must not be assigned by name." + f(»a« = 1, »b« = 2, »c« = 3); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/calledClassMustHaveConstructor.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/calledClassMustHaveConstructor.smltest new file mode 100644 index 000000000..e1996ee14 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/calledClassMustHaveConstructor.smltest @@ -0,0 +1,14 @@ +package tests.validation.expressions.calls.calledClassMustHaveConstructor + +class A + +class B() + +workflow test { + + // semantic_error "Cannot create an instance of a class that has no constructor." + val a = »A«(); + + // no_semantic_error "Cannot create an instance of a class that has no constructor." + val b = »B«(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/context.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/context.smltest new file mode 100644 index 000000000..45f805388 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/context.smltest @@ -0,0 +1,116 @@ +package tests.validation.expressions.calls.context + +class MyClass() { + fun noResults() + fun oneResult() -> first: Int + fun twoResults() -> (first: Int, second: Int) +} +class MyClassWithoutConstructor + +enum MyEnum { + MyVariant() + MyVariantWithoutConstructor +} + +fun functionNoResults() +fun functionOneResult() -> first: Int +fun functionTwoResults() -> (first: Int, second: Int) + +step stepNoResults() {} +step stepOneResult() -> first: Int {} +step stepTwoResults() -> (first: Int, second: Int) {} + +step test( + paramNoResults: () -> (), + paramOneResult: () -> first: Int, + paramTwoResults: () -> (first: Int, second: Int) +) { + val lambdaNoResults = () {}; + val lambdaOneResult = () { + yield first = 1; + }; + val lambdaTwoResults = () { + yield first = 1; + yield second = 2; + }; + + // no_semantic_error "A call that produces no results is not allowed in this context." + val a = »MyClass«(); + // no_semantic_error "A call that produces multiple results is not allowed in this context." + val b = »MyClass«(); + + // no_semantic_error "A call that produces no results is not allowed in this context." + val c = »MyClassWithoutConstructor«(); + // no_semantic_error "A call that produces multiple results is not allowed in this context." + val d = »MyClassWithoutConstructor«(); + + // no_semantic_error "A call that produces no results is not allowed in this context." + val e = »MyEnum.MyVariant«(); + // no_semantic_error "A call that produces multiple results is not allowed in this context." + val f = »MyEnum.MyVariant«(); + + // no_semantic_error "A call that produces no results is not allowed in this context." + val g = »MyEnum.MyVariantWithoutConstructor«(); + // no_semantic_error "A call that produces multiple results is not allowed in this context." + val h = »MyEnum.MyVariantWithoutConstructor«(); + + // semantic_error "A call that produces no results is not allowed in this context." + MyClass().»noResults«() + 1; + // no_semantic_error "A call that produces no results is not allowed in this context." + MyClass().»oneResult«() - 1; + // no_semantic_error "A call that produces multiple results is not allowed in this context." + MyClass().»oneResult«() * 1; + // semantic_error "A call that produces multiple results is not allowed in this context." + MyClass().»twoResults«() / 1; + + // semantic_error "A call that produces no results is not allowed in this context." + »functionNoResults«() + 1; + // no_semantic_error "A call that produces no results is not allowed in this context." + »functionOneResult«() - 1; + // no_semantic_error "A call that produces multiple results is not allowed in this context." + »functionOneResult«() * 1; + // semantic_error "A call that produces multiple results is not allowed in this context." + »functionTwoResults«() / 1; + + // semantic_error "A call that produces no results is not allowed in this context." + »stepNoResults«() + 1; + // no_semantic_error "A call that produces no results is not allowed in this context." + »stepOneResult«() - 1; + // no_semantic_error "A call that produces multiple results is not allowed in this context." + »stepOneResult«() * 1; + // semantic_error "A call that produces multiple results is not allowed in this context." + »stepTwoResults«() / 1; + + // semantic_error "A call that produces no results is not allowed in this context." + »paramNoResults«() + 1; + // no_semantic_error "A call that produces no results is not allowed in this context." + »paramOneResult«() - 1; + // no_semantic_error "A call that produces multiple results is not allowed in this context." + »paramOneResult«() * 1; + // semantic_error "A call that produces multiple results is not allowed in this context." + »paramTwoResults«() / 1; + + // semantic_error "A call that produces no results is not allowed in this context." + »lambdaNoResults«() + 1; + // no_semantic_error "A call that produces no results is not allowed in this context." + »lambdaOneResult«() - 1; + // no_semantic_error "A call that produces multiple results is not allowed in this context." + »lambdaOneResult«() * 1; + // semantic_error "A call that produces multiple results is not allowed in this context." + »lambdaTwoResults«() / 1; + + // no_semantic_error "A call that produces no results is not allowed in this context." + »functionNoResults«(); + // no_semantic_error "A call that produces multiple results is not allowed in this context." + »functionTwoResults«(); + + // semantic_error "A call that produces no results is not allowed in this context." + »functionNoResults«().first; + // no_semantic_error "A call that produces multiple results is not allowed in this context." + »functionTwoResults«().first; + + // semantic_error "A call that produces no results is not allowed in this context." + val x = »functionNoResults«(); + // no_semantic_error "A call that produces multiple results is not allowed in this context." + val y, val z = »functionTwoResults«(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/missingTypeArgumentList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/missingTypeArgumentList.smltest new file mode 100644 index 000000000..b1245c7f7 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/missingTypeArgumentList.smltest @@ -0,0 +1,19 @@ +package tests.validation.expressions.calls.missingTypeArgumentList + +fun functionWithoutTypeParameters() +fun functionWithTypeParameters() + +workflow myWorkflow { + // no_semantic_error "Missing type argument list." + »functionWithoutTypeParameters«(); + // no_semantic_error "Missing type argument list." + »functionWithoutTypeParameters«(); + // semantic_error "Missing type argument list." + »functionWithTypeParameters«(); + // semantic_error "Missing type argument list." + »functionWithTypeParameters«(); + // no_semantic_error "Missing type argument list." + »unresolvedFunction«(); + // no_semantic_error "Missing type argument list." + »unresolvedFunction«(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/receiverMustBeCallable.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/receiverMustBeCallable.smltest new file mode 100644 index 000000000..f8b8068a4 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/receiverMustBeCallable.smltest @@ -0,0 +1,74 @@ +package tests.validation.expressions.calls.receiverMustBeCallable + +annotation MyAnnotation + +class MyClass { + attr myAttribute: Int + class MyClass + enum MyEnum + fun myFunction() +} + +enum MyEnum { + MySimpleEnumInstance + MyComplexEnumInstance() +} + +fun myFunction() + +workflow myWorkflow {} + +step test(a: Int, b: () -> ()) { + val c = 1; + val d = () {}; + val e = () -> 1; + + /* References cannot point to annotations in the first place. */ + // no_semantic_error "This expression must not be called." + »MyAnnotation«(); + // no_semantic_error "This expression must not be called." + »MyClass«(); + // semantic_error "This expression must not be called." + »MyClass().myAttribute«(); + // no_semantic_error "This expression must not be called." + »MyClass.MyClass«(); + // semantic_error "This expression must not be called." + »MyClass.MyEnum«(); + // no_semantic_error "This expression must not be called." + »MyClass().myFunction«(); + // semantic_error "This expression must not be called." + »MyEnum«(); + // no_semantic_error "This expression must not be called." + »MyEnum.MySimpleEnumInstance«(); + // no_semantic_error "This expression must not be called." + »MyEnum.MyComplexEnumInstance«(); + // no_semantic_error "This expression must not be called." + »myFunction«(); + /* References cannot point to workflows in the first place. */ + // no_semantic_error "This expression must not be called." + »myWorkflow«(); + // semantic_error "This expression must not be called." + »a«(); + // no_semantic_error "This expression must not be called." + »b«(); + // semantic_error "This expression must not be called." + »c«(); + // no_semantic_error "This expression must not be called." + »d«(); + // no_semantic_error "This expression must not be called." + »e«(); + // no_semantic_error "This expression must not be called." + »(d)«(); + // no_semantic_error "This expression must not be called." + »(e)«(); + + + /****************************************************************************************************************** + * If a declaration is not in scope we already show a different error. + ******************************************************************************************************************/ + + // no_semantic_error "This expression must not be called." + »unknownGlobal«(); + // no_semantic_error "This expression must not be called." + »MyClass().unknownMember«(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/recursion.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/recursion.smltest new file mode 100644 index 000000000..b4dee8dbb --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/recursion.smltest @@ -0,0 +1,68 @@ +package tests.validation.expressions.calls.recursion + + +workflow w { + // semantic_error "Recursive calls are not allowed." + »a«(); + // semantic_error "Recursive calls are not allowed." + »b«(); + // semantic_error "Recursive calls are not allowed." + »c«(); + // no_semantic_error "Recursive calls are not allowed." + »d«(); + // no_semantic_error "Recursive calls are not allowed." + »f«(); +} + +step a() { + // semantic_error "Recursive calls are not allowed." + »a«(); + // semantic_error "Recursive calls are not allowed." + »b«(); + // semantic_error "Recursive calls are not allowed." + »c«(); + // no_semantic_error "Recursive calls are not allowed." + »d«(); + // no_semantic_error "Recursive calls are not allowed." + »f«(); + + () { + // semantic_error "Recursive calls are not allowed." + »a«(); + // semantic_error "Recursive calls are not allowed." + »b«(); + // semantic_error "Recursive calls are not allowed." + »c«(); + // no_semantic_error "Recursive calls are not allowed." + »d«(); + // no_semantic_error "Recursive calls are not allowed." + »f«(); + }; + + val lambda1 = () { + // no_semantic_error "Recursive calls are not allowed." + »lambda1«(); + }; + + val lambda2 = () { + // no_semantic_error "Recursive calls are not allowed." + »lambda3«(); + }; + + val lambda3 = () { + // no_semantic_error "Recursive calls are not allowed." + »lambda2«(); + }; +} + +step b() { + // semantic_error "Recursive calls are not allowed." + »c«(); +} + +step c() { + // semantic_error "Recursive calls are not allowed." + »b«(); +} + +step d() {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/unnecessaryArgumentList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/unnecessaryArgumentList.smltest new file mode 100644 index 000000000..a8dceb509 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/calls/unnecessaryArgumentList.smltest @@ -0,0 +1,40 @@ +package tests.validation.expressions.calls.unnecessaryArgumentList + +enum MyEnum { + A + B() + C(c: Int = 0) + D(d: Int) + E +} + +workflow test { + // semantic_info "Unnecessary argument list." + MyEnum.A»()«; + // no_semantic_info "Unnecessary argument list." + MyEnum.A»(1)«; + // semantic_info "Unnecessary argument list." + MyEnum.B»()«; + // no_semantic_info "Unnecessary argument list." + MyEnum.B»(1)«; + // no_semantic_info "Unnecessary argument list." + MyEnum.C»()«; + // no_semantic_info "Unnecessary argument list." + MyEnum.C»(1)«; + // no_semantic_info "Unnecessary argument list." + MyEnum.D»()«; + // no_semantic_info "Unnecessary argument list." + MyEnum.D»(1)«; + // no_semantic_info "Unnecessary argument list." + MyEnum.E»()«; + // no_semantic_info "Unnecessary argument list." + MyEnum.E»(1)«; + // no_semantic_info "Unnecessary argument list." + MyEnum.E»()«; + // no_semantic_info "Unnecessary argument list." + MyEnum.E»(1)«; + // no_semantic_info "Unnecessary argument list." + MyEnum.Unresolved»()«; + // no_semantic_info "Unnecessary argument list." + MyEnum.Unresolved»(1)«; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/infixOperation/division by zero.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/infixOperation/division by zero.smltest new file mode 100644 index 000000000..c8754a510 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/infixOperation/division by zero.smltest @@ -0,0 +1,38 @@ +package tests.validation.expressions.infixOperation.divisionByZero + +workflow test { + // semantic_error "Division by zero." + »1.0 / 0.0«; + // semantic_error "Division by zero." + »1.0 / -0.0«; + // no_semantic_error "Division by zero." + »1.0 / 1.0«; + + // semantic_error "Division by zero." + »1.0 / 0«; + // no_semantic_error "Division by zero." + »1.0 / 1«; + + // semantic_error "Division by zero." + »1 / 0.0«; + // semantic_error "Division by zero." + »1 / -0.0«; + // no_semantic_error "Division by zero." + »1 / 1.0«; + + // semantic_error "Division by zero." + »1 / 0«; + // no_semantic_error "Division by zero." + »1 / 1«; + + // no_semantic_error "Division by zero." + »null / 0.0«; + // no_semantic_error "Division by zero." + »null / -0.0«; + // no_semantic_error "Division by zero." + »null / 1.0«; + // no_semantic_error "Division by zero." + »null / 0«; + // no_semantic_error "Division by zero." + »null / 1«; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/infixOperation/unnecessaryElvisOperator.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/infixOperation/unnecessaryElvisOperator.smltest new file mode 100644 index 000000000..8727103fe --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/infixOperation/unnecessaryElvisOperator.smltest @@ -0,0 +1,42 @@ +package validation.expressions.elvis.unnecessaryElvisOperator + +fun f() -> result: Any? + +workflow test { + + // semantic_info "The left operand is never null so the elvis operator is unnecessary (keep left operand)." + »1 ?: 2«; + // semantic_info "The left operand is never null so the elvis operator is unnecessary (keep left operand)." + »1 ?: null«; + // no_semantic_info "The left operand is never null so the elvis operator is unnecessary (keep left operand)." + »null ?: 2«; + // no_semantic_info "The left operand is never null so the elvis operator is unnecessary (keep left operand)." + »null ?: null«; + + // no_semantic_info "The left operand is always null so the elvis operator is unnecessary (keep right operand)." + »1 ?: 2«; + // no_semantic_info "The left operand is always null so the elvis operator is unnecessary (keep right operand)." + »1 ?: null«; + // semantic_info "The left operand is always null so the elvis operator is unnecessary (keep right operand)." + »null ?: 2«; + // no_semantic_info "The left operand is always null so the elvis operator is unnecessary (keep right operand)." + »null ?: null«; + + // no_semantic_info "The right operand is always null so the elvis operator is unnecessary (keep left operand)." + »1 ?: 2«; + // semantic_info "The right operand is always null so the elvis operator is unnecessary (keep left operand)." + »f() ?: null«; + // no_semantic_info "The right operand is always null so the elvis operator is unnecessary (keep left operand)." + »null ?: 2«; + // no_semantic_info "The right operand is always null so the elvis operator is unnecessary (keep left operand)." + »null ?: null«; + + // no_semantic_info "Both operands are always null so the elvis operator is unnecessary (replace with null)." + »1 ?: 2«; + // no_semantic_info "Both operands are always null so the elvis operator is unnecessary (replace with null)." + »1 ?: null«; + // no_semantic_info "Both operands are always null so the elvis operator is unnecessary (replace with null)." + »null ?: 2«; + // semantic_info "Both operands are always null so the elvis operator is unnecessary (replace with null)." + »null ?: null«; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/lambdas/context.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/lambdas/context.smltest new file mode 100644 index 000000000..700de2f98 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/lambdas/context.smltest @@ -0,0 +1,86 @@ +package tests.validation.expressions.lambdas.context + +workflow invalidCases { + // semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + »() {}«; + // semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + »() -> 1«; + // semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + (»() {}«)(); + // semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + (»() -> 1«)(); + // semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + val a = »() {}«; + // semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + val b = »() -> 1«; +} + +step yieldBlockLambdaInStep() -> f: () -> res: Int { + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + yield f = »() { + yield res = 1; + }«; +} + +step yieldExpressionLambdaInStep() -> f: () -> res: Int { + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + yield f = »() -> 1«; +} + +step passLambdasAsArgumentToCallableType(f: (g: () -> res: Int) -> ()) { + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + f(»() { yield res = 1; }«); + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + f(»() -> 1«); +} + +class TestClass(f: () -> res: Int) +enum TestEnum { + Variant(f: () -> res: Int) +} +fun testFunction(f: () -> res: Int) +step testStep1(f: () -> res: Int) {} + +workflow passLambdasAsArguments { + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + TestClass(»() { yield res = 1; }«); + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + TestClass(»() -> 1«); + + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + TestEnum.Variant(»() { yield res = 1; }«); + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + TestEnum.Variant(»() -> 1«); + + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + testFunction(»() { yield res = 1; }«); + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + testFunction(»() -> 1«); + + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + testStep1(»() { yield res = 1; }«); + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + testStep1(»() -> 1«); +} + +/* Special cases */ + +workflow wrappedInParentheses { + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + testFunction((»() -> 1«)); +} + +step testStep2(param) {} + +step testStep3() {} + +workflow parameterHasNoType { + // semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + testStep2(»() -> 1«); + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + testStep2(param2 = »() -> 1«); + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + testStep3(»() -> 1«); + // no_semantic_error "A lambda must either be yielded in a step or assigned to a typed parameter." + unresolved(»() -> 1«); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/lambdas/uniqueNames.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/lambdas/uniqueNames.smltest new file mode 100644 index 000000000..32719c2db --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/lambdas/uniqueNames.smltest @@ -0,0 +1,38 @@ +package tests.uniqueNames + +workflow w { + ( + // semantic_error "A parameter, result or placeholder with name 'duplicateParameter' exists already in this lambda." + »duplicateParameter«, + // semantic_error "A parameter, result or placeholder with name 'duplicateParameter' exists already in this lambda." + »duplicateParameter«, + // no_semantic_error "A parameter, result or placeholder with name 'uniqueParameter' exists already in this lambda." + »uniqueParameter«, + // semantic_error "A parameter, result or placeholder with name 'parameterAndPlaceholder' exists already in this lambda." + »parameterAndPlaceholder«, + // semantic_error "A parameter, result or placeholder with name 'parameterAndResult' exists already in this lambda." + »parameterAndResult« + ) { + // semantic_error "A parameter, result or placeholder with name 'duplicatePlaceholder' exists already in this lambda." + val »duplicatePlaceholder« = 1; + // semantic_error "A parameter, result or placeholder with name 'duplicatePlaceholder' exists already in this lambda." + val »duplicatePlaceholder« = 1; + // no_semantic_error "A parameter, result or placeholder with name 'uniquePlaceholder' exists already in this lambda." + val »uniquePlaceholder« = 1; + // semantic_error "A parameter, result or placeholder with name 'parameterAndPlaceholder' exists already in this lambda." + val »parameterAndPlaceholder« = 1; + // semantic_error "A parameter, result or placeholder with name 'placeholderAndResult' exists already in this lambda." + val »placeholderAndResult« = 1; + + // semantic_error "A parameter, result or placeholder with name 'duplicateResult' exists already in this lambda." + yield »duplicateResult« = 0; + // semantic_error "A parameter, result or placeholder with name 'duplicateResult' exists already in this lambda." + yield »duplicateResult« = 0; + // no_semantic_error "A parameter, result or placeholder with name 'uniqueResult' exists already in this lambda." + yield »uniqueResult« = 0; + // semantic_error "A parameter, result or placeholder with name 'parameterAndResult' exists already in this lambda." + yield »parameterAndResult« = 0; + //semantic_error "A parameter, result or placeholder with name 'placeholderAndResult' exists already in this lambda." + yield »placeholderAndResult« = 0; + }; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/memberAccess/enumVariantMustBeInstantiated.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/memberAccess/enumVariantMustBeInstantiated.smltest new file mode 100644 index 000000000..b70e6da7a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/memberAccess/enumVariantMustBeInstantiated.smltest @@ -0,0 +1,42 @@ +package tests.validation.expressions.memberAccesses.enumVariantMustBeInstantiated + +enum MyEnum { + A + B() + C(c: Int = 0) + D(d: Int) + E +} + +workflow test { + + // no_semantic_error "An enum variant with parameters or type parameters must be instantiated." + MyEnum.»A«; + // no_semantic_error "An enum variant with parameters or type parameters must be instantiated." + MyEnum.»A«(); + + // no_semantic_error "An enum variant with parameters or type parameters must be instantiated." + MyEnum.»B«; + // no_semantic_error "An enum variant with parameters or type parameters must be instantiated." + MyEnum.»B«(); + + // semantic_error "An enum variant with parameters or type parameters must be instantiated." + MyEnum.»C«; + // no_semantic_error "An enum variant with parameters or type parameters must be instantiated." + MyEnum.»C«(); + + // semantic_error "An enum variant with parameters or type parameters must be instantiated." + MyEnum.»D«; + // no_semantic_error "An enum variant with parameters or type parameters must be instantiated." + MyEnum.»D«(); + + // semantic_error "An enum variant with parameters or type parameters must be instantiated." + MyEnum.»E«; + // no_semantic_error "An enum variant with parameters or type parameters must be instantiated." + MyEnum.»E«(); + + // no_semantic_error "An enum variant with parameters or type parameters must be instantiated." + MyEnum.»Unresolved«; + // no_semantic_error "An enum variant with parameters or type parameters must be instantiated." + MyEnum.»Unresolved«(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/memberAccess/instanceMethodMustBeCalled.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/memberAccess/instanceMethodMustBeCalled.smltest new file mode 100644 index 000000000..a696730de --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/memberAccess/instanceMethodMustBeCalled.smltest @@ -0,0 +1,25 @@ +package tests.instanceMethodMustBeCalled + +class MyClass { + fun myInstanceMethod() + static fun myStaticMethod() +} + +fun myFunction() + +step test(a: () -> ()) { + val b = () {}; + + // semantic_error "An instance method must be called." + MyClass().»myInstanceMethod«; + // no_semantic_error "An instance method must be called." + MyClass().»myInstanceMethod«(); + // no_semantic_error "An instance method must be called." + MyClass.»myStaticMethod«; + // no_semantic_error "An instance method must be called." + »myFunction«; + // no_semantic_error "An instance method must be called." + »a«; + // no_semantic_error "An instance method must be called." + »b«; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/memberAccess/missingSafeAccess.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/memberAccess/missingSafeAccess.smltest new file mode 100644 index 000000000..b1b08d35b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/memberAccess/missingSafeAccess.smltest @@ -0,0 +1,9 @@ +package validation.expressions.memberAccess.missingSafeAccess + +workflow test { + + // no_semantic_error "The receiver can be null so a safe access must be used." + »(1).toString«(); + // semantic_error "The receiver can be null so a safe access must be used." + »null.toString«(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/memberAccess/unnecessarySafeAccess.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/memberAccess/unnecessarySafeAccess.smltest new file mode 100644 index 000000000..bacb0b9b3 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/memberAccess/unnecessarySafeAccess.smltest @@ -0,0 +1,9 @@ +package validation.expressions.memberAccess.unnecessarySafeAccess + +workflow test { + + // semantic_info "The receiver is never null so the safe access is unnecessary." + »1?.toString«(); + // no_semantic_info "The receiver is never null so the safe access is unnecessary." + »null?.toString«(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/references/must not statically reference class.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/references/must not statically reference class.smltest new file mode 100644 index 000000000..44b9d1412 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/references/must not statically reference class.smltest @@ -0,0 +1,42 @@ +package tests.validation.references.mustNotStaticallyReferenceClass + +class ClassWithConstructor() + +class ClassWithoutConstructor + +class ClassWithStaticMembers { + static attr myAttribute: Int + + class InnerClassWithConstructor() { + static attr myAttribute: Int + } + + class InnerClassWithoutConstructor +} + +workflow test { + // no_semantic_error "Must not statically reference class." + »ClassWithConstructor«; + // semantic_error "Must not statically reference class." + »ClassWithoutConstructor«; + // no_semantic_error "Must not statically reference class." + »ClassWithoutConstructor«(); + // no_semantic_error "Must not statically reference class." + »ClassWithConstructor«(); + // no_semantic_error "Must not statically reference class." + »ClassWithStaticMembers«.myAttribute; + // no_semantic_error "Must not statically reference class." + »ClassWithStaticMembers«.unresolved; + // no_semantic_error "Must not statically reference class." + // no_semantic_error "Must not statically reference class." + »ClassWithStaticMembers«.»InnerClassWithConstructor«; + // no_semantic_error "Must not statically reference class." + // semantic_error "Must not statically reference class." + »ClassWithStaticMembers«.»InnerClassWithoutConstructor«; + // no_semantic_error "Must not statically reference class." + // no_semantic_error "Must not statically reference class." + »ClassWithStaticMembers«.»InnerClassWithConstructor«(); + // no_semantic_error "Must not statically reference class." + // no_semantic_error "Must not statically reference class." + »ClassWithStaticMembers«.»InnerClassWithConstructor«.myAttribute; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/references/must not statically reference enum.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/references/must not statically reference enum.smltest new file mode 100644 index 000000000..f944d3a14 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/references/must not statically reference enum.smltest @@ -0,0 +1,36 @@ +package tests.validation.references.mustNotStaticallyReferenceEnum + +enum Enum { + Variant +} + +class ClassWithEnum { + enum Enum { + Variant + } + + class ClassWithEnum { + enum Enum { + Variant + } + } +} + +workflow test { + // semantic_error "Must not statically reference enum." + »Enum«; + // semantic_error "Must not statically reference enum." + »Enum«(); + // no_semantic_error "Must not statically reference enum." + »Enum«.Variant; + // no_semantic_error "Must not statically reference enum." + »Enum«.unresolved; + // semantic_error "Must not statically reference enum." + ClassWithEnum.»Enum«; + // no_semantic_error "Must not statically reference enum." + ClassWithEnum.»Enum«.Variant; + // semantic_error "Must not statically reference enum." + ClassWithEnum.ClassWithEnum.»Enum«; + // no_semantic_error "Must not statically reference enum." + ClassWithEnum.ClassWithEnum.»Enum«.Variant; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/templateStrings/missingTemplateExpression.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/templateStrings/missingTemplateExpression.smltest new file mode 100644 index 000000000..36b42b9ca --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/expressions/templateStrings/missingTemplateExpression.smltest @@ -0,0 +1,10 @@ +package validation.expressions.templateStrings.missingTemplateExpression + +workflow test { + // semantic_error "There must be a template expression between two template string parts." + // semantic_error "There must be a template expression between two template string parts." + "start {{ »}} inner {{« »}} end"«; + // no_semantic_error "There must be a template expression between two template string parts." + // no_semantic_error "There must be a template expression between two template string parts." + "start {{ 1 »}} inner {{« 1 »}} end"«; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/arguments must be constant.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/arguments must be constant.smltest new file mode 100644 index 000000000..8cee274f6 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/arguments must be constant.smltest @@ -0,0 +1,16 @@ +package tests.validation.other.annotationCalls.argumentsMustBeConstant + +@Repeatable +annotation MyAnnotation(value: Int) + +fun myFunction() -> value: Int + +// no_semantic_error "Arguments in annotation call must be constant." +@MyAnnotation(»1«) +// no_semantic_error "Arguments in annotation call must be constant." +@MyAnnotation(»1 + 2«) +// semantic_error "Arguments in annotation call must be constant." +@MyAnnotation(»myFunction()«) +// semantic_error "Arguments in annotation call must be constant." +@MyAnnotation(value = »myFunction()«) +class MyClass diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/cardinality.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/cardinality.smltest new file mode 100644 index 000000000..75a7573a8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/cardinality.smltest @@ -0,0 +1,33 @@ +package tests.validation.other.annotationCalls.cardinality + +@Unrepeatable +annotation ExplicitlySingleUse + +annotation ImplicitlySingleUse + +@Repeatable +annotation ExplicitlyMultiUse + +// no_semantic_error "This annotation can only be used once." +»@ExplicitlySingleUse« +// no_semantic_error "This annotation can only be used once." +»@ImplicitlySingleUse« +// no_semantic_error "This annotation can only be used once." +»@ExplicitlyMultiUse« +// no_semantic_error "This annotation can only be used once." +»@ExplicitlyMultiUse« +// no_semantic_error "This annotation can only be used once." +»@UnresolvedAnnotation« +// no_semantic_error "This annotation can only be used once." +»@UnresolvedAnnotation« +class CorrectUse + +// semantic_error "This annotation can only be used once." +»@ExplicitlySingleUse« +// semantic_error "This annotation can only be used once." +»@ExplicitlySingleUse« +// semantic_error "This annotation can only be used once." +»@ImplicitlySingleUse« +// semantic_error "This annotation can only be used once." +»@ImplicitlySingleUse« +class IncorrectUse diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/duplicateTarget.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/duplicateTarget.smltest new file mode 100644 index 000000000..d7f62e321 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/duplicateTarget.smltest @@ -0,0 +1,11 @@ +package tests.validation.other.annotationCalls.duplicateTarget + +@Target( + // semantic_warning "This annotation target is used multiple times." + »AnnotationTarget.Annotation«, + // semantic_warning "This annotation target is used multiple times." + »AnnotationTarget.Annotation«, + // no_semantic_warning "This annotation target is used multiple times." + »AnnotationTarget.Class« +) +annotation TestAnnotation diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/identical python module (1).smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/identical python module (1).smltest new file mode 100644 index 000000000..c0d7a25ba --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/identical python module (1).smltest @@ -0,0 +1,3 @@ +// semantic_info "Python module is identical to Simple-ML package (can remove annotation call)." +»@PythonModule("tests.validation.other.annotationCalls.identicalPythonModule")« +package tests.validation.other.annotationCalls.identicalPythonModule diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/identical python module (2).smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/identical python module (2).smltest new file mode 100644 index 000000000..fe0022887 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/identical python module (2).smltest @@ -0,0 +1,7 @@ +// no_semantic_info "Python module is identical to Simple-ML package (can remove annotation call)." +»@PythonModule("tests.validation.other.annotation_calls.identical_python_module")« +package tests.validation.other.annotationCalls.identicalPythonModule + +// no_semantic_info "Python module is identical to Simple-ML package (can remove annotation call)." +»@PythonModule("TestClass")« +class TestClass diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/identical python name.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/identical python name.smltest new file mode 100644 index 000000000..d9212d195 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/identical python name.smltest @@ -0,0 +1,9 @@ +package tests.validation.other.annotationCalls.identicalPythonName + +// semantic_info "Python name is identical to Simple-ML name (can remove annotation call)." +»@PythonName("TestClass1")« +class TestClass1 + +// no_semantic_info "Python name is identical to Simple-ML name (can remove annotation call)." +»@PythonName("Test_Class_2")« +class TestClass2 diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/missingArgumentList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/missingArgumentList.smltest new file mode 100644 index 000000000..36a232305 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/missingArgumentList.smltest @@ -0,0 +1,35 @@ +package tests.validation.other.annotationCalls.missingArgumentList + +@Repeatable +annotation AnnotationWithoutParameterList + +@Repeatable +annotation AnnotationWithEmptyParameterList() + +@Repeatable +annotation AnnotationWithoutRequiredParameters(a: Int = 0) + +@Repeatable +annotation AnnotationWithRequiredParameters(a: Int) + +// no_semantic_error "Missing argument list." +@»AnnotationWithoutParameterList« +// no_semantic_error "Missing argument list." +@»AnnotationWithoutParameterList()« +// no_semantic_error "Missing argument list." +@»AnnotationWithEmptyParameterList« +// no_semantic_error "Missing argument list." +@»AnnotationWithEmptyParameterList()« +// no_semantic_error "Missing argument list." +@»AnnotationWithoutRequiredParameters« +// no_semantic_error "Missing argument list." +@»AnnotationWithoutRequiredParameters()« +// semantic_error "Missing argument list." +@»AnnotationWithRequiredParameters« +// no_semantic_error "Missing argument list." +@»AnnotationWithRequiredParameters()« +// no_semantic_error "Missing argument list." +@»UnresolvedAnnotation« +// no_semantic_error "Missing argument list." +@»UnresolvedAnnotation()« +class MyClass diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/pure implies no side effects.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/pure implies no side effects.smltest new file mode 100644 index 000000000..cd080cfae --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/pure implies no side effects.smltest @@ -0,0 +1,10 @@ +package tests.validation.other.annotationCalls.pureImpliesNoSideEffects + +// no_semantic_info "Purity implies absence of side effects (remove this annotation call)." +»@NoSideEffects« +fun functionWithoutSideEffects() + +// semantic_info "Purity implies absence of side effects (remove this annotation call)." +»@NoSideEffects« +@Pure +fun pureFunctionWithoutSideEffects() diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/target.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/target.smltest new file mode 100644 index 000000000..7bfb1c96c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/target.smltest @@ -0,0 +1,151 @@ +// semantic_error "This annotation cannot be applied to a compilation unit." +»@AnnotationForEnums« +// no_semantic_error "This annotation cannot be applied to a compilation unit." +»@AnnotationForCompilationUnits« +// no_semantic_error "This annotation cannot be applied to a compilation unit." +»@AnnotationForMultipleTargets« +// no_semantic_error "This annotation cannot be applied to a compilation unit." +»@AnnotationForAnything« +// no_semantic_error "This annotation cannot be applied to a compilation unit." +»@UnresolvedAnnotation« + +package tests.validation.other.annotationCalls.target + +@Target(AnnotationTarget.Annotation) +annotation AnnotationForAnnotations + +@Target(AnnotationTarget.Attribute) +annotation AnnotationForAttributes + +@Target(AnnotationTarget.Class) +annotation AnnotationForClasses + +@Target(AnnotationTarget.CompilationUnit) +annotation AnnotationForCompilationUnits + +@Target(AnnotationTarget.Enum) +annotation AnnotationForEnums + +@Target(AnnotationTarget.EnumVariant) +annotation AnnotationForEnumVariants + +@Target(AnnotationTarget.Function) +annotation AnnotationForFunctions + +@Target(AnnotationTarget.Parameter) +annotation AnnotationForParameters + +@Target(AnnotationTarget.Result) +annotation AnnotationForResults + +@Target(AnnotationTarget.TypeParameter) +annotation AnnotationForTypeParameters + +@Target(AnnotationTarget.Workflow) +annotation AnnotationForWorkflows + +@Target(AnnotationTarget.Step) +annotation AnnotationForSteps + +@Target(AnnotationTarget.CompilationUnit, AnnotationTarget.Class) +annotation AnnotationForMultipleTargets + +annotation AnnotationForAnything + +/* Test declarations -------------------------------------------------------- */ + +// semantic_error "This annotation cannot be applied to an annotation." +»@AnnotationForAttributes« +// no_semantic_error "This annotation cannot be applied to an annotation." +»@AnnotationForAnnotations« +// no_semantic_error "This annotation cannot be applied to an annotation." +»@AnnotationForAnything« +annotation TestAnnotation + +// semantic_error "This annotation cannot be applied to a class." +»@AnnotationForCompilationUnits« +// no_semantic_error "This annotation cannot be applied to a class." +»@AnnotationForClasses« +// no_semantic_error "This annotation cannot be applied to a class." +»@AnnotationForMultipleTargets« +// no_semantic_error "This annotation cannot be applied to a class." +»@AnnotationForAnything« +class TestClass< + + // semantic_error "This annotation cannot be applied to a type parameter." + »@AnnotationForWorkflows« + // no_semantic_error "This annotation cannot be applied to a type parameter." + »@AnnotationForTypeParameters« + // no_semantic_error "This annotation cannot be applied to a type parameter." + »@AnnotationForAnything« + TEST_TYPE_PARAMETER +> { + + // semantic_error "This annotation cannot be applied to an attribute." + »@AnnotationForClasses« + // no_semantic_error "This annotation cannot be applied to an attribute." + »@AnnotationForAttributes« + // no_semantic_error "This annotation cannot be applied to an attribute." + »@AnnotationForAnything« + attr testAttribute: Int +} + +// semantic_error "This annotation cannot be applied to an enum." +»@AnnotationForEnumVariants« +// no_semantic_error "This annotation cannot be applied to an enum." +»@AnnotationForEnums« +// no_semantic_error "This annotation cannot be applied to an enum." +»@AnnotationForAnything« +enum TestEnum { + + // semantic_error "This annotation cannot be applied to an enum variant." + »@AnnotationForFunctions« + // no_semantic_error "This annotation cannot be applied to an enum variant." + »@AnnotationForEnumVariants« + // no_semantic_error "This annotation cannot be applied to an enum variant." + »@AnnotationForAnything« + TestEnumVariant +} + + +// semantic_error "This annotation cannot be applied to a function." +»@AnnotationForParameters« +// no_semantic_error "This annotation cannot be applied to a function." +»@AnnotationForFunctions« +// no_semantic_error "This annotation cannot be applied to a function." +»@AnnotationForAnything« +fun testFunction( + + // semantic_error "This annotation cannot be applied to a parameter." + »@AnnotationForResults« + // no_semantic_error "This annotation cannot be applied to a parameter." + »@AnnotationForParameters« + // no_semantic_error "This annotation cannot be applied to a parameter." + »@AnnotationForAnything« + testParameter: Int +) -> ( + + // semantic_error "This annotation cannot be applied to a result." + »@AnnotationForTypeParameters« + // no_semantic_error "This annotation cannot be applied to a result." + »@AnnotationForResults« + // no_semantic_error "This annotation cannot be applied to a result." + »@AnnotationForAnything« + testResult: Int +) + +// semantic_error "This annotation cannot be applied to a workflow." +»@AnnotationForSteps« +// no_semantic_error "This annotation cannot be applied to a workflow." +»@AnnotationForWorkflows« +// no_semantic_error "This annotation cannot be applied to a workflow." +»@AnnotationForAnything« +workflow testWorkflow {} + +// semantic_error "This annotation cannot be applied to a step." +»@AnnotationForAnnotations« +// no_semantic_error "This annotation cannot be applied to a step." +»@AnnotationForSteps« +// no_semantic_error "This annotation cannot be applied to a step." +»@AnnotationForAnything« +step testStep() {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/unnecessaryArgumentList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/unnecessaryArgumentList.smltest new file mode 100644 index 000000000..e23be7081 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/annotationCalls/unnecessaryArgumentList.smltest @@ -0,0 +1,35 @@ +package tests.validation.other.annotationCalls.unnecessaryArgumentList + +@Repeatable +annotation AnnotationWithoutParameterList + +@Repeatable +annotation AnnotationWithEmptyParameterList() + +@Repeatable +annotation AnnotationWithoutRequiredParameters(a: Int = 0) + +@Repeatable +annotation AnnotationWithRequiredParameters(a: Int) + +// semantic_info "Unnecessary argument list." +@AnnotationWithoutParameterList»()« +// no_semantic_info "Unnecessary argument list." +@AnnotationWithoutParameterList»(1)« +// semantic_info "Unnecessary argument list." +@AnnotationWithEmptyParameterList»()« +// no_semantic_info "Unnecessary argument list." +@AnnotationWithEmptyParameterList»(1)« +// semantic_info "Unnecessary argument list." +@AnnotationWithoutRequiredParameters»()« +// no_semantic_info "Unnecessary argument list." +@AnnotationWithoutRequiredParameters»(1)« +// no_semantic_info "Unnecessary argument list." +@AnnotationWithParameters»()« +// no_semantic_info "Unnecessary argument list." +@AnnotationWithParameters»(1)« +// no_semantic_info "Unnecessary argument list." +@UnresolvedAnnotation»()« +// no_semantic_info "Unnecessary argument list." +@UnresolvedAnnotation»(1)« +class MyClass diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/argumentLists/missingRequiredParameter.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/argumentLists/missingRequiredParameter.smltest new file mode 100644 index 000000000..a8e714728 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/argumentLists/missingRequiredParameter.smltest @@ -0,0 +1,82 @@ +package tests.validation.other.argumentLists.missingRequiredParameter + +annotation A(a: Int, b: Int = 0) + +// semantic_error "The parameter 'a' is required and must be set here." +@A»()« +class C(a: Int, b: Int = 0) + +enum E { + V(a: Int, b: Int = 0) +} + +// no_semantic_error "The parameter 'a' is required and must be set here." +@A»(a = 1)« +// no_semantic_error "The parameter 'b' is required and must be set here." +@A»(a = 1, b = 1)« +// no_semantic_error "The parameter 'a' is required and must be set here." +@A»(1, 2, 3)« +fun f(a: Int, b: Int = 0) + +step g(f: (a: Int, b: Int = 0) -> ()) { + // semantic_error "The parameter 'a' is required and must be set here." + f»()«; + // no_semantic_error "The parameter 'a' is required and must be set here." + f»(a = 1)«; + // no_semantic_error "The parameter 'b' is required and must be set here." + f»(a = 1, b = 1)«; + // no_semantic_error "The parameter 'a' is required and must be set here." + f»(1, 2, 3)«; +} + +step stepWithVariadicParameter(vararg values: Int) {} + +workflow myWorkflow { + // semantic_error "The parameter 'a' is required and must be set here." + C»()«; + // no_semantic_error "The parameter 'a' is required and must be set here." + C»(a = 1)«; + // no_semantic_error "The parameter 'b' is required and must be set here." + C»(a = 1, b = 1)«; + // no_semantic_error "The parameter 'a' is required and must be set here." + C»(1, 2, 3)«; + + // semantic_error "The parameter 'a' is required and must be set here." + E.V»()«; + // no_semantic_error "The parameter 'a' is required and must be set here." + E.V»(a = 1)«; + // no_semantic_error "The parameter 'b' is required and must be set here." + E.V»(a = 1, b = 1)«; + // no_semantic_error "The parameter 'a' is required and must be set here." + E.V»(1, 2, 3)«; + + // semantic_error "The parameter 'a' is required and must be set here." + f»()«; + // no_semantic_error "The parameter 'a' is required and must be set here." + f»(a = 1)«; + // no_semantic_error "The parameter 'b' is required and must be set here." + f»(a = 1, b = 1)«; + // no_semantic_error "The parameter 'a' is required and must be set here." + f»(1, 2, 3)«; + + // semantic_error "The parameter 'a' is required and must be set here." + ((a, b) {})»()«; + // no_semantic_error "The parameter 'a' is required and must be set here." + ((a, b) {})»(a = 1)«; + // no_semantic_error "The parameter 'b' is required and must be set here." + ((a, b) {})»(a = 1, b = 1)«; + // no_semantic_error "The parameter 'a' is required and must be set here." + ((a, b) {})»(1, 2, 3)«; + + // semantic_error "The parameter 'a' is required and must be set here." + ((a, b) -> 1)»()«; + // no_semantic_error "The parameter 'a' is required and must be set here." + ((a, b) -> 1)»(a = 1)«; + // no_semantic_error "The parameter 'b' is required and must be set here." + ((a, b) -> 1)»(a = 1, b = 1)«; + // no_semantic_error "The parameter 'a' is required and must be set here." + ((a, b) -> 1)»(1, 2, 3)«; + + // no_semantic_error "The parameter 'values' is required and must be set here." + stepWithVariadicParameter(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/argumentLists/noPositionalArgumentsAfterFirstNamedArgument.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/argumentLists/noPositionalArgumentsAfterFirstNamedArgument.smltest new file mode 100644 index 000000000..d74adc193 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/argumentLists/noPositionalArgumentsAfterFirstNamedArgument.smltest @@ -0,0 +1,45 @@ +package tests.validation.other.argumentLists.noPositionalArgumentsAfterFirstNamedArgument + +annotation MyAnnotation(a: Int, b: Int = 0, c: Int = 0, d: Int = 0, vararg e: Int) + +// no_semantic_error "After the first named argument all arguments must be named." +// no_semantic_error "After the first named argument all arguments must be named." +// semantic_error "After the first named argument all arguments must be named." +// no_semantic_error "After the first named argument all arguments must be named." +// semantic_error "After the first named argument all arguments must be named." +@MyAnnotation(»0«, »a = 1«, »2«, »b = 3«, »4«) class MyClass1 + +// no_semantic_error "After the first named argument all arguments must be named." +@MyAnnotation(»0«) class MyClass2 + +// no_semantic_error "After the first named argument all arguments must be named." +// no_semantic_error "After the first named argument all arguments must be named." +// semantic_error "After the first named argument all arguments must be named." +// no_semantic_error "After the first named argument all arguments must be named." +@UnresolvedAnnotation(»0«, »a = 1«, »2«, »b = 3«) class MyClass3 + +// no_semantic_error "After the first named argument all arguments must be named." +@UnresolvedAnnotation(»0«) class MyClass4 + +fun f(a: Int, b: Int = 0, c: Int = 0, d: Int = 0, vararg e: Int) + +workflow myWorkflow { + // no_semantic_error "After the first named argument all arguments must be named." + // no_semantic_error "After the first named argument all arguments must be named." + // semantic_error "After the first named argument all arguments must be named." + // no_semantic_error "After the first named argument all arguments must be named." + // semantic_error "After the first named argument all arguments must be named." + f(»0«, »a = 1«, »2«, »b = 3«, »4«); + + // no_semantic_error "After the first named argument all arguments must be named." + f(»0«); + + // no_semantic_error "After the first named argument all arguments must be named." + // no_semantic_error "After the first named argument all arguments must be named." + // semantic_error "After the first named argument all arguments must be named." + // no_semantic_error "After the first named argument all arguments must be named." + unresolvedCallable(»0«, »a = 1«, »2«, »b = 3«); + + // no_semantic_error "After the first named argument all arguments must be named." + unresolvedCallable(»0«); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/argumentLists/tooManyArguments.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/argumentLists/tooManyArguments.smltest new file mode 100644 index 000000000..a7cce0459 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/argumentLists/tooManyArguments.smltest @@ -0,0 +1,88 @@ +package tests.validation.other.argumentLists.tooManyArguments + +annotation MyAnnotation1(a: Int, b: Int = 0) +annotation MyAnnotation2(a: Int) +annotation MyAnnotation3(vararg a: Int) + +// semantic_error "Expected between 1 and 2 arguments but got 3." +@MyAnnotation1»(1, 2, 3)« +// semantic_error "Expected exactly 1 argument but got 3." +@MyAnnotation2»(1, 2, 3)« +class MyClass1(a: Int, b: Int = 0) + +// no_semantic_error "Expected between 1 and 2 arguments but got 0." +@MyAnnotation1»(1)« +// no_semantic_error "Expected exactly 1 argument but got 0." +@MyAnnotation2»(1)« +// no_semantic_error "Expected exactly 1 argument but got 2." +@MyAnnotation3»(1, 2)« +class MyClass2(a: Int) +class MyClass3(vararg a: Int) + +enum MyEnum { + MyVariant1(a: Int, b: Int = 0) + MyVariant2(a: Int) + MyVariant3(vararg a: Int) +} + +fun myFunction1(a: Int, b: Int = 0) +fun myFunction2(a: Int) +fun myFunction3(vararg a: Int) + +step g(f1: (a: Int, b: Int = 0) -> (), f2: (a: Int) -> (), f3: (vararg a: Int) -> ()) { + // semantic_error "Expected between 1 and 2 arguments but got 3." + f1»(1, 2, 3)«; + // semantic_error "Expected exactly 1 argument but got 3." + f2»(1, 2, 3)«; + // no_semantic_error "Expected between 1 and 2 arguments but got 0." + f1»(1)«; + // no_semantic_error "Expected exactly 1 argument but got 0." + f2»(1)«; + // no_semantic_error "Expected exactly 1 argument but got 2." + f3»(1, 2)«; +} + +workflow myWorkflow { + // semantic_error "Expected between 1 and 2 arguments but got 3." + MyClass1»(1, 2, 3)«; + // semantic_error "Expected exactly 1 argument but got 3." + MyClass2»(1, 2, 3)«; + // no_semantic_error "Expected between 1 and 2 arguments but got 0." + MyClass1»(1)«; + // no_semantic_error "Expected exactly 1 argument but got 0." + MyClass2»(1)«; + // no_semantic_error "Expected exactly 1 argument but got 2." + MyClass3»(1, 2)«; + + // semantic_error "Expected between 1 and 2 arguments but got 3." + MyEnum.MyVariant1»(1, 2, 3)«; + // semantic_error "Expected exactly 1 argument but got 3." + MyEnum.MyVariant2»(1, 2, 3)«; + // no_semantic_error "Expected between 1 and 2 arguments but got 0." + MyEnum.MyVariant1»(1)«; + // no_semantic_error "Expected exactly 1 argument but got 0." + MyEnum.MyVariant2»(1)«; + // no_semantic_error "Expected exactly 1 argument but got 2." + MyEnum.MyVariant3»(1, 2)«; + + // semantic_error "Expected between 1 and 2 arguments but got 3." + myFunction1»(1, 2, 3)«; + // semantic_error "Expected exactly 1 argument but got 3." + myFunction2»(1, 2, 3)«; + // no_semantic_error "Expected between 1 and 2 arguments but got 0." + myFunction1»(1)«; + // no_semantic_error "Expected exactly 1 argument but got 0." + myFunction2»(1)«; + // no_semantic_error "Expected exactly 1 argument but got 2." + myFunction3»(1, 2)«; + + // semantic_error "Expected exactly 1 argument but got 3." + ((a) {})»(1, 2, 3)«; + // no_semantic_error "Expected exactly 1 argument but got 0." + ((a) {})»(1)«; + + // semantic_error "Expected exactly 1 argument but got 3." + ((a) -> 1)»(1, 2, 3)«; + // no_semantic_error "Expected exactly 1 argument but got 0." + ((a) -> 1)»(1)«; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/argumentLists/uniqueParameters.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/argumentLists/uniqueParameters.smltest new file mode 100644 index 000000000..dc18cd937 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/argumentLists/uniqueParameters.smltest @@ -0,0 +1,162 @@ +package tests.validation.other.argumentLists.uniqueParameters + +annotation A(a: Int, b: Int = 0) + +@A( + // semantic_error "The parameter 'a' is already set." + »1«, + // semantic_error "The parameter 'a' is already set." + »a = 1« +) +@A( + // semantic_error "The parameter 'b' is already set." + »b = 1«, + // semantic_error "The parameter 'b' is already set." + »b = 1« +) +class C(a: Int, b: Int = 0) + +enum E { + V(a: Int, b: Int = 0) +} + +@A( + // no_semantic_error "The parameter 'a' is already set." + »a = 1«, + // no_semantic_error "The parameter 'b' is already set." + »b = 1« +) +fun f(a: Int, b: Int = 0, vararg c: Int) + +step g(f: (a: Int, b: Int = 0) -> ()) { + f( + // semantic_error "The parameter 'a' is already set." + »1«, + // semantic_error "The parameter 'a' is already set." + »a = 1« + ); + f( + // semantic_error "The parameter 'b' is already set." + »b = 1«, + // semantic_error "The parameter 'b' is already set." + »b = 1« + ); + f( + // no_semantic_error "The parameter 'a' is already set." + »a = 1«, + // no_semantic_error "The parameter 'b' is already set." + »b = 1« + ); + f( + // no_semantic_error "The parameter 'c' is already set." + »c = 1«, + // no_semantic_error "The parameter 'c' is already set." + »c = 1« + ); + f( + 1, + 2, + // no_semantic_error "The parameter 'c' is already set." + »3«, + // no_semantic_error "The parameter 'c' is already set." + »4« + ); +} + +workflow test { + C( + // semantic_error "The parameter 'a' is already set." + »1«, + // semantic_error "The parameter 'a' is already set." + »a = 1« + ); + C( + // semantic_error "The parameter 'b' is already set." + »b = 1«, + // semantic_error "The parameter 'b' is already set." + »b = 1« + ); + C( + // no_semantic_error "The parameter 'a' is already set." + »a = 1«, + // no_semantic_error "The parameter 'b' is already set." + »b = 1« + ); + + E.V( + // semantic_error "The parameter 'a' is already set." + »1«, + // semantic_error "The parameter 'a' is already set." + »a = 1« + ); + E.V( + // semantic_error "The parameter 'b' is already set." + »b = 1«, + // semantic_error "The parameter 'b' is already set." + »b = 1« + ); + E.V( + // no_semantic_error "The parameter 'a' is already set." + »a = 1«, + // no_semantic_error "The parameter 'b' is already set." + »b = 1« + ); + + + f( + // semantic_error "The parameter 'a' is already set." + »1«, + // semantic_error "The parameter 'a' is already set." + »a = 1« + ); + f( + // semantic_error "The parameter 'b' is already set." + »b = 1«, + // semantic_error "The parameter 'b' is already set." + »b = 1« + ); + f( + // no_semantic_error "The parameter 'a' is already set." + »a = 1«, + // no_semantic_error "The parameter 'b' is already set." + »b = 1« + ); + + ((a, b) {})( + // semantic_error "The parameter 'a' is already set." + »1«, + // semantic_error "The parameter 'a' is already set." + »a = 1« + ); + ((a, b) {})( + // semantic_error "The parameter 'b' is already set." + »b = 1«, + // semantic_error "The parameter 'b' is already set." + »b = 1« + ); + ((a, b) {})( + // no_semantic_error "The parameter 'a' is already set." + »a = 1«, + // no_semantic_error "The parameter 'b' is already set." + »b = 1« + ); + + ((a, b) -> 1)( + // semantic_error "The parameter 'a' is already set." + »1«, + // semantic_error "The parameter 'a' is already set." + »a = 1« + ); + ((a, b) -> 1)( + // semantic_error "The parameter 'b' is already set." + »b = 1«, + // semantic_error "The parameter 'b' is already set." + »b = 1« + ); + ((a, b) -> 1)( + // no_semantic_error "The parameter 'a' is already set." + »a = 1«, + // no_semantic_error "The parameter 'b' is already set." + »b = 1« + ); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/is unnecessary in call.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/is unnecessary in call.smltest new file mode 100644 index 000000000..77a58914c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/is unnecessary in call.smltest @@ -0,0 +1,46 @@ +package tests.validation.expressions.typeArgumentList.isUnnecessaryInCall + +class ClassWithoutTypeParameters() +class ClassWithTypeParameters() + +enum Enum { + VariantWithoutTypeParameters + VariantWithTypeParameters +} + +fun functionWithoutTypeParameters() +fun functionWithTypeParameters() + +workflow myWorkflow { + // semantic_info "Unnecessary type argument list." + ClassWithoutTypeParameters»<>«(); + // semantic_info "Unnecessary type argument list." + ClassWithoutTypeParameters»«(); + // no_semantic_info "Unnecessary type argument list." + ClassWithTypeParameters»<>«(); + // no_semantic_info "Unnecessary type argument list." + ClassWithTypeParameters»«(); + + // semantic_info "Unnecessary type argument list." + Enum.VariantWithoutTypeParameters»<>«(); + // semantic_info "Unnecessary type argument list." + Enum.VariantWithoutTypeParameters»«(); + // no_semantic_info "Unnecessary type argument list." + Enum.VariantWithTypeParameters»<>«(); + // no_semantic_info "Unnecessary type argument list." + Enum.VariantWithTypeParameters»«(); + + // semantic_info "Unnecessary type argument list." + functionWithoutTypeParameters»<>«(); + // semantic_info "Unnecessary type argument list." + functionWithoutTypeParameters»«(); + // no_semantic_info "Unnecessary type argument list." + functionWithTypeParameters»<>«(); + // no_semantic_info "Unnecessary type argument list." + functionWithTypeParameters»«(); + + // no_semantic_info "Unnecessary type argument list." + unresolved»<>«(); + // no_semantic_info "Unnecessary type argument list." + unresolved»«(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/is unnecessary in named type.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/is unnecessary in named type.smltest new file mode 100644 index 000000000..d713aa573 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/is unnecessary in named type.smltest @@ -0,0 +1,46 @@ +package tests.validation.expressions.typeArgumentList.isUnnecessaryNamedType + +class ClassWithoutTypeParameters() +class ClassWithTypeParameters() + +enum Enum { + VariantWithoutTypeParameters + VariantWithTypeParameters +} + +fun functionWithoutTypeParameters() +fun functionWithTypeParameters() + +fun myFunction( + // semantic_info "Unnecessary type argument list." + a: ClassWithoutTypeParameters»<>«, + // semantic_info "Unnecessary type argument list." + b: ClassWithoutTypeParameters»«, + // no_semantic_info "Unnecessary type argument list." + c: ClassWithTypeParameters»<>«, + // no_semantic_info "Unnecessary type argument list." + d: ClassWithTypeParameters»«, + + // semantic_info "Unnecessary type argument list." + e: Enum.VariantWithoutTypeParameters»<>«, + // semantic_info "Unnecessary type argument list." + f: Enum.VariantWithoutTypeParameters»«, + // no_semantic_info "Unnecessary type argument list." + g: Enum.VariantWithTypeParameters»<>«, + // no_semantic_info "Unnecessary type argument list." + h: Enum.VariantWithTypeParameters»«, + + // no_semantic_info "Unnecessary type argument list." + i: functionWithoutTypeParameters»<>«, + // no_semantic_info "Unnecessary type argument list." + j: functionWithoutTypeParameters»«, + // no_semantic_info "Unnecessary type argument list." + k: functionWithTypeParameters»<>«, + // no_semantic_info "Unnecessary type argument list." + l: functionWithTypeParameters»«, + + // no_semantic_info "Unnecessary type argument list." + m: Unresolved»<>«, + // no_semantic_info "Unnecessary type argument list." + n: Unresolved»«, +) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/missingRequiredTypeParameter.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/missingRequiredTypeParameter.smltest new file mode 100644 index 000000000..e38e56ea6 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/missingRequiredTypeParameter.smltest @@ -0,0 +1,12 @@ +package tests.missingRequiredTypeParameter + +class MyClass + +step myStep( + // semantic_error "The type parameter 'T' is required and must be set here." + g: MyClass»<>«, + // no_semantic_error "The type parameter 'T' is required and must be set here." + h: MyClass»«, + // no_semantic_error "The type parameter 'T' is required and must be set here." + i: MyClass»« +) {} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/noPositionalTypeArgumentsAfterFirstNamedTypeArgument.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/noPositionalTypeArgumentsAfterFirstNamedTypeArgument.smltest new file mode 100644 index 000000000..f1e74d199 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/noPositionalTypeArgumentsAfterFirstNamedTypeArgument.smltest @@ -0,0 +1,25 @@ +package tests.noPositionalTypeArgumentsAfterFirstNamedTypeArgument + +// no_semantic_error "After the first named type argument all type arguments must be named." +// no_semantic_error "After the first named type argument all type arguments must be named." +// semantic_error "After the first named type argument all type arguments must be named." +// no_semantic_error "After the first named type argument all type arguments must be named." +step myStep1( + f: MyClass<»Int«, »A = Int«, »Int«, »B = Int«> +) {} + +// no_semantic_error "After the first named type argument all type arguments must be named." +step myStep2( + f: MyClass<»Int«> +) {} + +workflow myWorkflow { + // no_semantic_error "After the first named type argument all type arguments must be named." + // no_semantic_error "After the first named type argument all type arguments must be named." + // semantic_error "After the first named type argument all type arguments must be named." + // no_semantic_error "After the first named type argument all type arguments must be named." + call<»Int«, »A = Int«, »Int«, »B = Int«>(); + + // no_semantic_error "After the first named type argument all type arguments must be named." + call<»Int«>(); +} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/tooManyTypeArguments.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/tooManyTypeArguments.smltest new file mode 100644 index 000000000..ba1a3d73d --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/tooManyTypeArguments.smltest @@ -0,0 +1,17 @@ +package tests.validation.typeArgumentLists.tooManyTypeArguments + +class MyClass1 +class MyClass2 + +step myStep( + // no_semantic_error r"Expected exactly \d* type arguments? but got \d*\." + f: MyClass1»<>«, + // no_semantic_error r"Expected exactly \d* type arguments? but got \d*\." + g: MyClass1»«, + // semantic_error "Expected exactly 1 type argument but got 2." + h: MyClass1»«, + // semantic_error "Expected exactly 2 type arguments but got 3." + i: MyClass2»«, + // no_semantic_error r"Expected exactly \d* type arguments? but got \d*\." + j: Unresolved»« +) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/uniqueTypeParameters.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/uniqueTypeParameters.smltest new file mode 100644 index 000000000..0563ed69f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/other/typeArgumentLists/uniqueTypeParameters.smltest @@ -0,0 +1,30 @@ +package tests.validation.typeArgumentLists.uniqueTypeParameters + +class MyClass + +step myStep( + f: MyClass< + // semantic_error "The type parameter 'A' is already set." + »Int«, + // semantic_error "The type parameter 'A' is already set." + »A = Int« + >, + g: MyClass< + // semantic_error "The type parameter 'B' is already set." + »B = Int«, + // semantic_error "The type parameter 'B' is already set." + »B = Int« + >, + h: MyClass< + // no_semantic_error r"The type parameter '\w+' is already set\." + »A = Int«, + // no_semantic_error r"The type parameter '\w+' is already set\." + »B = Int« + >, + i: MyClass< + // no_semantic_error r"The type parameter '\w+' is already set\." + »Unresolved = Int«, + // no_semantic_error r"The type parameter '\w+' is already set\." + »Unresolved = Int« + > +) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/assignments/hasNoEffect.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/assignments/hasNoEffect.smltest new file mode 100644 index 000000000..b232f8fa5 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/assignments/hasNoEffect.smltest @@ -0,0 +1,19 @@ +step myFunction() -> a: Int { + // semantic_warning "This statement does nothing." + »_ = 1 + 2;« + + // no_semantic_warning "This statement does nothing." + »val a = 1;« + // no_semantic_warning "This statement does nothing." + »yield a = 1;« + + () { + // semantic_warning "This statement does nothing." + »_ = 1 + 2;« + + // no_semantic_warning "This statement does nothing." + »val a = 1;« + // no_semantic_warning "This statement does nothing." + »yield a = 1;« + }; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/assignments/ignoresResultOfCall.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/assignments/ignoresResultOfCall.smltest new file mode 100644 index 000000000..6af1ff8a8 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/assignments/ignoresResultOfCall.smltest @@ -0,0 +1,65 @@ +package tests.ignoresResultOfCall + +class MyClass { + static fun oneResult() -> first: Int + static fun twoResults() -> (first: Int, second: Int) +} + +fun functionOneResult() -> first: Int +fun functionTwoResults() -> (first: Int, second: Int) + +step stepOneResult() -> first: Int {} +step stepTwoResults() -> (first: Int, second: Int) {} + +step test( + paramOneResult: () -> first: Int, + paramTwoResults: () -> (first: Int, second: Int) +) { + val lambdaOneResult = () { + yield first = 1; + }; + val lambdaTwoResults = () { + yield first = 1; + yield second = 2; + }; + + // no_semantic_warning "The result 'first' is implicitly ignored." + »val methodResult1« = MyClass.oneResult(); + // no_semantic_warning "The result 'first' is implicitly ignored." + »val methodResult2« = MyClass.twoResults(); + // semantic_warning "The result 'second' is implicitly ignored." + »val methodResult3« = MyClass.twoResults(); + + // no_semantic_warning "The result 'first' is implicitly ignored." + »val functionResult1« = functionOneResult(); + // no_semantic_warning "The result 'first' is implicitly ignored." + »val functionResult2« = functionTwoResults(); + // semantic_warning "The result 'second' is implicitly ignored." + »val functionResult3« = functionTwoResults(); + + // no_semantic_warning "The result 'first' is implicitly ignored." + »val stepResult1« = stepOneResult(); + // no_semantic_warning "The result 'first' is implicitly ignored." + »val stepResult2« = stepTwoResults(); + // semantic_warning "The result 'second' is implicitly ignored." + »val stepResult3« = stepTwoResults(); + + // no_semantic_warning "The result 'first' is implicitly ignored." + »val callableResult1« = paramOneResult(); + // no_semantic_warning "The result 'first' is implicitly ignored." + »val callableResult2« = paramTwoResults(); + // semantic_warning "The result 'second' is implicitly ignored." + »val callableResult3« = paramTwoResults(); + + // no_semantic_warning "The result 'first' is implicitly ignored." + »val lambdaResult1« = paramOneResult(); + // no_semantic_warning "The result 'first' is implicitly ignored." + »val lambdaResult2« = paramTwoResults(); + // semantic_warning "The result 'second' is implicitly ignored." + »val lambdaResult3« = paramTwoResults(); + + /* If the call cannot be resolved, no additional error should be displayed. */ + + // no_semantic_warning "The result '???' is implicitly ignored." + »val unresolved« = bla(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/assignments/nothingAssigned.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/assignments/nothingAssigned.smltest new file mode 100644 index 000000000..dbbe985fd --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/assignments/nothingAssigned.smltest @@ -0,0 +1,29 @@ +package tests.nothing_assigned + +fun noResults() +fun oneResult() -> first: Int +fun twoResults() -> (first: Int, second: Int) + +step f() { + // semantic_error "No value is assigned to this assignee." + »val a« = noResults(); + // semantic_error "No value is assigned to this assignee." + _, »val b« = oneResult(); + // semantic_error "No value is assigned to this assignee." + _, _, »val c« = twoResults(); + // semantic_error "No value is assigned to this assignee." + _, »val d« = 1; + + // no_semantic_error "No value is assigned to this assignee." + »val e« = oneResult(); + // no_semantic_error "No value is assigned to this assignee." + »val f« = twoResults(); + + + /****************************************************************************************************************** + * If we call an unknown function we already show a different error. + ******************************************************************************************************************/ + + // no_semantic_error "No value is assigned to this assignee." + »val g« = unknownFunction(); +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/assignments/unnecessaryAssignment.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/assignments/unnecessaryAssignment.smltest new file mode 100644 index 000000000..13687f839 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/assignments/unnecessaryAssignment.smltest @@ -0,0 +1,31 @@ +package tests.validation.statements.assignments.unnecessaryAssignment + +fun f() -> (a: Int, b: Int) + +class MyClass { + fun f() -> (a: Int, b: Int) +} + +step myFunction() -> a: Int { + // semantic_info "This assignment can be converted to an expression statement." + »_, _ = f();« + // semantic_info "This assignment can be converted to an expression statement." + »_, _ = MyClass().f();« + + // no_semantic_info "This assignment can be converted to an expression statement." + »val a = 1;« + // no_semantic_info "This assignment can be converted to an expression statement." + »yield a = 1;« + + () { + // semantic_info "This assignment can be converted to an expression statement." + »_, _ = f();« + // semantic_info "This assignment can be converted to an expression statement." + »_, _ = MyClass().f();« + + // no_semantic_info "This assignment can be converted to an expression statement." + »val a = 1;« + // no_semantic_info "This assignment can be converted to an expression statement." + »yield a = 1;« + }; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/expressionStatements/hasNoEffect.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/expressionStatements/hasNoEffect.smltest new file mode 100644 index 000000000..b5fc08bdb --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/statements/expressionStatements/hasNoEffect.smltest @@ -0,0 +1,78 @@ +package tests.validation.statements.expressionStatements.hasNoEffect + +fun impureFunction() +@Pure fun pureFunction() -> a: Int + +class MyClass() { + fun impureFunction() + @Pure fun pureFunction() +} + +step pureStep() { + val a = pureFunction(); +} + +step impureStep() { + impureFunction(); +} + +step recursiveA() { + recursiveB(); +} + +step recursiveB() { + recursiveA(); +} + +step myStep() { + // semantic_warning "This statement does nothing." + »1 + 2;« + // semantic_warning "This statement does nothing." + »pureFunction();« + // semantic_warning "This statement does nothing." + »MyClass().pureFunction();« + + // no_semantic_warning "This statement does nothing." + »impureFunction();« + // no_semantic_warning "This statement does nothing." + »MyClass().impureFunction();« + + () { + // semantic_warning "This statement does nothing." + »1 + 2;« + // semantic_warning "This statement does nothing." + »pureFunction();« + // semantic_warning "This statement does nothing." + »MyClass().pureFunction();« + + // no_semantic_warning "This statement does nothing." + »impureFunction();« + // no_semantic_warning "This statement does nothing." + »MyClass().impureFunction();« + }; + + // semantic_warning "This statement does nothing." + »(() { + pureFunction(); + MyClass().pureFunction(); + })();« + + // semantic_warning "This statement does nothing." + »pureStep();« + + // no_semantic_warning "This statement does nothing." + »(() { + impureFunction(); + })();« + + // no_semantic_warning "This statement does nothing." + »(() { + MyClass().impureFunction(); + })();« + + // no_semantic_warning "This statement does nothing." + »impureStep();« + + // no_semantic_warning "This statement does nothing." + »recursiveA();« +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/arguments.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/arguments.smltest new file mode 100644 index 000000000..cdd85aea0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/arguments.smltest @@ -0,0 +1,336 @@ +package tests.validation.typeChecking.arguments + +step myStep(vararg variadicParam: Int) { + + // no_semantic_error "An argument of type '(Int) -> (Int)' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(»intToInt«); + // semantic_error "An argument of type '(C) -> ()' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(»f2«); + // semantic_error "An argument of type 'B' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(»B()«); + // semantic_error "An argument of type 'C' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(callableType = »C()«); + // semantic_error "An argument of type 'D' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(»D()«); + // semantic_error "An argument of type 'C?' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(»maybeC()«); + // semantic_error "An argument of type 'MyEnum1' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(»someVariantOfMyEnum1()«); + // semantic_error "An argument of type 'MyEnum2' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(»someVariantOfMyEnum2()«); + // semantic_error "An argument of type 'MyEnum1.Variant1' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(»MyEnum1.Variant1«); + // semantic_error "An argument of type 'MyEnum1.Variant2' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(»MyEnum1.Variant2«); + // semantic_error "An argument of type 'MyEnum2.Variant1' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(»MyEnum2.Variant1«); + // semantic_error "An argument of type 'union' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(»aOrC()«); + // semantic_error "An argument of type 'union' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(»bOrC()«); + // semantic_error "An argument of type 'vararg' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(»variadicParam«); + // no_semantic_error "An argument of type '$Unresolved' cannot be assigned to a parameter of type '(Int) -> (Int)'." + f1(»unresolved«); + // no_semantic_error r"An argument of type '[^']*' cannot be assigned to a parameter of type '[^']*'." + f1(unresolved = »1«); + + // semantic_error "An argument of type '(Int) -> (Int)' cannot be assigned to a parameter of type 'C'." + f2(»intToInt«); + // semantic_error "An argument of type '(C) -> ()' cannot be assigned to a parameter of type 'C'." + f2(»f2«); + // semantic_error "An argument of type 'B' cannot be assigned to a parameter of type 'C'." + f2(»B()«); + // no_semantic_error "An argument of type 'C' cannot be assigned to a parameter of type 'C'." + f2(classType = »C()«); + // no_semantic_error "An argument of type 'D' cannot be assigned to a parameter of type 'C'." + f2(»D()«); + // semantic_error "An argument of type 'C?' cannot be assigned to a parameter of type 'C'." + f2(»maybeC()«); + // semantic_error "An argument of type 'MyEnum1' cannot be assigned to a parameter of type 'C'." + f2(»someVariantOfMyEnum1()«); + // semantic_error "An argument of type 'MyEnum2' cannot be assigned to a parameter of type 'C'." + f2(»someVariantOfMyEnum2()«); + // semantic_error "An argument of type 'MyEnum1.Variant1' cannot be assigned to a parameter of type 'C'." + f2(»MyEnum1.Variant1«); + // semantic_error "An argument of type 'MyEnum1.Variant2' cannot be assigned to a parameter of type 'C'." + f2(»MyEnum1.Variant2«); + // semantic_error "An argument of type 'MyEnum2.Variant1' cannot be assigned to a parameter of type 'C'." + f2(»MyEnum2.Variant1«); + // semantic_error "An argument of type 'union' cannot be assigned to a parameter of type 'C'." + f2(»aOrC()«); + // semantic_error "An argument of type 'union' cannot be assigned to a parameter of type 'C'." + f2(»bOrC()«); + // semantic_error "An argument of type 'vararg' cannot be assigned to a parameter of type 'C'." + f2(»variadicParam«); + // no_semantic_error "An argument of type '$Unresolved' cannot be assigned to a parameter of type 'C'." + f2(»unresolved«); + // no_semantic_error r"An argument of type '[^']*' cannot be assigned to a parameter of type '[^']*'." + f2(unresolved = »1«); + + // semantic_error "An argument of type '(Int) -> (Int)' cannot be assigned to a parameter of type 'MyEnum1'." + f3(»intToInt«); + // semantic_error "An argument of type '(C) -> ()' cannot be assigned to a parameter of type 'MyEnum1'." + f3(»f2«); + // semantic_error "An argument of type 'B' cannot be assigned to a parameter of type 'MyEnum1'." + f3(»B()«); + // semantic_error "An argument of type 'C' cannot be assigned to a parameter of type 'MyEnum1'." + f3(enumType = »C()«); + // semantic_error "An argument of type 'D' cannot be assigned to a parameter of type 'MyEnum1'." + f3(»D()«); + // semantic_error "An argument of type 'C?' cannot be assigned to a parameter of type 'MyEnum1'." + f3(»maybeC()«); + // no_semantic_error "An argument of type 'MyEnum1' cannot be assigned to a parameter of type 'MyEnum1'." + f3(»someVariantOfMyEnum1()«); + // semantic_error "An argument of type 'MyEnum2' cannot be assigned to a parameter of type 'MyEnum1'." + f3(»someVariantOfMyEnum2()«); + // no_semantic_error "An argument of type 'MyEnum1.Variant1' cannot be assigned to a parameter of type 'MyEnum1'." + f3(»MyEnum1.Variant1«); + // no_semantic_error "An argument of type 'MyEnum1.Variant2' cannot be assigned to a parameter of type 'MyEnum1'." + f3(»MyEnum1.Variant2«); + // semantic_error "An argument of type 'MyEnum2.Variant1' cannot be assigned to a parameter of type 'MyEnum1'." + f3(»MyEnum2.Variant1«); + // semantic_error "An argument of type 'union' cannot be assigned to a parameter of type 'MyEnum1'." + f3(»aOrC()«); + // semantic_error "An argument of type 'union' cannot be assigned to a parameter of type 'MyEnum1'." + f3(»bOrC()«); + // semantic_error "An argument of type 'vararg' cannot be assigned to a parameter of type 'MyEnum1'." + f3(»variadicParam«); + // no_semantic_error "An argument of type '$Unresolved' cannot be assigned to a parameter of type 'MyEnum1'." + f3(»unresolved«); + // no_semantic_error r"An argument of type '[^']*' cannot be assigned to a parameter of type '[^']*'." + f3(unresolved = »1«); + + // semantic_error "An argument of type '(Int) -> (Int)' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(»intToInt«); + // semantic_error "An argument of type '(C) -> ()' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(»f2«); + // semantic_error "An argument of type 'B' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(»B()«); + // semantic_error "An argument of type 'C' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(enumVariantType = »C()«); + // semantic_error "An argument of type 'D' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(»D()«); + // semantic_error "An argument of type 'C?' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(»maybeC()«); + // semantic_error "An argument of type 'MyEnum1' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(»someVariantOfMyEnum1()«); + // semantic_error "An argument of type 'MyEnum2' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(»someVariantOfMyEnum2()«); + // no_semantic_error "An argument of type 'MyEnum1.Variant1' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(»MyEnum1.Variant1«); + // semantic_error "An argument of type 'MyEnum1.Variant2' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(»MyEnum1.Variant2«); + // semantic_error "An argument of type 'MyEnum2.Variant1' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(»MyEnum2.Variant1«); + // semantic_error "An argument of type 'union' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(»aOrC()«); + // semantic_error "An argument of type 'union' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(»bOrC()«); + // semantic_error "An argument of type 'vararg' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(»variadicParam«); + // no_semantic_error "An argument of type '$Unresolved' cannot be assigned to a parameter of type 'MyEnum1.Variant1'." + f4(»unresolved«); + // no_semantic_error r"An argument of type '[^']*' cannot be assigned to a parameter of type '[^']*'." + f4(unresolved = »1«); + + // semantic_error "An argument of type '(Int) -> (Int)' cannot be assigned to a parameter of type 'union'." + f5(»intToInt«); + // semantic_error "An argument of type '(C) -> ()' cannot be assigned to a parameter of type 'union'." + f5(»f2«); + // no_semantic_error "An argument of type 'B' cannot be assigned to a parameter of type 'union'." + f5(»B()«); + // no_semantic_error "An argument of type 'C' cannot be assigned to a parameter of type 'union'." + f5(unionType = »C()«); + // no_semantic_error "An argument of type 'D' cannot be assigned to a parameter of type 'union'." + f5(»D()«); + // semantic_error "An argument of type 'C?' cannot be assigned to a parameter of type 'union'." + f5(»maybeC()«); + // semantic_error "An argument of type 'MyEnum1' cannot be assigned to a parameter of type 'union'." + f5(»someVariantOfMyEnum1()«); + // semantic_error "An argument of type 'MyEnum2' cannot be assigned to a parameter of type 'union'." + f5(»someVariantOfMyEnum2()«); + // semantic_error "An argument of type 'MyEnum1.Variant1' cannot be assigned to a parameter of type 'union'." + f5(»MyEnum1.Variant1«); + // semantic_error "An argument of type 'MyEnum1.Variant2' cannot be assigned to a parameter of type 'union'." + f5(»MyEnum1.Variant2«); + // semantic_error "An argument of type 'MyEnum2.Variant1' cannot be assigned to a parameter of type 'union'." + f5(»MyEnum2.Variant1«); + // semantic_error "An argument of type 'union' cannot be assigned to a parameter of type 'union'." + f5(»aOrC()«); + // no_semantic_error "An argument of type 'union' cannot be assigned to a parameter of type 'union'." + f5(»bOrC()«); + // semantic_error "An argument of type 'vararg' cannot be assigned to a parameter of type 'union'." + f5(»variadicParam«); + // no_semantic_error "An argument of type '$Unresolved' cannot be assigned to a parameter of type 'union'." + f5(»unresolved«); + // no_semantic_error r"An argument of type '[^']*' cannot be assigned to a parameter of type '[^']*'." + f5(unresolved = »1«); + + // semantic_error "An argument of type '(Int) -> (Int)' cannot be assigned to a parameter of type '$Unresolved'." + f6(»intToInt«); + // semantic_error "An argument of type '(C) -> ()' cannot be assigned to a parameter of type '$Unresolved'." + f6(»f2«); + // semantic_error "An argument of type 'B' cannot be assigned to a parameter of type '$Unresolved'." + f6(»B()«); + // semantic_error "An argument of type 'C' cannot be assigned to a parameter of type '$Unresolved'." + f6(unresolvedType = »C()«); + // semantic_error "An argument of type 'D' cannot be assigned to a parameter of type '$Unresolved'." + f6(»D()«); + // semantic_error "An argument of type 'C?' cannot be assigned to a parameter of type '$Unresolved'." + f6(»maybeC()«); + // semantic_error "An argument of type 'MyEnum1' cannot be assigned to a parameter of type '$Unresolved'." + f6(»someVariantOfMyEnum1()«); + // semantic_error "An argument of type 'MyEnum2' cannot be assigned to a parameter of type '$Unresolved'." + f6(»someVariantOfMyEnum2()«); + // semantic_error "An argument of type 'MyEnum1.Variant1' cannot be assigned to a parameter of type '$Unresolved'." + f6(»MyEnum1.Variant1«); + // semantic_error "An argument of type 'MyEnum1.Variant2' cannot be assigned to a parameter of type '$Unresolved'." + f6(»MyEnum1.Variant2«); + // semantic_error "An argument of type 'MyEnum2.Variant1' cannot be assigned to a parameter of type '$Unresolved'." + f6(»MyEnum2.Variant1«); + // semantic_error "An argument of type 'union' cannot be assigned to a parameter of type '$Unresolved'." + f6(»aOrC()«); + // semantic_error "An argument of type 'union' cannot be assigned to a parameter of type '$Unresolved'." + f6(»bOrC()«); + // semantic_error "An argument of type 'vararg' cannot be assigned to a parameter of type '$Unresolved'." + f6(»variadicParam«); + // no_semantic_error "An argument of type '$Unresolved' cannot be assigned to a parameter of type '$Unresolved'." + f6(»unresolved«); + // no_semantic_error r"An argument of type '[^']*' cannot be assigned to a parameter of type '[^']*'." + f6(unresolved = »1«); + + // semantic_error "An argument of type '(Int) -> (Int)' cannot be assigned to a parameter of type 'vararg'." + f7(»intToInt«); + // semantic_error "An argument of type '(C) -> ()' cannot be assigned to a parameter of type 'vararg'." + f7(»f2«); + // semantic_error "An argument of type 'B' cannot be assigned to a parameter of type 'vararg'." + f7(»B()«); + // no_semantic_error "An argument of type 'C' cannot be assigned to a parameter of type 'vararg'." + f7(unresolvedType = »C()«); + // no_semantic_error "An argument of type 'D' cannot be assigned to a parameter of type 'vararg'." + f7(»D()«); + // semantic_error "An argument of type 'C?' cannot be assigned to a parameter of type 'vararg'." + f7(»maybeC()«); + // semantic_error "An argument of type 'MyEnum1' cannot be assigned to a parameter of type 'vararg'." + f7(»someVariantOfMyEnum1()«); + // semantic_error "An argument of type 'MyEnum2' cannot be assigned to a parameter of type 'vararg'." + f7(»someVariantOfMyEnum2()«); + // semantic_error "An argument of type 'MyEnum1.Variant1' cannot be assigned to a parameter of type 'vararg'." + f7(»MyEnum1.Variant1«); + // semantic_error "An argument of type 'MyEnum1.Variant2' cannot be assigned to a parameter of type 'vararg'." + f7(»MyEnum1.Variant2«); + // semantic_error "An argument of type 'MyEnum2.Variant1' cannot be assigned to a parameter of type 'vararg'." + f7(»MyEnum2.Variant1«); + // semantic_error "An argument of type 'union' cannot be assigned to a parameter of type 'vararg'." + f7(»aOrC()«); + // semantic_error "An argument of type 'union' cannot be assigned to a parameter of type 'vararg'." + f7(»bOrC()«); + // semantic_error "An argument of type 'vararg' cannot be assigned to a parameter of type 'vararg'." + f7(»variadicParam«); + // no_semantic_error "An argument of type '$Unresolved' cannot be assigned to a parameter of type 'vararg'." + f7(»unresolved«); + // no_semantic_error r"An argument of type '[^']*' cannot be assigned to a parameter of type '[^']*'." + f7(unresolved = »1«); + + // no_semantic_error r"An argument of type '[^']*' cannot be assigned to a parameter of type '[^']*'." + f8(»(vararg a: Int) {}«); + + // no_semantic_error "An argument of type '(Int) -> (Int)' cannot be assigned to a parameter of type 'Any'." + f9(»intToInt«); + // no_semantic_error "An argument of type '(C) -> ()' cannot be assigned to a parameter of type 'Any'." + f9(»f2«); + // no_semantic_error "An argument of type 'B' cannot be assigned to a parameter of type 'Any'." + f9(»B()«); + // no_semantic_error "An argument of type 'C' cannot be assigned to a parameter of type 'Any'." + f9(callableType = »C()«); + // no_semantic_error "An argument of type 'D' cannot be assigned to a parameter of type 'Any'." + f9(»D()«); + // semantic_error "An argument of type 'C?' cannot be assigned to a parameter of type 'Any'." + f9(»maybeC()«); + // no_semantic_error "An argument of type 'MyEnum1' cannot be assigned to a parameter of type 'Any'." + f9(»someVariantOfMyEnum1()«); + // no_semantic_error "An argument of type 'MyEnum2' cannot be assigned to a parameter of type 'Any'." + f9(»someVariantOfMyEnum2()«); + // no_semantic_error "An argument of type 'MyEnum1.Variant1' cannot be assigned to a parameter of type 'Any'." + f9(»MyEnum1.Variant1«); + // no_semantic_error "An argument of type 'MyEnum1.Variant2' cannot be assigned to a parameter of type 'Any'." + f9(»MyEnum1.Variant2«); + // no_semantic_error "An argument of type 'MyEnum2.Variant1' cannot be assigned to a parameter of type 'Any'." + f9(»MyEnum2.Variant1«); + // no_semantic_error "An argument of type 'union' cannot be assigned to a parameter of type 'Any'." + f9(»aOrC()«); + // no_semantic_error "An argument of type 'union' cannot be assigned to a parameter of type 'Any'." + f9(»bOrC()«); + // no_semantic_error "An argument of type 'vararg' cannot be assigned to a parameter of type 'Any'." + f9(»variadicParam«); + // no_semantic_error "An argument of type '$Unresolved' cannot be assigned to a parameter of type 'Any'." + f9(»unresolved«); + // no_semantic_error r"An argument of type '[^']*' cannot be assigned to a parameter of type '[^']*'." + f9(unresolved = »1«); + + // no_semantic_error "An argument of type '(Int) -> (Int)' cannot be assigned to a parameter of type 'Any?'." + f10(»intToInt«); + // no_semantic_error "An argument of type '(C) -> ()' cannot be assigned to a parameter of type 'Any?'." + f10(»f2«); + // no_semantic_error "An argument of type 'B' cannot be assigned to a parameter of type 'Any?'." + f10(»B()«); + // no_semantic_error "An argument of type 'C' cannot be assigned to a parameter of type 'Any?'." + f10(callableType = »C()«); + // no_semantic_error "An argument of type 'D' cannot be assigned to a parameter of type 'Any?'." + f10(»D()«); + // no_semantic_error "An argument of type 'C?' cannot be assigned to a parameter of type 'Any?'." + f10(»maybeC()«); + // no_semantic_error "An argument of type 'MyEnum1' cannot be assigned to a parameter of type 'Any?'." + f10(»someVariantOfMyEnum1()«); + // no_semantic_error "An argument of type 'MyEnum2' cannot be assigned to a parameter of type 'Any?'." + f10(»someVariantOfMyEnum2()«); + // no_semantic_error "An argument of type 'MyEnum1.Variant1' cannot be assigned to a parameter of type 'Any?'." + f10(»MyEnum1.Variant1«); + // no_semantic_error "An argument of type 'MyEnum1.Variant2' cannot be assigned to a parameter of type 'Any?'." + f10(»MyEnum1.Variant2«); + // no_semantic_error "An argument of type 'MyEnum2.Variant1' cannot be assigned to a parameter of type 'Any?'." + f10(»MyEnum2.Variant1«); + // no_semantic_error "An argument of type 'union' cannot be assigned to a parameter of type 'Any?'." + f10(»aOrC()«); + // no_semantic_error "An argument of type 'union' cannot be assigned to a parameter of type 'Any?'." + f10(»bOrC()«); + // no_semantic_error "An argument of type 'vararg' cannot be assigned to a parameter of type 'Any?'." + f10(»variadicParam«); + // no_semantic_error "An argument of type '$Unresolved' cannot be assigned to a parameter of type 'Any?'." + f10(»unresolved«); + // no_semantic_error r"An argument of type '[^']*' cannot be assigned to a parameter of type '[^']*'." + f10(unresolved = »1«); +} + +fun f1(callableType: (a: Int) -> (r: Int)) +fun f2(classType: C) +fun f3(enumType: MyEnum1) +fun f4(enumVariantType: MyEnum1.Variant1) +fun f5(unionType: union) +fun f6(unresolvedType: Unresolved) +fun f7(vararg variadicType: C) +fun f8(callableType: (vararg a: Int) -> ()) +fun f9(any: Any) +fun f10(anyOrNull: Any?) + +class A() +class B() +class C() +class D() sub C + +enum MyEnum1 { + Variant1 + Variant2 +} +enum MyEnum2 { + Variant1 + Variant2 +} + +fun maybeC() -> instanceOrNull: C? +fun aOrC() -> instance: union +fun bOrC() -> instance: union +fun someVariantOfMyEnum1() -> variant: MyEnum1 +fun someVariantOfMyEnum2() -> variant: MyEnum2 +fun intToInt(a: Int) -> (r: Int) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/defaultValues.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/defaultValues.smltest new file mode 100644 index 000000000..9a077ab70 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/defaultValues.smltest @@ -0,0 +1,19 @@ +package tests.validation.typeChecking.defaultValues + +fun myFun( + // no_semantic_error "An default value of type 'Int' cannot be assigned to a parameter of type 'Int'." + param1: Int = »1«, + + // semantic_error "A default value of type 'String' cannot be assigned to a parameter of type 'Int'." + param2: Int = »""«, +) + +fun myOtherFun(callback: (a: Int) -> ()) + +step myStep() { + // no_semantic_error "An default value of type 'Int' cannot be assigned to a parameter of type 'Int'." + myOtherFun((a = »1«) {}); + + // semantic_error "A default value of type 'String' cannot be assigned to a parameter of type 'Int'." + myOtherFun((a = »""«) {}); +} \ No newline at end of file diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/indexedAccesses.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/indexedAccesses.smltest new file mode 100644 index 000000000..8eb576994 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/indexedAccesses.smltest @@ -0,0 +1,39 @@ +package tests.validation.typeChecking.indexedAccesses + +step f(a: Int, vararg b: Int) { + // semantic_error "The receiver of an indexed access must refer to a variadic parameter." + »a«[0]; + + // no_semantic_error "The receiver of an indexed access must refer to a variadic parameter." + »b«[0]; + + // no_semantic_error "The receiver of an indexed access must refer to a variadic parameter." + »unresolved«[0]; + + // no_semantic_error "The receiver of an indexed access must refer to a variadic parameter." + »C.unresolved«[0]; + + // no_semantic_error "The index of an indexed access must be an instance of the class 'Int'." + b[»0«]; + + // semantic_error "The index of an indexed access must be an instance of the class 'Int'." + b[»""«]; + + // semantic_error "The index of an indexed access must be an instance of the class 'Int'." + b[»g«]; + + // semantic_error "The index of an indexed access must be an instance of the class 'Int'." + b[»h()«]; + + // semantic_error "The index of an indexed access must be an instance of the class 'Int'." + b[»b«]; + + // no_semantic_error "The index of an indexed access must be an instance of the class 'Int'." + b[»unresolved«]; + + // no_semantic_error "The index of an indexed access must be an instance of the class 'Int'." + b[»C.unresolved«]; +} + +fun g() +fun h() -> index: Int? diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/infixOperations.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/infixOperations.smltest new file mode 100644 index 000000000..c0c2a0ef0 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/infixOperations.smltest @@ -0,0 +1,230 @@ +package tests.validation.typeChecking.infixOperations + +step f(vararg a: Int) { + + // no_semantic_error "The left operand of a logical infix operation must be an instance of the class 'Boolean'." + // no_semantic_error "The right operand of a logical infix operation must be an instance of the class 'Boolean'." + »true« or »true« ; + // no_semantic_error "The left operand of a logical infix operation must be an instance of the class 'Boolean'." + // no_semantic_error "The right operand of a logical infix operation must be an instance of the class 'Boolean'." + »false« or »false«; + // semantic_error "The left operand of a logical infix operation must be an instance of the class 'Boolean'." + // semantic_error "The right operand of a logical infix operation must be an instance of the class 'Boolean'." + »i()« or »i()«; + // semantic_error "The left operand of a logical infix operation must be an instance of the class 'Boolean'." + // semantic_error "The right operand of a logical infix operation must be an instance of the class 'Boolean'." + »0« or »0«; + // semantic_error "The left operand of a logical infix operation must be an instance of the class 'Boolean'." + // semantic_error "The right operand of a logical infix operation must be an instance of the class 'Boolean'." + »a« or »a«; + // no_semantic_error "The left operand of a logical infix operation must be an instance of the class 'Boolean'." + // no_semantic_error "The right operand of a logical infix operation must be an instance of the class 'Boolean'." + »unresolved« or »unresolved«; + // no_semantic_error "The left operand of a logical infix operation must be an instance of the class 'Boolean'." + // no_semantic_error "The right operand of a logical infix operation must be an instance of the class 'Boolean'." + »C.unresolved« or »C.unresolved«; + + // no_semantic_error "The left operand of a logical infix operation must be an instance of the class 'Boolean'." + // no_semantic_error "The right operand of a logical infix operation must be an instance of the class 'Boolean'." + »true« and »true«; + // no_semantic_error "The left operand of a logical infix operation must be an instance of the class 'Boolean'." + // no_semantic_error "The right operand of a logical infix operation must be an instance of the class 'Boolean'." + »false« and »false«; + // semantic_error "The left operand of a logical infix operation must be an instance of the class 'Boolean'." + // semantic_error "The right operand of a logical infix operation must be an instance of the class 'Boolean'." + »i()« and »i()«; + // semantic_error "The left operand of a logical infix operation must be an instance of the class 'Boolean'." + // semantic_error "The right operand of a logical infix operation must be an instance of the class 'Boolean'." + »0« and »0«; + // semantic_error "The left operand of a logical infix operation must be an instance of the class 'Boolean'." + // semantic_error "The right operand of a logical infix operation must be an instance of the class 'Boolean'." + »a« and »a«; + // no_semantic_error "The left operand of a logical infix operation must be an instance of the class 'Boolean'." + // no_semantic_error "The right operand of a logical infix operation must be an instance of the class 'Boolean'." + »unresolved« and »unresolved«; + // no_semantic_error "The left operand of a logical infix operation must be an instance of the class 'Boolean'." + // no_semantic_error "The right operand of a logical infix operation must be an instance of the class 'Boolean'." + »C.unresolved« and »C.unresolved«; + + + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »0.0« + »0.0«; + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »0« + »0«; + // semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »h()« + »h()«; + // semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »""« + »""«; + // semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »a« + »a«; + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »unresolved« + »unresolved«; + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »C.unresolved« + »C.unresolved«; + + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »0.0« - »0.0«; + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »0« - »0«; + // semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »h()« - »h()«; + // semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »""« - »""«; + // semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »a« - »a«; + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »unresolved« - »unresolved«; + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »C.unresolved« - »C.unresolved«; + + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »0.0« * »0.0«; + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »0« * »0«; + // semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »h()« * »h()«; + // semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »""« * »""«; + // semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »a« * »a«; + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »unresolved« * »unresolved«; + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »C.unresolved« * »C.unresolved«; + + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »0.0« / »0.0«; + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »0« / »0«; + // semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »h()« / »h()«; + // semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »""« / »""«; + // semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »a« / »a«; + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »unresolved« / »unresolved«; + // no_semantic_error "The left operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of an arithmetic infix operation must be an instance of the class 'Float' or the class 'Int'." + »C.unresolved« / »C.unresolved«; + + + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »0.0« < »0.0«; + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »0« < »0«; + // semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »h()« < »h()«; + // semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »""« < »""«; + // semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »a« < »a«; + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »unresolved« < »unresolved«; + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »C.unresolved« < »C.unresolved«; + + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »0.0« <= »0.0«; + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »0« <= »0«; + // semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »h()« <= »h()«; + // semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »""« <= »""«; + // semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »a« <= »a«; + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »unresolved« <= »unresolved«; + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »C.unresolved« <= »C.unresolved«; + + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »0.0« >= »0.0«; + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »0« >= »0«; + // semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »h()« >= »h()«; + // semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »""« >= »""«; + // semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »a« >= »a«; + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »unresolved« >= »unresolved«; + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »C.unresolved« >= »C.unresolved«; + + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »0.0« > »0.0«; + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »0« > »0«; + // semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »h()« > »h()«; + // semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »""« > »""«; + // semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »a« > »a«; + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »unresolved« > »unresolved«; + // no_semantic_error "The left operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + // no_semantic_error "The right operand of a comparison must be an instance of the class 'Float' or the class 'Int'." + »C.unresolved« > »C.unresolved«; +} + +fun g() +fun h() -> index: Int? +fun i() -> isTrue: Boolean? diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/prefixOperations.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/prefixOperations.smltest new file mode 100644 index 000000000..e3ed3b126 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/prefixOperations.smltest @@ -0,0 +1,38 @@ +package tests.validation.typeChecking.prefixOperations + +step f(vararg a: Int) { + + // no_semantic_error "The operand of a logical negation must be an instance of the class 'Boolean'." + not »true«; + // no_semantic_error "The operand of a logical negation must be an instance of the class 'Boolean'." + not »false«; + // semantic_error "The operand of a logical negation must be an instance of the class 'Boolean'." + not »i()«; + // semantic_error "The operand of a logical negation must be an instance of the class 'Boolean'." + not »0«; + // semantic_error "The operand of a logical negation must be an instance of the class 'Boolean'." + not »a«; + // no_semantic_error "The operand of a logical negation must be an instance of the class 'Boolean'." + not »unresolved«; + // no_semantic_error "The operand of a logical negation must be an instance of the class 'Boolean'." + not »C.unresolved«; + + // no_semantic_error "The operand of an arithmetic negation must be an instance of the class 'Float' or the class 'Int'." + -»0.0«; + // no_semantic_error "The operand of an arithmetic negation must be an instance of the class 'Float' or the class 'Int'." + -»0«; + // semantic_error "The operand of an arithmetic negation must be an instance of the class 'Float' or the class 'Int'." + -»h()«; + // semantic_error "The operand of an arithmetic negation must be an instance of the class 'Float' or the class 'Int'." + -»""«; + // semantic_error "The operand of an arithmetic negation must be an instance of the class 'Float' or the class 'Int'." + -»a«; + // no_semantic_error "The operand of an arithmetic negation must be an instance of the class 'Float' or the class 'Int'." + -»unresolved«; + // no_semantic_error "The operand of an arithmetic negation must be an instance of the class 'Float' or the class 'Int'." + -»C.unresolved«; +} + +fun g() +fun h() -> index: Int? +fun i() -> isTrue: Boolean? diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/yields.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/yields.smltest new file mode 100644 index 000000000..999ce85cd --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/typeChecking/yields.smltest @@ -0,0 +1,13 @@ +package tests.validation.typeChecking.yields + +step myStep1() -> result: Int { + + // no_semantic_error "A value of type 'Int' cannot be assigned to a result of type 'Int'." + yield result = »1«; +} + +step myStep2() -> result: Int { + + // semantic_error "A value of type 'String' cannot be assigned to a result of type 'Int'." + yield result = »""«; +} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/types/callableTypes/no optional parameters.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/types/callableTypes/no optional parameters.smltest new file mode 100644 index 000000000..b5a8553cb --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/types/callableTypes/no optional parameters.smltest @@ -0,0 +1,4 @@ +package tests.validation.types.callableTypes.noOptionalParameters + +// semantic_error "Parameters in callable types must not be optional." +fun f(g: (param: Int = »1«) -> ()) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/types/callableTypes/uniqueNames.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/types/callableTypes/uniqueNames.smltest new file mode 100644 index 000000000..0a26eb77f --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/types/callableTypes/uniqueNames.smltest @@ -0,0 +1,23 @@ +package tests.uniqueNames + +step s( + f: ( + // semantic_error "A parameter or result with name 'duplicateParameter' exists already in this callable type." + »duplicateParameter«: Int, + // semantic_error "A parameter or result with name 'duplicateParameter' exists already in this callable type." + »duplicateParameter«: Int, + // no_semantic_error "A parameter or result with name 'uniqueParameter' exists already in this callable type." + »uniqueParameter«: Int, + // semantic_error "A parameter or result with name 'parameterAndResult' exists already in this callable type." + »parameterAndResult«: Int + ) -> ( + // semantic_error "A parameter or result with name 'duplicateResult' exists already in this callable type." + »duplicateResult«: Int, + // semantic_error "A parameter or result with name 'duplicateResult' exists already in this callable type." + »duplicateResult«: Int, + // no_semantic_error "A parameter or result with name 'uniqueResult' exists already in this callable type." + »uniqueResult«: Int, + // semantic_error "A parameter or result with name 'parameterAndResult' exists already in this callable type." + »parameterAndResult«: Int + ) +) {} diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/types/namedTypes/missingTypeArgumentList.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/types/namedTypes/missingTypeArgumentList.smltest new file mode 100644 index 000000000..2dfd9c30c --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/types/namedTypes/missingTypeArgumentList.smltest @@ -0,0 +1,19 @@ +package tests.validation.types.namedTypes.missingTypeArgumentList + +class ClassWithoutTypeParameters +class ClassWithTypeParameters + +fun myFunction( + // no_semantic_error "Missing type argument list." + a: »ClassWithoutTypeParameters«, + // no_semantic_error "Missing type argument list." + b: »ClassWithoutTypeParameters«, + // semantic_error "Missing type argument list." + c: »ClassWithTypeParameters«, + // semantic_error "Missing type argument list." + d: »ClassWithTypeParameters«, + // no_semantic_error "Missing type argument list." + e: »UnresolvedClass«, + // no_semantic_error "Missing type argument list." + f: »UnresolvedClass«, +) diff --git a/DSL/de.unibonn.simpleml/src/test/resources/validation/types/unionTypes/numberOfTypeArguments.smltest b/DSL/de.unibonn.simpleml/src/test/resources/validation/types/unionTypes/numberOfTypeArguments.smltest new file mode 100644 index 000000000..4f684a4d1 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/test/resources/validation/types/unionTypes/numberOfTypeArguments.smltest @@ -0,0 +1,21 @@ +package tests.numberOfTypeArguments + +// semantic_error "A union type must have least one type argument." +step myStep1( + f: »union<>« +) {} + +// semantic_info "A union type with one type argument is equivalent to the the type argument itself." +step myStep1( + f: »union« +) {} + +// no_semantic_error "A union type must have least one type argument." +step myStep1( + f: »union« +) {} + +// no_semantic_info "A union type with one type argument is equivalent to the the type argument itself." +step myStep1( + f: »union« +) {} diff --git a/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/LineBreaks.kt b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/LineBreaks.kt new file mode 100644 index 000000000..55c3ab8fe --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/LineBreaks.kt @@ -0,0 +1,11 @@ +package de.unibonn.simpleml.testing + +/** + * Replaces line-breaks in the string with the ones used by the operating system (\n on Unix, \r on MacOS, \r\n on + * Windows). + */ +fun String.withSystemLineBreaks(): String { + return this + .replace(Regex("\r\n?"), "\n") + .replace("\n", System.lineSeparator()) +} diff --git a/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/ParseHelper.kt b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/ParseHelper.kt new file mode 100644 index 000000000..269beab7e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/ParseHelper.kt @@ -0,0 +1,113 @@ +@file:Suppress("unused", "MemberVisibilityCanBePrivate") + +package de.unibonn.simpleml.testing + +import com.google.inject.Inject +import com.google.inject.Provider +import de.unibonn.simpleml.simpleML.SmlCompilationUnit +import de.unibonn.simpleml.stdlibAccess.loadStdlib +import org.eclipse.core.runtime.FileLocator +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.emf.ecore.resource.ResourceSet +import org.eclipse.xtext.testing.util.ParseHelper +import java.nio.file.Files +import java.nio.file.Paths + +/** + * Relative path under the `resources` folder of the test source set. + */ +typealias ResourceName = String + +/** + * Utilities for tests that load their test data from files or strings. + */ +class ParseHelper @Inject constructor( + private val parseHelper: ParseHelper, + private val resourceSetProvider: Provider +) { + + /** + * Parses the contents of the resource with the given [resourceName] and returns the contained [SmlCompilationUnit] + * or `null` if something went wrong. Any resources in the [context] are included in the same [ResourceSet]. + * Likewise, if [loadStdlib] is `true`, the standard library is also included in the [ResourceSet]. + */ + fun parseResource( + resourceName: ResourceName, + context: List = emptyList(), + loadStdlib: Boolean = true + ): SmlCompilationUnit? { + + val programText = readProgramTextFromResource(resourceName) ?: return null + val uri = javaClass.classLoader.getResourceEmfUri(resourceName) ?: return null + return parseProgramText(programText, uri, context, loadStdlib) + } + + /** + * Parses the given [programText] and returns the contained [SmlCompilationUnit] or `null` if something went wrong. + * Any resources in the [context] are included in the same [ResourceSet]. Likewise, if [loadStdlib] is `true`, the + * standard library is also included in the [ResourceSet]. + */ + fun parseProgramText( + programText: String, + context: List = emptyList(), + loadStdlib: Boolean = true + ): SmlCompilationUnit? { + + val resourceSet = createResourceSetFromContext(context) + if (loadStdlib) { + resourceSet.loadStdlib() + } + return parseHelper.parse(programText, resourceSet) + } + + /** + * Parses the given [programText] and returns the contained [SmlCompilationUnit] or `null` if something went wrong. + * The [URI] of the [Resource] that contains the created [SmlCompilationUnit] is set to [uriToUse]. Any resources in + * the [context] are included in the same [ResourceSet]. Likewise, if [loadStdlib] is `true`, the standard library + * is also included in the [ResourceSet]. + */ + fun parseProgramText( + programText: String, + uriToUse: URI, + context: List = emptyList(), + loadStdlib: Boolean = true + ): SmlCompilationUnit? { + + val resourceSet = createResourceSetFromContext(context) + if (loadStdlib) { + resourceSet.loadStdlib() + } + return parseHelper.parse(programText, uriToUse, resourceSet) + } + + /** + * Returns the program text within the resource with the given [resourceName] or `null` if reading failed. + */ + private fun readProgramTextFromResource(resourceName: ResourceName): String? { + val resourcePath = javaClass.classLoader.getResourcePath(resourceName) ?: return null + if (!Files.isReadable(resourcePath)) { + return null + } + + return Files.readString(resourcePath).replace("\r\n", "\n") + } + + /** + * Creates a [ResourceSet] containing all resources in the [context]. + */ + private fun createResourceSetFromContext(context: List): ResourceSet { + val result = resourceSetProvider.get() + for (resourceName in context) { + val resourceUrl = javaClass.classLoader.getResource(resourceName) ?: continue + val resourceFileUri = FileLocator.resolve(resourceUrl).toURI() + val resourceEmfUri = URI.createURI(resourceFileUri.toString(), false) + val resourcePath = Paths.get(resourceFileUri) + + result + .createResource(resourceEmfUri) + ?.load(Files.newInputStream(resourcePath), result.loadOptions) + } + return result + } +} diff --git a/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/SimpleMLInjectorProvider.kt b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/SimpleMLInjectorProvider.kt new file mode 100644 index 000000000..0fdf21987 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/SimpleMLInjectorProvider.kt @@ -0,0 +1,62 @@ +package de.unibonn.simpleml.testing + +import com.google.inject.Guice +import com.google.inject.Injector +import de.unibonn.simpleml.SimpleMLRuntimeModule +import de.unibonn.simpleml.SimpleMLStandaloneSetup +import org.eclipse.xtext.testing.GlobalRegistries.GlobalStateMemento +import org.eclipse.xtext.testing.GlobalRegistries.initializeDefaults +import org.eclipse.xtext.testing.GlobalRegistries.makeCopyOfGlobalState +import org.eclipse.xtext.testing.IInjectorProvider +import org.eclipse.xtext.testing.IRegistryConfigurator + +class SimpleMLInjectorProvider : IInjectorProvider, IRegistryConfigurator { + private var stateBeforeInjectorCreation: GlobalStateMemento? = null + private var stateAfterInjectorCreation: GlobalStateMemento? = null + private var injector: Injector? = null + + override fun getInjector(): Injector { + if (injector == null) { + injector = internalCreateInjector() + stateAfterInjectorCreation = makeCopyOfGlobalState() + } + return injector!! + } + + private fun internalCreateInjector(): Injector { + return object : SimpleMLStandaloneSetup() { + override fun createInjector(): Injector { + return Guice.createInjector(createRuntimeModule()) + } + }.createInjectorAndDoEMFRegistration() + } + + private fun createRuntimeModule(): SimpleMLRuntimeModule { + // make it work also with Maven/Tycho and OSGI + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=493672 + return object : SimpleMLRuntimeModule() { + override fun bindClassLoaderToInstance(): ClassLoader { + return SimpleMLInjectorProvider::class.java.classLoader + } + } + } + + override fun restoreRegistry() { + stateBeforeInjectorCreation!!.restoreGlobalState() + stateBeforeInjectorCreation = null + } + + override fun setupRegistry() { + stateBeforeInjectorCreation = makeCopyOfGlobalState() + if (injector == null) { + getInjector() + } + stateAfterInjectorCreation!!.restoreGlobalState() + } + + companion object { + init { + initializeDefaults() + } + } +} diff --git a/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/TestMarker.kt b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/TestMarker.kt new file mode 100644 index 000000000..f5d6e9b2e --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/TestMarker.kt @@ -0,0 +1,17 @@ +package de.unibonn.simpleml.testing + +/** + * Describe a program range in a test file from the opening to the closing test marker. + */ +object TestMarker { + + /** + * Start of the test marker. + */ + const val OPEN = '\u00BB' // » + + /** + * End of the test marker. + */ + const val CLOSE = '\u00AB' // « +} diff --git a/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/TestRanges.kt b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/TestRanges.kt new file mode 100644 index 000000000..f1cea0b78 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/TestRanges.kt @@ -0,0 +1,140 @@ +package de.unibonn.simpleml.testing + +import de.unibonn.simpleml.location.XtextPosition +import de.unibonn.simpleml.location.XtextRange +import de.unibonn.simpleml.testing.FindTestRangesResult.CloseWithoutOpenFailure +import de.unibonn.simpleml.testing.FindTestRangesResult.OpenWithoutCloseFailure +import de.unibonn.simpleml.testing.FindTestRangesResult.Success +import de.unibonn.simpleml.testing.TestMarker.CLOSE +import de.unibonn.simpleml.testing.TestMarker.OPEN + +/** + * Finds test ranges, i.e. parts of the program delimited by opening and closing test markers. They are sorted by the + * position of their opening test markers. In case opening and closing markers don't match an error value is returned. + * Nested test markers are supported. + * + * @param program The program with test markers. + * @return A wrapper that indicates success of failure. + * @see FindTestRangesResult + * @see TestMarker + */ +fun findTestRanges(program: String): FindTestRangesResult { + var currentLine = 1 + var currentColumn = 1 + var previousChar: Char? = null + + val testRangeStarts = ArrayDeque() + val finishedLocations = mutableListOf() + + program.toCharArray().forEachIndexed { currentIndex, currentChar -> + when (currentChar) { + OPEN -> { + currentColumn++ + + testRangeStarts.addLast( + TestRangeStart(currentLine, currentColumn, currentIndex) + ) + } + CLOSE -> { + currentColumn++ + + if (testRangeStarts.isEmpty()) { + return CloseWithoutOpenFailure( + XtextPosition.fromInts( + line = currentLine, + column = currentColumn - 1 + ) + ) + } + + finishedLocations += testRangeStarts.removeLast().toProgramRange( + endLine = currentLine, + endColumn = currentColumn - 1, + endIndex = currentIndex - 1 + ) + } + '\r' -> { + currentLine++ + currentColumn = 1 + } + '\n' -> { + if (previousChar != '\r') { + currentLine++ + currentColumn = 1 + } + } + else -> { + currentColumn++ + } + } + + previousChar = currentChar + } + + return when { + testRangeStarts.isEmpty() -> Success(finishedLocations.sortedBy { it.start }) + else -> OpenWithoutCloseFailure( + testRangeStarts.map { + XtextPosition.fromInts(it.startLine, it.startColumn - 1) + } + ) + } +} + +/** + * A wrapper that indicates success of failure of the `findTestRanges` method. + */ +@Suppress("MemberVisibilityCanBePrivate") +sealed interface FindTestRangesResult { + + /** + * Opening and closing test markers matched and program ranges were successfully created. + */ + class Success(val ranges: List) : FindTestRangesResult + + /** + * Something went wrong when creating program ranges. + */ + sealed interface Failure : FindTestRangesResult { + + /** + * A human-readable description of what went wrong. + */ + val message: String + } + + /** + * Found a closing test marker without a previous opening test marker. + */ + class CloseWithoutOpenFailure(val position: XtextPosition) : Failure { + override val message: String + get() { + return "Found '$CLOSE' without previous '$OPEN' at $position." + } + } + + /** + * Reached the end of the program but there were still unclosed opening test markers. + */ + class OpenWithoutCloseFailure(val positions: List) : Failure { + override val message: String + get() { + return "Found '$OPEN' without following '$CLOSE' at ${positions.joinToString()}." + } + } +} + +/** + * Stores where a test range starts. + */ +private class TestRangeStart(val startLine: Int, val startColumn: Int, val startIndex: Int) { + fun toProgramRange(endLine: Int, endColumn: Int, endIndex: Int): XtextRange { + return XtextRange.fromInts( + startLine, + startColumn, + endLine, + endColumn, + length = endIndex - startIndex + ) + } +} diff --git a/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/TestResourceUtils.kt b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/TestResourceUtils.kt new file mode 100644 index 000000000..6bfe11c6b --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/TestResourceUtils.kt @@ -0,0 +1,86 @@ +package de.unibonn.simpleml.testing + +import de.unibonn.simpleml.constant.SmlFileExtension +import org.eclipse.core.runtime.FileLocator +import org.eclipse.emf.common.util.URI +import org.junit.jupiter.api.DynamicContainer +import org.junit.jupiter.api.DynamicNode +import org.junit.jupiter.api.DynamicTest +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.util.stream.Stream +import kotlin.streams.asSequence + +fun ClassLoader.getResourcePath(fileOrFolder: String): Path? { + val resourceUrl = getResource(fileOrFolder) ?: return null + val resourceFileUri = FileLocator.resolve(resourceUrl).toURI() + return Paths.get(resourceFileUri) +} + +fun ClassLoader.getResourceEmfUri(fileOrFolder: String): URI? { + val resourceUrl = getResource(fileOrFolder) ?: return null + val resourceFileUri = FileLocator.resolve(resourceUrl).toURI() + return URI.createURI(resourceFileUri.toString(), false) +} + +fun Path.createDynamicTestsFromResourceFolder( + validator: (resourcePath: Path, filePath: Path, program: String) -> String?, + categorizedTestCreator: (resourcePath: Path, filePath: Path, program: String) -> Sequence +): Stream { + return Files.walk(this) + .asSequence() + .filter(::isTestFile) + .flatMap { filePath -> createDynamicTestFromResource(this, filePath, validator, categorizedTestCreator) } + .groupBy { it.category } + .map { (category, tests) -> + DynamicContainer.dynamicContainer(category, tests.map { it.test }) + } + .stream() +} + +private fun createDynamicTestFromResource( + resourcePath: Path, + filePath: Path, + validator: (resourcePath: Path, filePath: Path, program: String) -> String?, + categorizedTestCreator: (resourcePath: Path, filePath: Path, program: String) -> Sequence +) = sequence { + val program = Files.readString(filePath) + + val testFileError = validator(resourcePath, filePath, program) + if (testFileError != null) { + yield( + CategorizedTest( + "### BAD TEST FILE ###", + DynamicTest.dynamicTest(testDisplayName(resourcePath, filePath), filePath.toUri()) { + throw IllegalArgumentException(testFileError) + } + ) + ) + } else { + yieldAll(categorizedTestCreator(resourcePath, filePath, program)) + } +} + +private fun isTestFile(filePath: Path): Boolean { + return Files.isRegularFile(filePath) && + ( + filePath.fileName.toString().endsWith(".${SmlFileExtension.Flow}") || + filePath.fileName.toString().endsWith(".${SmlFileExtension.Stub}") || + filePath.fileName.toString().endsWith(".${SmlFileExtension.Test}") + ) && + !filePath.fileName.toString().startsWith("_skip_") +} + +fun testDisplayName(resourcePath: Path, filePath: Path, message: String = "") = buildString { + append("[") + val relativePath = resourcePath.relativize(filePath) + append(relativePath.toString().replace("\\", "/")) + append("]") + + if (message.isNotBlank()) { + append(" \"$message\"") + } +} + +class CategorizedTest(val category: String, val test: DynamicNode) diff --git a/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/assertions/EmfAssertions.kt b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/assertions/EmfAssertions.kt new file mode 100644 index 000000000..0a2aa5acf --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/assertions/EmfAssertions.kt @@ -0,0 +1,46 @@ +package de.unibonn.simpleml.testing.assertions + +import de.unibonn.simpleml.emf.descendants +import de.unibonn.simpleml.simpleML.SmlAbstractDeclaration +import org.eclipse.emf.ecore.EObject + +/** + * Find a unique declaration among the descendants of the receiver with the given type and name. + * + * @receiver Root of the subtree within the EMF model that should be searched. + * @param name The name the declaration needs to have. + * @return The unique declaration if it exists. + * @throws AssertionError If no unique declaration exists. + */ +inline fun EObject.findUniqueDeclarationOrFail(name: String): T { + shouldHaveUniqueDeclaration(name) + return this.descendants().find { it.name == name }!! +} + +/** + * Assert that a unique declaration exists among the descendants of the receiver with the given type and name. + * + * @receiver Root of the subtree within the EMF model that should be searched. + * @param name The name the declaration needs to have. + */ +inline fun EObject.shouldHaveUniqueDeclaration(name: String) { + val candidates = this.descendants().filter { it.name == name }.toList() + + if (candidates.isEmpty()) { + throw AssertionError("Expected a unique matching fact of type ${T::class.simpleName} but found none.") + } else if (candidates.size > 1) { + throw AssertionError("Expected a unique matching fact but found ${candidates.size}: $candidates") + } +} + +fun SmlAbstractDeclaration.shouldBeResolved() { + if (this.eIsProxy()) { + throw AssertionError("Expected cross-reference to be resolved but it wasn't.") + } +} + +fun SmlAbstractDeclaration.shouldNotBeResolved() { + if (!this.eIsProxy()) { + throw AssertionError("Expected cross-reference to be unresolved but it wasn't.") + } +} diff --git a/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/assertions/FactbaseAssertions.kt b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/assertions/FactbaseAssertions.kt new file mode 100644 index 000000000..a9be20b62 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/assertions/FactbaseAssertions.kt @@ -0,0 +1,126 @@ +package de.unibonn.simpleml.testing.assertions + +import de.unibonn.simpleml.prologBridge.model.facts.AnnotationCallT +import de.unibonn.simpleml.prologBridge.model.facts.DeclarationT +import de.unibonn.simpleml.prologBridge.model.facts.ExpressionT +import de.unibonn.simpleml.prologBridge.model.facts.Node +import de.unibonn.simpleml.prologBridge.model.facts.NodeWithParent +import de.unibonn.simpleml.prologBridge.model.facts.PlFact +import de.unibonn.simpleml.prologBridge.model.facts.PlFactbase +import de.unibonn.simpleml.prologBridge.model.facts.ProtocolTermT +import de.unibonn.simpleml.simpleML.SmlAbstractExpression +import de.unibonn.simpleml.simpleML.SmlAbstractProtocolTerm +import de.unibonn.simpleml.utils.Id +import io.kotest.assertions.asClue +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.ints.shouldBeGreaterThan +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import org.eclipse.emf.ecore.EObject + +inline fun PlFactbase.findUniqueFactOrFail(filter: (T) -> Boolean = { true }): T { + shouldHaveUniqueFact(filter) + return findUniqueFact(filter)!! +} + +inline fun PlFactbase.shouldHaveUniqueFact(filter: (T) -> Boolean) { + val candidates = findFacts(filter) + if (candidates.isEmpty()) { + throw AssertionError("Expected a unique matching fact of type ${T::class.simpleName} but found none.") + } else if (candidates.size > 1) { + throw AssertionError("Expected a unique matching fact but found ${candidates.size}: $candidates") + } +} + +inline fun PlFactbase.shouldBeChildOf(childId: Id?, parent: Node) { + childId.shouldNotBeNull() + + val child = findUniqueFactOrFail { it.id == childId } + child.asClue { + child.id.value shouldBeGreaterThan parent.id.value + child.parent shouldBe parent.id + } +} + +inline fun PlFactbase.shouldBeNChildrenOf( + childIds: List>?, + parent: Node, + n: Int +) { + childIds.shouldNotBeNull() + childIds shouldHaveSize n + childIds.forEach { + shouldBeChildOf(it, parent) + } +} + +inline fun PlFactbase.shouldBeChildExpressionOf( + childId: Id?, + parent: Node +) { + childId.shouldNotBeNull() + + val child = findUniqueFactOrFail { it.id == childId } + val expectedEnclosing = when (parent) { + is ExpressionT -> parent.enclosing + else -> parent.id + } + + child.asClue { + child.id.value shouldBeGreaterThan parent.id.value + child.parent shouldBe parent.id + child.enclosing shouldBe expectedEnclosing + } +} + +inline fun PlFactbase.shouldBeNChildExpressionsOf( + childIds: List>?, + parent: Node, + n: Int +) { + childIds.shouldNotBeNull() + childIds shouldHaveSize n + childIds.forEach { + shouldBeChildExpressionOf(it, parent) + } +} + +inline fun PlFactbase.shouldBeChildProtocolTermOf( + childId: Id?, + parent: Node +) { + childId.shouldNotBeNull() + + val child = findUniqueFactOrFail { it.id == childId } + val expectedEnclosing = when (parent) { + is ProtocolTermT -> parent.enclosing + else -> parent.id + } + + child.asClue { + child.id.value shouldBeGreaterThan parent.id.value + child.parent shouldBe parent.id + child.enclosing shouldBe expectedEnclosing + } +} + +inline fun PlFactbase.shouldBeNChildProtocolTermsOf( + childIds: List>?, + parent: Node, + n: Int +) { + childIds.shouldNotBeNull() + childIds shouldHaveSize n + childIds.forEach { + shouldBeChildProtocolTermOf(it, parent) + } +} + +fun PlFactbase.shouldHaveNAnnotationCalls( + declaration: DeclarationT, + n: Int, +) { + val annotationUses = findFacts { it.parent == declaration.id } + annotationUses shouldHaveSize n + annotationUses.forEach { shouldBeChildOf(it.id, declaration) } +} diff --git a/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/assertions/NumberAssertions.kt b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/assertions/NumberAssertions.kt new file mode 100644 index 000000000..9e5cf4d2a --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/assertions/NumberAssertions.kt @@ -0,0 +1,8 @@ +package de.unibonn.simpleml.testing.assertions + +import io.kotest.matchers.doubles.plusOrMinus +import io.kotest.matchers.shouldBe + +infix fun Double.shouldBeCloseTo(n: Double) { + this.shouldBe(n plusOrMinus Math.ulp(n)) +} diff --git a/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/assertions/ParsingAssertions.kt b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/assertions/ParsingAssertions.kt new file mode 100644 index 000000000..c65ee9593 --- /dev/null +++ b/DSL/de.unibonn.simpleml/src/testFixtures/kotlin/de/unibonn/simpleml/testing/assertions/ParsingAssertions.kt @@ -0,0 +1,132 @@ +package de.unibonn.simpleml.testing.assertions + +import de.unibonn.simpleml.location.XtextRange +import org.eclipse.xtext.diagnostics.Severity +import org.eclipse.xtext.validation.Issue + +fun List.shouldHaveNoErrorsOrWarnings() { + val errorsOrWarnings = this.filter { it.severity == Severity.ERROR || it.severity == Severity.WARNING } + if (errorsOrWarnings.isNotEmpty()) { + throw AssertionError("Expected no errors or warnings but got${errorsOrWarnings.stringify()}") + } +} + +fun List.shouldHaveSyntaxError(expected: ExpectedIssue) { + val syntaxErrors = this.filter { it.isSyntaxError } + if (syntaxErrors.none { expected.matches(it) }) { + throw AssertionError("Expected $expected but got${this.stringify()}") + } +} + +fun List.shouldHaveNoSyntaxError(expected: ExpectedIssue) { + val syntaxErrors = this.filter { it.isSyntaxError } + if (syntaxErrors.any { expected.matches(it) }) { + throw AssertionError("Expected $expected but got${this.stringify()}") + } +} + +fun List.shouldHaveSemanticError(expected: ExpectedIssue) { + val errors = this.filter { it.severity == Severity.ERROR } + if (errors.none { expected.matches(it) }) { + throw AssertionError("Expected $expected but got${this.stringify()}") + } +} + +fun List.shouldHaveNoSemanticError(expected: ExpectedIssue) { + val errors = this.filter { it.severity == Severity.ERROR } + if (errors.any { expected.matches(it) }) { + throw AssertionError("Expected $expected but got${this.stringify()}") + } +} + +fun List.shouldHaveSemanticWarning(expected: ExpectedIssue) { + val warnings = this.filter { it.severity == Severity.WARNING } + if (warnings.none { expected.matches(it) }) { + throw AssertionError("Expected $expected but got${this.stringify()}") + } +} + +fun List.shouldHaveNoSemanticWarning(expected: ExpectedIssue) { + val warnings = this.filter { it.severity == Severity.WARNING } + if (warnings.any { expected.matches(it) }) { + throw AssertionError("Expected $expected but got${this.stringify()}") + } +} + +fun List.shouldHaveSemanticInfo(expected: ExpectedIssue) { + val infos = this.filter { it.severity == Severity.INFO } + if (infos.none { expected.matches(it) }) { + throw AssertionError("Expected $expected but got${this.stringify()}") + } +} + +fun List.shouldHaveNoSemanticInfo(expected: ExpectedIssue) { + val infos = this.filter { it.severity == Severity.INFO } + if (infos.any { expected.matches(it) }) { + throw AssertionError("Expected $expected but got${this.stringify()}") + } +} + +fun List.shouldHaveNoIssue(expected: ExpectedIssue) { + if (this.isNotEmpty()) { + throw AssertionError("Expected $expected but got${this.stringify()}") + } +} + +fun List.stringify(): String { + if (this.isEmpty()) { + return " nothing." + } + + return this.joinToString(prefix = ":\n", separator = "\n") { " * $it" } +} + +class ExpectedIssue( + val severity: String, + val message: String, + val messageIsRegex: Boolean, + private val range: XtextRange? +) { + + fun matches(issue: Issue): Boolean { + return locationMatches(issue) && messageMatches(issue) + } + + private fun locationMatches(issue: Issue): Boolean { + return range == null || range == issue.range + } + + private fun messageMatches(issue: Issue): Boolean { + return when { + message.isBlank() -> true + !messageIsRegex -> message == issue.message + else -> { + val regex = Regex(message) + regex.matches(issue.message) + } + } + } + + private val Issue.range: XtextRange + get() = XtextRange.fromInts( + this.lineNumber, + this.column, + this.lineNumberEnd, + this.columnEnd, + this.length + ) + + override fun toString() = buildString { + append(severity) + if (messageIsRegex || message.isNotBlank()) { + append(" ") + } + if (messageIsRegex) { + append("r") + } + if (message.isNotBlank()) { + append("\"$message\"") + } + range?.let { append(" at $range") } + } +} diff --git a/DSL/gradle.properties b/DSL/gradle.properties new file mode 100644 index 000000000..da6ddc4f7 --- /dev/null +++ b/DSL/gradle.properties @@ -0,0 +1 @@ +org.gradle.caching = true \ No newline at end of file diff --git a/DSL/gradle/wrapper/gradle-wrapper.jar b/DSL/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..5c2d1cf01 Binary files /dev/null and b/DSL/gradle/wrapper/gradle-wrapper.jar differ diff --git a/DSL/gradle/wrapper/gradle-wrapper.properties b/DSL/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..aa991fcea --- /dev/null +++ b/DSL/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/DSL/gradlew b/DSL/gradlew new file mode 100755 index 000000000..83f2acfdc --- /dev/null +++ b/DSL/gradlew @@ -0,0 +1,188 @@ +#!/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 +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +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='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +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 + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# 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 + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((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" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +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/DSL/gradlew.bat b/DSL/gradlew.bat new file mode 100644 index 000000000..9618d8d96 --- /dev/null +++ b/DSL/gradlew.bat @@ -0,0 +1,100 @@ +@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 +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@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="-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 + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +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% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/DSL/settings.gradle.kts b/DSL/settings.gradle.kts new file mode 100644 index 000000000..a829b0c82 --- /dev/null +++ b/DSL/settings.gradle.kts @@ -0,0 +1,7 @@ +rootProject.name = "Simple-ML.DSL" + +include( + "de.unibonn.simpleml", + "de.unibonn.simpleml.ide", + "de.unibonn.simpleml.vscode" +) diff --git a/README.md b/README.md new file mode 100644 index 000000000..e532021d8 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# safe-data-science + +[Read the docs][docs] + +[docs]: ./docs/README.md diff --git a/Runtime/safe-ds-runner/poetry.lock b/Runtime/safe-ds-runner/poetry.lock new file mode 100644 index 000000000..4d0155fa2 --- /dev/null +++ b/Runtime/safe-ds-runner/poetry.lock @@ -0,0 +1,245 @@ +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "21.4.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "coverage" +version = "6.4.1" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "dev" +optional = false +python-versions = ">=3.6.8" + +[package.extras] +diagrams = ["railroad-diagrams", "jinja2"] + +[[package]] +name = "pytest" +version = "7.1.2" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +tomli = ">=1.0.0" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "3.0.0" +description = "Pytest plugin for measuring coverage." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] + +[[package]] +name = "safe-ds" +version = "0.0.1" +description = "The Safe-DS standard library." +category = "dev" +optional = false +python-versions = "^3.10" +develop = true + +[package.source] +type = "directory" +url = "../safe-ds" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" + +[metadata] +lock-version = "1.1" +python-versions = "^3.10" +content-hash = "a2a90f1dbd07bfce799db49e93df2744fcb017fa6f6288e55cecf4abb07b58ea" + +[metadata.files] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, + {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +coverage = [ + {file = "coverage-6.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b"}, + {file = "coverage-6.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068"}, + {file = "coverage-6.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4"}, + {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84e65ef149028516c6d64461b95a8dbcfce95cfd5b9eb634320596173332ea84"}, + {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f69718750eaae75efe506406c490d6fc5a6161d047206cc63ce25527e8a3adad"}, + {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc"}, + {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749"}, + {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4"}, + {file = "coverage-6.4.1-cp310-cp310-win32.whl", hash = "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df"}, + {file = "coverage-6.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6"}, + {file = "coverage-6.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46"}, + {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982"}, + {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f4f3df85aa39da00fd3ec4b5abeb7407e82b68c7c5ad181308b0e2526da5d4"}, + {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:961e2fb0680b4f5ad63234e0bf55dfb90d302740ae9c7ed0120677a94a1590cb"}, + {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b"}, + {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3"}, + {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6"}, + {file = "coverage-6.4.1-cp37-cp37m-win32.whl", hash = "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e"}, + {file = "coverage-6.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28"}, + {file = "coverage-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54"}, + {file = "coverage-6.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9"}, + {file = "coverage-6.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13"}, + {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2bd9a6fc18aab8d2e18f89b7ff91c0f34ff4d5e0ba0b33e989b3cd4194c81fd9"}, + {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3384f2a3652cef289e38100f2d037956194a837221edd520a7ee5b42d00cc605"}, + {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d"}, + {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428"}, + {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83"}, + {file = "coverage-6.4.1-cp38-cp38-win32.whl", hash = "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b"}, + {file = "coverage-6.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c"}, + {file = "coverage-6.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df"}, + {file = "coverage-6.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d"}, + {file = "coverage-6.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4"}, + {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd180ed867e289964404051a958f7cccabdeed423f91a899829264bb7974d3d3"}, + {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3"}, + {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8"}, + {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72"}, + {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264"}, + {file = "coverage-6.4.1-cp39-cp39-win32.whl", hash = "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9"}, + {file = "coverage-6.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397"}, + {file = "coverage-6.4.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815"}, + {file = "coverage-6.4.1.tar.gz", hash = "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] +pyparsing = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] +pytest = [ + {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, + {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, +] +pytest-cov = [ + {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, + {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, +] +safe-ds = [] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] diff --git a/Runtime/safe-ds-runner/pyproject.toml b/Runtime/safe-ds-runner/pyproject.toml new file mode 100644 index 000000000..55361006d --- /dev/null +++ b/Runtime/safe-ds-runner/pyproject.toml @@ -0,0 +1,27 @@ +[tool.poetry] +name = "safe-ds-runner" +version = "0.0.1" +description = "Run Safe-DS code." +authors = ["Lars Reimann "] +license = "MIT" + +[tool.poetry.scripts] + +[tool.poetry.dependencies] +python = "^3.10" + +[tool.poetry.dev-dependencies] +pytest = "^7.1.2" +pytest-cov = "^3.0.0" +safe-ds = { path = "../safe-ds/", develop = true } + +[tool.mypy] +ignore_missing_imports = true +disallow_untyped-calls = true +disallow_untyped-defs = true +disallow_incomplete-defs = true +disallow_untyped-decorators = true + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/Runtime/safe-ds-runner/safe_ds_runner/__init__.py b/Runtime/safe-ds-runner/safe_ds_runner/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Runtime/safe-ds-runner/safe_ds_runner/codegen/__init__.py b/Runtime/safe-ds-runner/safe_ds_runner/codegen/__init__.py new file mode 100644 index 000000000..e793a7a35 --- /dev/null +++ b/Runtime/safe-ds-runner/safe_ds_runner/codegen/__init__.py @@ -0,0 +1 @@ +from ._operators import eager_and, eager_or, eager_elvis, safe_access diff --git a/Runtime/safe-ds-runner/safe_ds_runner/codegen/_operators.py b/Runtime/safe-ds-runner/safe_ds_runner/codegen/_operators.py new file mode 100644 index 000000000..8d994009f --- /dev/null +++ b/Runtime/safe-ds-runner/safe_ds_runner/codegen/_operators.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from typing import Any, TypeVar, Optional + + +def eager_or(left_operand: bool, right_operand: bool) -> bool: + return left_operand or right_operand + + +def eager_and(left_operand: bool, right_operand: bool) -> bool: + return left_operand and right_operand + + +Elvis_T = TypeVar("Elvis_T") + + +def eager_elvis(left_operand: Elvis_T, right_operand: Elvis_T) -> Elvis_T: + return left_operand if left_operand is not None else right_operand + + +Safe_Access_T = TypeVar("Safe_Access_T") + + +def safe_access(receiver: Any, member_name: str) -> Optional[Safe_Access_T]: + return getattr(receiver, member_name) if receiver is not None else None diff --git a/Runtime/safe-ds-runner/tests/codegen/test_eager_and.py b/Runtime/safe-ds-runner/tests/codegen/test_eager_and.py new file mode 100644 index 000000000..26cc2b414 --- /dev/null +++ b/Runtime/safe-ds-runner/tests/codegen/test_eager_and.py @@ -0,0 +1,48 @@ +import pytest + +from safe_ds_runner.codegen import eager_and + + +@pytest.mark.parametrize( + "left_operand,right_operand,expected_result", + [ + (False, False, False), + (False, True, False), + (True, False, False), + (True, True, True), + ] +) +def test_should_compute_conjunction(left_operand: bool, right_operand: bool, expected_result: bool): + assert eager_and(left_operand, right_operand) == expected_result + + +def test_should_evaluate_left_operand_before_right_operand(): + call_order: list[str] = [] + + def left() -> bool: + call_order.append("left") + return True + + def right() -> bool: + call_order.append("right") + return True + + eager_and(left(), right()) + + assert call_order == ["left", "right"] + + +def test_should_always_evaluate_both_operands(): + call_order: list[str] = [] + + def left() -> bool: + call_order.append("left") + return False + + def right() -> bool: + call_order.append("right") + return False + + eager_and(left(), right()) + + assert call_order == ["left", "right"] diff --git a/Runtime/safe-ds-runner/tests/codegen/test_eager_elvis.py b/Runtime/safe-ds-runner/tests/codegen/test_eager_elvis.py new file mode 100644 index 000000000..b419b47b3 --- /dev/null +++ b/Runtime/safe-ds-runner/tests/codegen/test_eager_elvis.py @@ -0,0 +1,55 @@ +from typing import Any + +import pytest + +from safe_ds_runner.codegen import eager_elvis + + +@pytest.mark.parametrize( + "left_operand,right_operand,expected_result", + [ + (None, None, None), + (None, False, False), + (None, True, True), + (False, None, False), + (False, False, False), + (False, True, False), + (True, None, True), + (True, False, True), + (True, True, True), + ] +) +def test_should_compute_elvis_operation(left_operand: Any, right_operand: Any, expected_result: Any): + assert eager_elvis(left_operand, right_operand) == expected_result + + +def test_should_evaluate_left_operand_before_right_operand(): + call_order: list[str] = [] + + def left() -> Any: + call_order.append("left") + return None + + def right() -> Any: + call_order.append("right") + return None + + eager_elvis(left(), right()) + + assert call_order == ["left", "right"] + + +def test_should_always_evaluate_both_operands(): + call_order: list[str] = [] + + def left() -> Any: + call_order.append("left") + return True + + def right() -> Any: + call_order.append("right") + return True + + eager_elvis(left(), right()) + + assert call_order == ["left", "right"] diff --git a/Runtime/safe-ds-runner/tests/codegen/test_eager_or.py b/Runtime/safe-ds-runner/tests/codegen/test_eager_or.py new file mode 100644 index 000000000..c43ccdc85 --- /dev/null +++ b/Runtime/safe-ds-runner/tests/codegen/test_eager_or.py @@ -0,0 +1,48 @@ +import pytest + +from safe_ds_runner.codegen import eager_or + + +@pytest.mark.parametrize( + "left_operand,right_operand,expected_result", + [ + (False, False, False), + (False, True, True), + (True, False, True), + (True, True, True), + ] +) +def test_should_compute_disjunction(left_operand: bool, right_operand: bool, expected_result: bool): + assert eager_or(left_operand, right_operand) == expected_result + + +def test_should_evaluate_left_operand_before_right_operand(): + call_order: list[str] = [] + + def left() -> bool: + call_order.append("left") + return False + + def right() -> bool: + call_order.append("right") + return False + + eager_or(left(), right()) + + assert call_order == ["left", "right"] + + +def test_should_always_evaluate_both_operands(): + call_order: list[str] = [] + + def left() -> bool: + call_order.append("left") + return True + + def right() -> bool: + call_order.append("right") + return True + + eager_or(left(), right()) + + assert call_order == ["left", "right"] diff --git a/Runtime/safe-ds-runner/tests/codegen/test_safe_access.py b/Runtime/safe-ds-runner/tests/codegen/test_safe_access.py new file mode 100644 index 000000000..e0ff045fa --- /dev/null +++ b/Runtime/safe-ds-runner/tests/codegen/test_safe_access.py @@ -0,0 +1,46 @@ +from typing import Any + +import pytest + +from safe_ds_runner.codegen import safe_access + + +# Test data -------------------------------------------------------------------- + +class __C: + def __init__(self): + self.a: int = 1 + + +# Actual tests ----------------------------------------------------------------- + +@pytest.mark.parametrize( + "receiver,member_name,expected_result", + [ + (None, "a", None), + (__C(), "a", 1), + ] +) +def test_should_guard_against_member_access_on_none( + receiver: Any, + member_name: str, + expected_result: Any +): + assert safe_access(receiver, member_name) == expected_result + + +def test_should_evaluate_receiver_exactly_once(): + call_order: list[str] = [] + + def receiver() -> Any: + call_order.append("call") + return __C() + + safe_access(receiver(), "a") + + assert len(call_order) == 1 + + +def test_should_raise_exception_if_member_does_not_exist(): + with pytest.raises(AttributeError, match=r"'__C' object has no attribute 'b'"): + safe_access(__C(), "b") diff --git a/Runtime/safe-ds/poetry.lock b/Runtime/safe-ds/poetry.lock new file mode 100644 index 000000000..aa00b8894 --- /dev/null +++ b/Runtime/safe-ds/poetry.lock @@ -0,0 +1,231 @@ +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "21.4.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "coverage" +version = "6.4.1" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "dev" +optional = false +python-versions = ">=3.6.8" + +[package.extras] +diagrams = ["railroad-diagrams", "jinja2"] + +[[package]] +name = "pytest" +version = "7.1.2" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +tomli = ">=1.0.0" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "3.0.0" +description = "Pytest plugin for measuring coverage." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" + +[metadata] +lock-version = "1.1" +python-versions = "^3.10" +content-hash = "4c9d18e8b9e8efb022f8e741f2fdfdee94f76a90aad9ef0549948d15454e6e55" + +[metadata.files] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, + {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +coverage = [ + {file = "coverage-6.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b"}, + {file = "coverage-6.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068"}, + {file = "coverage-6.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4"}, + {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84e65ef149028516c6d64461b95a8dbcfce95cfd5b9eb634320596173332ea84"}, + {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f69718750eaae75efe506406c490d6fc5a6161d047206cc63ce25527e8a3adad"}, + {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc"}, + {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749"}, + {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4"}, + {file = "coverage-6.4.1-cp310-cp310-win32.whl", hash = "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df"}, + {file = "coverage-6.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6"}, + {file = "coverage-6.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46"}, + {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982"}, + {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f4f3df85aa39da00fd3ec4b5abeb7407e82b68c7c5ad181308b0e2526da5d4"}, + {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:961e2fb0680b4f5ad63234e0bf55dfb90d302740ae9c7ed0120677a94a1590cb"}, + {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b"}, + {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3"}, + {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6"}, + {file = "coverage-6.4.1-cp37-cp37m-win32.whl", hash = "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e"}, + {file = "coverage-6.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28"}, + {file = "coverage-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54"}, + {file = "coverage-6.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9"}, + {file = "coverage-6.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13"}, + {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2bd9a6fc18aab8d2e18f89b7ff91c0f34ff4d5e0ba0b33e989b3cd4194c81fd9"}, + {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3384f2a3652cef289e38100f2d037956194a837221edd520a7ee5b42d00cc605"}, + {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d"}, + {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428"}, + {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83"}, + {file = "coverage-6.4.1-cp38-cp38-win32.whl", hash = "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b"}, + {file = "coverage-6.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c"}, + {file = "coverage-6.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df"}, + {file = "coverage-6.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d"}, + {file = "coverage-6.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4"}, + {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd180ed867e289964404051a958f7cccabdeed423f91a899829264bb7974d3d3"}, + {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3"}, + {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8"}, + {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72"}, + {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264"}, + {file = "coverage-6.4.1-cp39-cp39-win32.whl", hash = "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9"}, + {file = "coverage-6.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397"}, + {file = "coverage-6.4.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815"}, + {file = "coverage-6.4.1.tar.gz", hash = "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] +pyparsing = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] +pytest = [ + {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, + {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, +] +pytest-cov = [ + {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, + {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, +] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] diff --git a/Runtime/safe-ds/pyproject.toml b/Runtime/safe-ds/pyproject.toml new file mode 100644 index 000000000..71f21df81 --- /dev/null +++ b/Runtime/safe-ds/pyproject.toml @@ -0,0 +1,24 @@ +[tool.poetry] +name = "safe-ds" +version = "0.0.1" +description = "The Safe-DS standard library." +authors = ["Lars Reimann "] +license = "MIT" + +[tool.poetry.dependencies] +python = "^3.10" + +[tool.poetry.dev-dependencies] +pytest = "^7.1.2" +pytest-cov = "^3.0.0" + +[tool.mypy] +ignore_missing_imports = true +disallow_untyped-calls = true +disallow_untyped-defs = true +disallow_incomplete-defs = true +disallow_untyped-decorators = true + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/Runtime/safe-ds/safe_ds/__init__.py b/Runtime/safe-ds/safe_ds/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Runtime/safe-ds/tests/__init__.py b/Runtime/safe-ds/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/docs/DSL/development/how-to-add-a-new-language-concept.md b/docs/DSL/development/how-to-add-a-new-language-concept.md new file mode 100644 index 000000000..52dfc679d --- /dev/null +++ b/docs/DSL/development/how-to-add-a-new-language-concept.md @@ -0,0 +1,110 @@ +# How to add a new language concept + +1. Create new classes in the EMF model (_abstract syntax_): + 1. [Update the Ecore file][SimpleML.ecore]. Ensure that `SmlAbstractObject` is either a direct of transitive supertype. + 1. [Update the Genmodel file][SimpleML.genmodel]. + +1. Update the grammar (_concrete syntax_). + 1. Create [grammar tests][grammar-tests]: + 1. Positive tests (with comment `// no_syntax_error`) + 1. Negative tests (with comment `// syntax_error`) + 1. Run the tests (`./gradlew test`). There should be failures. + 1. Update the [Xtext grammar file][SimpleML.xtext]. + 1. Run the tests again (`./gradlew test`). Tests should now pass. + +1. Update the [constants][constants] if the concrete syntax of your concept has terminals that need to be accessed programmatically (e.g. operators or modifiers). + +1. Update the [creators][creators], which simplify the creation of instances of model classes. There should be at least one function for each class. + +1. Update the [access shortcuts][shortcuts], which simplify the traversal of the EMF model. This is not always required and the file should only contain functions that are simple enough to not require tests. + +1. Update the converters if your concept includes terminals where the value they represent differs from their textual representation. + 1. Create [converter tests][converter-tests] + 1. Run the tests (`./gradlew test`). There should be failures. + 1. Add a [converter][converters]. + 1. Run the tests again (`./gradlew test`). Tests should now pass. + +1. Update the scope provider if your concept has cross-references. + 1. Create [scoping tests][scoping-tests]. + 1. Run the tests (`./gradlew test`). There should be failures. + 1. Update the [local scope provider][local-scope-provider]. + 1. Run the tests again (`./gradlew test`). Tests should now pass. + +1. Update the [resource description strategy][resource-description-strategy] if your concept is a declaration that should be visible from another file. + +1. Update the [static analyses][static-analysis]. + +1. Update the validator. + 1. Create [validation tests][validation-tests] + 1. Run the tests (`./gradlew test`). There should be failures. + 1. Update the [validators][validators] or add a new one. + 1. Run the tests again (`./gradlew test`). Tests should now pass. + +1. Update the code generator. + 1. Create [generator tests][generator-tests] + 1. Run the tests (`./gradlew test`). There should be failures. + 1. Update the [generator][generator]. + 1. Run the tests again (`./gradlew test`). Tests should now pass. + +1. Update the formatter. + 1. Create [formatting tests][formatting-tests]. + 1. Run the tests (`./gradlew test`). There should be failures. + 1. Update the [formatter][formatting]. + 1. Run the tests again (`./gradlew test`). Tests should now pass. + +1. Update the converter to Prolog. + 1. Create [classes for facts][prolog-facts]. + 1. Create [tests][prolog-tests]. + 1. Run the tests (`./gradlew test`). There should be failures. + 1. Update the [converter to Prolog][prolog-converter]. + 1. Run the tests again (`./gradlew test`). Tests should now pass. + +1. Update the [tutorial][tutorial]. + + + +[SimpleML.ecore]: ../../../DSL/de.unibonn.simpleml/model/SimpleML.ecore + +[SimpleML.genmodel]: ../../../DSL/de.unibonn.simpleml/model/SimpleML.genmodel + +[grammar-tests]: ../../../DSL/de.unibonn.simpleml/src/test/resources/grammar + +[SimpleML.xtext]: ../../../DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/SimpleML.xtext + +[converter-tests]: ../../../DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/conversion + +[converters]: ../../../DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/conversion + +[scoping-tests]: ../../../DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/scoping/ScopingTest.kt + +[local-scope-provider]: ../../../DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/scoping/SimpleMLScopeProvider.kt + +[resource-description-strategy]: ../../../DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/scoping/SimpleMLResourceDescriptionStrategy.kt + +[static-analysis]: ../../../DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/staticAnalysis + +[validation-tests]: ../../../DSL/de.unibonn.simpleml/src/test/resources/validation + +[validators]: ../../../DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/validation + +[constants]: ../../../DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/constant + +[creators]: ../../../DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/emf/Creators.kt + +[shortcuts]: ../../../DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/emf/SimpleShortcuts.kt + +[generator-tests]: ../../../DSL/de.unibonn.simpleml/src/test/resources/generator + +[generator]: ../../../DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/generator/SimpleMLGenerator.kt + +[formatting-tests]: ../../../DSL/de.unibonn.simpleml/src/test/resources/formatting + +[formatting]: ../../../DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/formatting2/SimpleMLFormatter.kt + +[prolog-facts]: ../../../DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/model/facts/Facts.kt + +[prolog-tests]: ../../../DSL/de.unibonn.simpleml/src/test/kotlin/de/unibonn/simpleml/prologBridge/AstToPrologFactbaseTest.kt + +[prolog-converter]: ../../../DSL/de.unibonn.simpleml/src/main/kotlin/de/unibonn/simpleml/prologBridge/converters/AstToPrologFactbase.kt + +[tutorial]: ../tutorial/README.md diff --git a/docs/DSL/tutorial/README.md b/docs/DSL/tutorial/README.md new file mode 100644 index 000000000..65baff54a --- /dev/null +++ b/docs/DSL/tutorial/README.md @@ -0,0 +1,10 @@ +# Simple-ML DSL Tutorial + +The Simple-ML DSL is split into two main parts: + +* The _[workflow language][workflow-language]_ is used to solve specific Machine Learning problems. Unless you want to add functionality to Simple-ML, this sublanguage is all you need to learn. To use the workflow language, create a file with the extension `.smlflow`. +* The _[stub language][stub-language]_ is used to integrate external code written in Python into Simple-ML, so it can be used in the [workflow language][workflow-language]. The main purpose of this sublanguage is to define the [Simple-ML Standard Library (stdlib)][stdlib]. To use the stub language, create a file with the extension `.smlstub`. + +[workflow-language]: ./workflow-language/README.md +[stub-language]: ./stub-language/README.md +[stdlib]: ../../../DSL/de.unibonn.simpleml/src/main/resources/stdlib diff --git a/docs/DSL/tutorial/common/README.md b/docs/DSL/tutorial/common/README.md new file mode 100644 index 000000000..cd397f179 --- /dev/null +++ b/docs/DSL/tutorial/common/README.md @@ -0,0 +1,20 @@ +# Common Parts of Workflow Language and Stub Language + +Several parts of the Simple-ML DSL are shared between the [workflow lanugage][workflow-language] and the [stub language][stub-language]. Here is the list: + +* [Packages][packages] help avoid conflicts that could arise if two declarations have the same name. +* [Imports][imports] make declarations in other packages accessible. +* [Parameters][parameters] define the expected inputs of some declaration that can be [called][calls]. +* [Results][results] define the outputs of some declaration when it is [called][calls]. +* [Types][types] describe the values that a declaration can accept. +* [Comments][comments] document the code. + +[workflow-language]: ../workflow-language/README.md +[stub-language]: ../stub-language/README.md +[calls]: ../workflow-language/expressions.md#calls +[packages]: ./packages.md +[imports]: ./imports.md +[parameters]: ./parameters.md +[results]: ./results.md +[types]: ./types.md +[comments]: ./comments.md diff --git a/docs/DSL/tutorial/common/comments.md b/docs/DSL/tutorial/common/comments.md new file mode 100644 index 000000000..94f89ba4c --- /dev/null +++ b/docs/DSL/tutorial/common/comments.md @@ -0,0 +1,30 @@ +# Comments + +Comments are mostly used to add explanations to the code for future readers - which can be yourself. They can also be used to "comment out" code that you want to keep but that should not be run right now, since comments are ignored by the compiler. + +Simple-ML has two types of comments, namely _line comments_, which end at a linebreak, and _block comments_, which can cover multiple lines. + +## Line Comments + +Line comments stop at the end of a line: + +```kt +// This is a comment. +``` + +As we can see here, they start with two slashes. There must be no space between the slashes. Everything after the double slashes in the same line is the text of the comment. + +## Block Comments + +Block comments have a start and end delimiter, which allows them to cover multiple lines: + +```kt +/* +This +is +another +comment +*/ +``` + +They are started with `/*` and end at the inverted delimiter `*/`. There must be no space between the slash and the star. Everything in between the delimiters is the text of the comment. diff --git a/docs/DSL/tutorial/common/imports.md b/docs/DSL/tutorial/common/imports.md new file mode 100644 index 000000000..1bdac9897 --- /dev/null +++ b/docs/DSL/tutorial/common/imports.md @@ -0,0 +1,76 @@ +# Imports + +By default, only declarations in the same package as the current file or in the `simpleml.lang` [package][packages] are accessible. All other declarations must be imported first. + +Simple-ML has two kinds of imports, namely a _qualified import_, which imports a single declaration, and a _wildcard import_, which imports everything in a [package][packages]. + +## Qualified Imports + +A _qualified import_ makes a single declaration available. Here is an example that imports the [class][classes] `DecisionTree` in the [package][packages] `simpleml.model.regression`: + +``` +import simpleml.model.regression.DecisionTree +``` + +The syntax consists of the following parts: +* The keyword `import` +* The _qualified name_ of the declaration to import (here `simpleml.model.regression.DecisionTree`). The qualified name can be obtained by concatenating the name of the [package][packages] that contains the declaration (i.e. `simpleml.model.regression`) with the name of the declaration (i.e. `DecisionTree`). The two parts are separated by a dot. + +Once the declaration is imported, we can refer to it by its _simple name_. This is the last segment of the qualified name, which is the name of the declaration. Here is, for example, a [call][calls] to the constructor of the `DecisionTree` class: + +``` +DecisionTree() +``` + +### Qualified Imports with Alias + +Sometimes the name of the imported declaration can conflict with other declarations that are imported or that are contained in the importing file. To counter this, declarations can be imported under an alias: + +``` +import simpleml.model.regression.DecisionTree as StdlibDecisionTree +``` + +Let us take apart the syntax: +* The keyword `import`. +* The _qualified name_ of the declaration to import (here `simpleml.model.regression.DecisionTree`). The qualified name can be obtained by concatenating the name of the [package][packages] that contains the declaration (i.e. `simpleml.model.regression`) with the name of the declaration (i.e. `DecisionTree`). The two parts are separated by a dot. +* The keyword `as`. +* The alias to use (here `StdlibDecisionTree`). This can be any combination of upper- and lowercase letters, underscores, and numbers, as long as it does not start with a number. + +Afterwards, the declaration can **only** be accessed using the alias. The simple name cannot be used anymore. The next example shows how to create a new instance of the class now by invoking its constructor: + +``` +StdlibDecisionTree() +``` + +## Wildcard Imports + +We can also import all declarations in a [package][packages] with a single import. While this saves some typing, it also increases the likelihood of name conflicts and can make it harder for readers of the code to determine where a declaration comes from. Therefore, this should be used with caution. + +Nevertheless, let us look at an example, which imports all declarations from the [package][packages] `simpleml.model.regression`: + +``` +import simpleml.model.regression.* +``` + +Here is the breakdown of the syntax: +* The keyword `import`. +* The name of the [package][packages] to import (here `simpleml.model.regression`). +* A dot. +* A star. + +Afterwards, we can again access declarations by their simple name, such as the [class][classes] `DecisionTree`: + +``` +DecisionTree() +``` + +[Aliases](#qualified-imports-with-alias) cannot be used together with wildcard imports. + +Note that declarations in subpackages, i. e. packages that have a different name but the same prefix as the imported one, are **not** imported. Therefore, if we would only `import simpleml.model`, we could no longer access the [class][classes] `DecisionTree`. + + + +[stub-language]: ../stub-language/README.md +[classes]: ../stub-language/classes.md +[packages]: ./packages.md +[calls]: ../workflow-language/expressions.md#calls diff --git a/docs/DSL/tutorial/common/packages.md b/docs/DSL/tutorial/common/packages.md new file mode 100644 index 000000000..7d90124cf --- /dev/null +++ b/docs/DSL/tutorial/common/packages.md @@ -0,0 +1,157 @@ +# Packages + +_Packages_ are used to prevent conflicts when multiple files have declarations with the same name. They accomplish this by grouping all declarations in a file into a namespace. Here is an example for a package declaration: + +``` +package de.unibonn.speedPrediction +``` + +It has these syntactic elements: +* The keyword `package`. +* The name of the package, which consists of multiple _segments_ separated by dots. Each segment can be any combination of upper- and lowercase letters, underscores, and numbers, as long as it does not start with a number. However, we suggest to use `lowerCamelCase` for all segments. We also recommend to use the [reverse domain name notation](https://en.wikipedia.org/wiki/Reverse_domain_name_notation) for the package name. + +Multiple files can belong to the same package but each non-empty file must declare its package before any [import][imports] or declarations. Moreover, within the same package the names of declarations must be unique. + +Continue with the explanation of [imports][imports] to understand how to access declarations that are defined in other packages. + +## Corresponding Python Code + +**Note:** This section is only relevant if you are interested in the [stub language][stub-language]. + +In order for code generation to work properly, the Simple-ML package of a Simple-ML declaration in the [stub language][stub-language] must correspond to a [Python module][python-modules] that exports its matching Python declaration. These [Python modules][python-modules] can either directly contain the implementation or globally import the declaration. The latter is possible since all declarations that are globally imported in a [Python module][python-modules] are also automatically exported again in Python. + +### File Contains Implementation + +The first option is to use the file that contains the actual implementation of the declaration. Let us start with the following Python code to explain this idea: + +```py +# Python file "simpleml/model/regression/_decision_tree.py" + +class DecisionTree: + def __init__(self): + pass # Implementation omitted +``` + +This file contains the actual implementation of the Python class `DecisionTree`. We now want to make this Python class available in Simple-ML, which requires the following Simple-ML stub file: + +``` +// Simple-ML file "simpleml/model/regression/_decision_tree/DecisionTree.smlstub" + +package simpleml.model.regression._decision_tree + +class DecisionTree() +``` + +Note that the Simple-ML package corresponds to the path to the Python file, where we replace file separators (here `/`) by dots and remove the file extension (here `.py`). Another way to think about this is to ask how a from-import of the Python declaration would look like in Python code that wants to use the Python declaration. In this case we would write + +```py +# Python + +from simpleml.model.regression._decision_tree import DecisionTree +``` + +The part between `from` and `import` is exactly what the Simple-ML package has to be. + +The file path is irrelevant in Simple-ML. For the sake of code organization, however, we advise to use the segments of the package name to create a folder hierarchy and then create a Simple-ML stub file with some fitting name in there. + +### File Imports Declaration + +The second option is to chose the Simple-ML package that corresponds to a [Python module][python-modules] that imports the declaration we are interested in. This is particularly useful if multiple [Python modules][python-modules] are bundled into a [Python package][python-packages]. In the `__init__.py` file we can then bundle the declarations from different [Python module][python-modules] together. + +We will demonstrate this by extending the example we used above: + +```py +# Python file "simpleml/model/regression/_decision_tree.py" + +class DecisionTree: + def __init__(self): + pass # Implementation omitted +``` + +```py +# Python file "simpleml/model/regression/__init__.py + +from ._decision_tree import DecisionTree +``` + +The addition of the `__init__.py` file now allows us to import the Python class `DecisionTree` in another way in Python client code: + +```py +# Python + +from simpleml.model.regression import DecisionTree +``` + +Note the omission of the suffix `._decision_tree` after `simpleml.model.regression`. Likewise, we can now update the Simple-ML stub code. We again just take everything between `from` and `import` and use this as the Simple-ML package name: + +``` +// Simple-ML file "simpleml/model/regression/DecisionTree.smlstub" + +package simpleml.model.regression + +class DecisionTree() +``` + +Generally, this method is preferable to our initial solution in the Section ["File Contains Implementation"](#file-contains-implementation), since we hide the detail in which file a declaration resides. This makes later refactorings easier since we can freely move declarations between files in the same Python package without breaking client code. + +### `@PythonModule` Annotation + +Choosing the Simple-ML package according to the rules described above is essential for code generation to work properly. However, we might sometimes get warnings related to the Simple-ML naming convention, which wants the segments of the Simple-ML package to be `lowerCamelCase`. We now have several options: +* If the declaration is not part of the Simple-ML standard library, we can ignore the warning. +* We can update the Python code. +* We can use the `@PythonModule` annotation. + +The first two options are self-explanatory. Let us now look at the third one. We use our initial example from the Section ["File Contains Implementation"](#file-contains-implementation) again: + +```py +# Python file "simpleml/model/regression/_decision_tree.py" + +class DecisionTree: + def __init__(self): + pass # Implementation omitted +``` + +Our original solution leads to a warning because the Simple-ML package name contains the segment `_decision_tree`, which is not `lowerCamelCase` due to the underscores: + +``` +// Simple-ML file "simpleml/model/regression/_decision_tree/DecisionTree.smlstub" + +package simpleml.model.regression._decision_tree + +class DecisionTree() +``` + +By [calling][annotation-calls] the [annotation][annotations] `@PythonModule`, we can also specify the corresponding [Python module][python-modules], however. If this [annotation][annotations] is [called][annotation-calls], it takes precedence over the Simple-ML package name. This allows us to pick an arbitrary Simple-ML package that respects the Simple-ML naming convention. We can even group multiple [Python modules][python-modules] together in one Simple-ML package without relying on Python's `__init__.py` files: + +``` +// Simple-ML file "simpleml/model/regression/DecisionTree.smlstub" + +@PythonModule("simpleml.model.regression._decision_tree") + +package simpleml.model.regression + +class DecisionTree() +``` + +Here is a breakdown of this: +* We [call][annotation-calls] the `@PythonModule` [annotation][annotations] before we declare the Simple-ML package. The [Python module][python-modules] that exports the Python declarations that correspond to the Simple-ML declarations in this stub file is passed as a [string literal][string-literals] (here `simpleml.model.regression._decision_tree`). This is used only for code generation and does not affect users of Simple-ML. +* We specify the Simple-ML package as usual. This must be used when we [import][imports] the declaration in another Simple-ML file: + ``` + // Simple-ML + + import simpleml.model.regression.DecisionTree + ``` + +It is important to note that the `@PythonModule` annotation only affects the one Simple-ML file that contains it rather than the entire Simple-ML package. This allows different Simple-ML files in the same package to point to different [Python modules][python-modules]. + +[stub-language]: ../stub-language/README.md +[annotations]: ../stub-language/annotations.md +[annotation-calls]: ../stub-language/annotations.md#calling-an-annotation +[imports]: ./imports.md +[classes]: ../stub-language/classes.md +[steps]: ../workflow-language/steps.md +[workflows]: ../workflow-language/workflows.md +[string-literals]: ../workflow-language/expressions.md#string-literals + +[python-modules]: https://docs.python.org/3/tutorial/modules.html#modules +[python-packages]: https://docs.python.org/3/tutorial/modules.html#packages diff --git a/docs/DSL/tutorial/common/parameters.md b/docs/DSL/tutorial/common/parameters.md new file mode 100644 index 000000000..c15df7a7f --- /dev/null +++ b/docs/DSL/tutorial/common/parameters.md @@ -0,0 +1,197 @@ +# Parameters + +_Parameters_ define the expected inputs of some declaration that can be [called][calls]. We refer to such declarations as _callables_. We distinguish between +* [required parameters](#required-parameters), which must always be passed, +* [optional parameters](#optional-parameters), which use a default value if no value is passed explicitly, and +* [variadic parameters](#variadic-parameters), which can accept zero or more values. + +## Required Parameters + +_Required parameters_ must always be passed when the declaration is [called][calls]. Let us look at an example: + +```txt +requiredParameter: Int +``` + +Here are the pieces of syntax: +* The name of the parameter (here `requiredParameter`). This can be any combination of upper- and lowercase letters, underscores, and numbers, as long as it does not start with a number. However, we suggest to use `lowerCamelCase` for the names of parameters. +* A colon. +* The [type][types] of the parameter (here `Int`). + +## Optional Parameters + +_Optional parameters_ have a default value and, thus, need not be passed as an [argument][calls] unless the default value does not fit. Here is an example: + +```txt +optionalParameter: Int = 1 +``` + +These are the syntactic elements: +* The name of the parameter (here `optionalParameter`). This can be any combination of upper- and lowercase letters, underscores, and numbers, as long as it does not start with a number. However, we suggest to use `lowerCamelCase` for the names of parameters. +* A colon. +* The [type][types] of the parameter (here `Int`). +* An equals sign. +* The default value of the parameter (here `1`). This must be a constant expression, i.e. something that can be evaluated by the compiler. Particularly [calls][calls] usually do not fulfill this requirement. + +## Variadic Parameters + +_Variadic parameters_ can consume arbitrarily many [arguments][calls]. Here is an example: + +```txt +vararg variadicParameter: Int +``` + +Let us break down the syntax: +* The keyword `vararg` +* The name of the parameter (here `variadicParameter`). This can be any combination of upper- and lowercase letters, underscores, and numbers, as long as it does not start with a number. However, we suggest to use `lowerCamelCase` for the names of parameters. +* A colon. +* The [type][types] of the parameter (here `Int`). + +## Complete Example + +Let us now look at a full example of a [step][steps] called `doSomething` with one [required parameter](#required-parameters) and one [optional parameter](#optional-parameters): + +```txt +step doSomething(requiredParameter: Int, optionalParameter: Boolean = false) { + // ... +} +``` + +The interesting part is the list of parameters, which uses the following syntactic elements: +* An opening parenthesis. +* A list of parameters, the syntax is as described above. They are separated by commas. A trailing commas is permitted. +* A closing parenthesis. + +## Restrictions + +Several restrictions apply to the order of parameters and to combinations of the various categories of parameters: +* After an [optional parameter](#optional-parameters) all parameters must be optional. +* A single [variadic parameter](#variadic-parameters) can be added at the end of the parameter list. +* Implied by this: A callable cannot have both [optional parameters](#optional-parameters) and [variadic parameters](#variadic-parameters). + + +## Corresponding Python Code + +**Note:** This section is only relevant if you are interested in the [stub language][stub-language]. + +Parameters must be ordered the same way in Python as they are in Simple-ML. Moreover, for each parameter the following elements must match: +* Name +* Type +* Kind (required vs. optional vs. variadic) +* Default value for optional parameters + +Let's look at these elements in turn. + +### Matching Name + +By default, parameter names in Simple-ML must be identical to their names in Python. If this is not desired, for example due to clashing name conventions in Simple-ML and Python, the `@PythonName` annotation can be used to link a Simple-ML parameter to a Python parameter with a different name. Here is an example: + +```py +# Python code + +def accuracy(x_pred: Dataset, x_test: Dataset) -> float: + pass +``` + +```txt +// Simple-ML code + +fun accuracy( + @PythonName("x_pred") xPred: Dataset, + @PythonName("x_test") xTest: Dataset +) -> accuracy: Float +``` + +In this case, the Simple-ML parameters `xPred` and `xTest` refer to the Python parameters `x_pred` and `x_test` respectively. + +### Matching Type + +The Simple-ML type of a parameter should capture the legal values of this parameter accurately. Ideally, the Python parameter should also have a matching [type hint][types-python]. + +### Matching Kind + +Parameters kinds must match on the Simple-ML and Python sides as well. Concretely, this means: +* All required parameters in Simple-ML must be required in Python. +* All optional parameters in Simple-ML must be optional in Python. +* All variadic parameters in Simple-ML must be variadic in Python (`*args`). + +Moreover, it must be possible to pass +* required parameters by position, +* optional parameters by name, +* variadic parameters by position. + +These rules allow us to restrict required parameters to [positional-only][python-positional-only] or optional parameters to [keyword-only][python-keyword-only]. We can also keep both unrestricted. + +The following three examples show valid pairs of Python and Simple-ML programs. + +#### Required Parameter + +```py +# Python code + +def required(a: int): + pass +``` + +```txt +// Simple-ML code + +fun required(a: Int) +``` + +#### Optional Parameter + +```py +# Python code + +def optional(a: int = 1): + pass +``` + +```txt +// Simple-ML code + +fun optional(a: Int = 1) +``` + +#### Variadic Parameter + +```py +# Python code + +def variadic(*a: int): + pass +``` + +```txt +// Simple-ML code + +fun variadic(vararg a: Int) +``` + +### Matching Default Value + +Most commonly, default values in Python are literals, since default values are only evaluated once in Python rather than every time the function is called. The following table shows how Simple-ML literals and Python literals correspond: + +| Simple-ML Literal | Python Literal | +|---------------------------------------|------------------------| +| `1` ([int][int-literals]) | `1` | +| `1.0` ([float][float-literals]) | `1.0` | +| `"hello"` ([string][string-literals]) | `"hello"` or `'hello'` | +| `false` ([boolean][boolean-literals]) | `False` | +| `true` ([boolean][boolean-literals]) | `True` | +| `null` ([null][null-literals]) | `None` | + +[types]: ./types.md +[types-python]: ./types.md#corresponding-python-code +[steps]: ../workflow-language/steps.md +[calls]: ../workflow-language/expressions.md#calls +[stub-language]: ../stub-language/README.md +[literals]: ../workflow-language/expressions.md#literals +[int-literals]: ../workflow-language/expressions.md#int-literals +[float-literals]: ../workflow-language/expressions.md#float-literals +[string-literals]: ../workflow-language/expressions.md#string-literals +[boolean-literals]: ../workflow-language/expressions.md#boolean-literals +[null-literals]: ../workflow-language/expressions.md#null-literal +[python-keyword-only]: https://peps.python.org/pep-3102/ +[python-positional-only]: https://peps.python.org/pep-0570/ \ No newline at end of file diff --git a/docs/DSL/tutorial/common/results.md b/docs/DSL/tutorial/common/results.md new file mode 100644 index 000000000..46e18416f --- /dev/null +++ b/docs/DSL/tutorial/common/results.md @@ -0,0 +1,70 @@ +# Results + +_Results_ define the outputs of some declaration when it is [called][calls]. Here is an example: + +```txt +result: Int +``` + +Here is a breakdown of the syntax: +* The name of the result (here `result`). This can be any combination of upper- and lowercase letters, underscores, and numbers, as long as it does not start with a number. However, we suggest to use `lowerCamelCase` for the names of parameters. +* A colon. +* The [type][types] of the parameter (here `Int`). + +## Complete Example + +Let us now look at a full example of a [step][steps] called `doSomething` with two results: + +```txt +step doSomething() -> (result1: Int, result2: Boolean) { + // ... +} +``` + +The interesting part is the list of results, which uses the following syntactic elements: +* An arrow `->`. +* An opening parenthesis. +* A list of results, the syntax is as described above. They are separated by commas. A trailing commas is permitted. +* A closing parenthesis. + +## Shorthand Version: Single Result + +In case that the callable produces only a single result, we can omit the parentheses. The following two declarations are, hence, equivalent: + +```txt +step doSomething1() -> (result: Int) {} +``` + +```txt +step doSomething2() -> result: Int {} +``` + +## Shorthand Version: No Results + +In case that the callable produces no results, we can usually omit the entire results list. The following two declarations are, hence equivalent: + +```txt +step doSomething1() -> () {} +``` + +```txt +step doSomething2() {} +``` + +The notable exception are [callable types][callable-types], where the result list must always be specified even when it is empty. + +## Corresponding Python Code + +**Note:** This section is only relevant if you are interested in the [stub language][stub-language]. + +Results must be ordered the same way in Python as they are in Simple-ML. Moreover, the Simple-ML type of a result should capture the possible values of this result accurately. Ideally, the Python result should also have a matching [type hint][types-python]. + +Since Python results do not have a name, the names of Simple-ML results can be arbitrary. Naturally, a name should be chosen that captures the purpose of the result. + + +[stub-language]: ../stub-language/README.md +[types]: ./types.md +[types-python]: ./types.md#corresponding-python-code +[callable-types]: ./types.md#callable-type +[steps]: ../workflow-language/steps.md +[calls]: ../workflow-language/expressions.md#calls diff --git a/docs/DSL/tutorial/common/types.md b/docs/DSL/tutorial/common/types.md new file mode 100644 index 000000000..643a8333a --- /dev/null +++ b/docs/DSL/tutorial/common/types.md @@ -0,0 +1,429 @@ +# Types + +Types describe the values that a declaration can accept. Simple-ML has various categories of types, which are explained in this document. + +## Categories of Types + +### Named Types + +_Named types_ either denote that a declaration must be an instance of a [class][classes] or one of its [subclasses][subclassing], or an instance of a [variant][variants] of an [enum][enums]. In either case the syntax of the type is just the name of the [class][classes] or the [enum][enums] respectively. + +#### Class Types + +A declaration with a _class type_ must be an instance of a [class][classes] or one of its [subclasses][subclassing]. Let us use the following [classes][classes] for our example: + +``` +class SomeClass + +class SomeSubclass sub SomeClass +``` + +To denote that a declaration accepts instances of `SomeClass` and its [subclass][subclassing] `SomeSubclass`, we write the name of the class as the type: + +``` +SomeClass +``` + +##### Nullable Class Types + +The value `null` (see [null][null-literal]) deserves special treatment since it is not possible to operate on it in the same manner as on proper instances of a [class][classes]. For this reason `null` cannot be assigned to declarations with class types such as `SomeClass`. + +To specifically allow `null` as a value, simply add a question mark to the named type: + +``` +SomeClass? +``` + +#### Enum Types + +A declaration with an _enum type_ must be one of the [variants][variants] of the [enum][enums]. Let us use the following [enum][enums] for our example: + +``` +enum SomeEnum { + SomeEnumVariant, + SomeOtherEnumVariant(count: Int) +} +``` + +To denote that a declaration accepts instances of any [variant][variants] of `SomeEnum`, use the name of the enum as the type: + +``` +SomeEnum +``` + +This type expects either the value `SomeEnum.SomeEnumVariant` (see [member access][member-accesses]) or anything constructed from the [variant][variants] `SomeOtherEnumVariant` such as `SomeEnum.SomeOtherEnumVariant(3)`. + +#### Type Arguments + +**Note:** This is an advanced section. Feel free to skip it initially. + +If a declaration has [type parameters][type-parameters] we need to assign all of them when we use the declaration as a named type. This assignment happens in the form of _type arguments_. We explain this using the following declaration: + +``` +class SomeSpecialList +``` + +When we use this [class][classes] as a named type, we need to specify the value for the [type parameter][type-parameters] `T`, which is supposed to denote the type of the elements in the list.Similar to [calls][calls], we can either use _positional type arguments_ or _named type arguments_. + +In the case of positional type arguments, they are mapped to [type parameters][type-parameters] by position, i.e. the first type argument is assigned to the first [type parameter][type-parameters], the second type argument is assigned to the second [type parameter][type-parameters] and so forth. + +If a positional type argument is used, we just write down its value. The value is either +* a [type projection](#type-projection), or +* a [star projection](#star-projection). + +For example, if we expect a list of integers, we could use the following type: + +``` +SomeSpecialList +``` + +Let us break down the syntax: +* The usual named type (here `SomeSpecialList`). +* Opening angle bracket. +* A positional type argument (here `Int`). +* A closing angle bracket. + +When a named type argument is used, we explicitly specify the [type parameter][type-parameters] that we want to assign. This allows us to specify them in any order. It can also improve the clarity of the code since the meaning of the type argument becomes more apparent. Here is the type for our list of integers when a named argument is used: + +``` +SomeSpecialList +``` + +These are the syntactic elements: +* The usual named type (here `SomeSpecialList`). +* Opening angle bracket. +* A named type argument (here `T = Int`). This in turn consists of + * The name of the [type parameter][type-parameters] (here `T`) + * An equals sign. + * The value of the type argument, which is still either a [type projection](#type-projection), or a [star projection](#star-projection). +* A closing angle bracket. + +Within a list of type arguments both positional and named type arguments can be used. However, after the first named type arguments all type arguments must be named. + +Let us finally look at how multiple type arguments are passed. For this we use the following declaration: + +``` +class SomeSpecialMap +``` + +This [class][classes] has to [type parameters][type-parameters], namely `K` and `V`, which must both be set if we use this [class][classes] as a named type. + +Here is a valid use: + +``` +SomeSpecialMap +``` + +We will again go over the syntax: + +* The usual named type (here `SomeSpecialMap`). +* An opening angle bracket. +* The list of type arguments. Each element is either a positional or a named type argument (see above). Individual elements are separated by commas. A trailing comma is allowed +* A closing angle bracket. + +We will now look at the values that we can pass within type arguments. + +##### Type Projection + +The most basic case is that we pass a concrete type as the value. We have already seen this in the example above where we constructed the type for a list of integers: + +``` +SomeSpecialList +``` + +The value of the type argument is just another named type (here `Int`). + +###### Use-Site Variance + +It is also possible to set the [variance][variance] of a [type parameter][type-parameters] at the use-site, i.e. where we use the containing declaration as a named type. This is only possible, however, if we did not [specify the variance at the declaration-site][declaration-site-variance]. + +Covariance is denoted by the keyword `out`. If the variance of a [type parameter][type-parameters] is set to `out`, we can only access methods of the class that only use the [type parameter][type-parameters] in the out-position, i.e. as [results][results]. Methods that use the [type parameter][type-parameters] in the in-position, i.e. as [parameters][parameters] are hidden. Here is an example for the syntax: + +``` +SomeSpecialList +``` + +The key element here is the keyword `out` that is added to the type argument. This essentially creates a list of integers that can be read from but that cannot be written to. + +Contravariance is denoted by the keyword `in`. If the variance of a [type parameter][type-parameters] is set to `in`, we can only access methods of the class that only use the [type parameter][type-parameters] in the in-position, i.e. as [parameters][parameters]. Methods that use the [type parameter][type-parameters] in the out-position, i.e. as [results][results] are hidden. Here is an example of the syntax: + +``` +SomeSpecialList +``` + +The key element here is the keyword `in` that is added to the type argument. Here we essentially create a list of integers that can be written to but not read from. + +##### Star Projection + +If we do not want to specify a value for a [type parameter][type-parameters] and just accept everything, we can use a _star projection_. Here is the syntax: + +``` +SomeSpecialList<*> +``` + +It consists only of the `*`, which we use as the value of the type argument. + +The star projection is usually equivalent to the type projections +* `out Any?` (`Any?` is the supertype of everything), or +* `in Nothing` (`Nothing` is the subtype of everything). + +If the [type parameter][type-parameters] has [bounds][type-parameter-bounds], however, the star projection denotes that any type within the [bounds][type-parameter-bounds] is acceptable. + +### Member Types + +A member type is essentially the same as a [named type](#named-types) with the difference that the declaration we refer to is nested inside [classes][classes] or [enums][enums]. + +#### Class Member Types + +We begin with nested classes and use these declarations to illustrate the concept: + +``` +class SomeOuterClass { + class SomeInnerClass +} +``` + +To specify that a declaration accepts instances of `SomeInnerClass` or its [subclasses][subclassing], use the following member type: + +``` +SomeOuterClass.SomeInnerClass +``` + +This has the following syntactic elements: +* Name of the outer [class][classes] (here `SomeOuterClass`). +* A dot. +* Name of the inner [class][classes] (here `SomeInnerClass`). + +Classes can be nested multiple levels deep. In this case, use a member access for each level. Let us use the following declarations to explain this: + +``` +class SomeOuterClass { + class SomeMiddleClass { + class SomeInnerClass + } +} +``` + +To specify that a declaration accepts instances of `SomeInnerClass`, or its [subclasses][subclassing], use the following member type: + +``` +SomeOuterClass.SomeMiddleClass.SomeInnerClass +``` + +If any referenced class has [type parameters][type-parameters] these must be specified by [type arguments](#type-arguments). For this we use these declarations: + +``` +class SomeOuterClass { + class SomeInnerClass +} +``` + +To specify that a declaration accepts instances of `SomeInnerClass` where all type parameters are set to `Int`, or its [subclasses][subclassing], use the following member type: + +``` +SomeOuterClass.SomeInnerClass +``` + +Finally, as with [named types](#named-types), `null` is not an allowed value by default. To allow it, add a question mark at the end of the member type. This can be used independently from [type arguments](#type-arguments): + +``` +SomeOuterClass.SomeInnerClass? +``` + +#### Enum Variant Types + +Member types are also used to specify that a declaration is an instance of a single [variant][variants] of an [enum][enums]. For this, we use the following declarations: + +``` +enum SomeEnum { + SomeEnumVariant(count: Int), + SomeOtherEnumVariant +} +``` + +To allow only instances of the [variant][variants] `SomeEnumVariant`, use the following member type: + +``` +SomeEnum.SomeEnumVariant +``` + +Let us take apart the syntax: +* The name of the [enum][enums] (here `SomeEnum`). +* A dot. +* The name of the [enum variant][variants] (here `SomeEnumVariant`). + +Identical to [class member types](#class-member-types), all [type parameters][type-parameters] of the [enum variant][variants] must be assigned by [type arguments](#type-arguments). We use these declarations to explain the concept: + +``` +enum SomeEnum { + SomeEnumVariant(value: T), + SomeOtherEnumVariant +} +``` + +To now allow only instances of the [variant][variants] `SomeEnumVariant` with `Int` values, use the following member type: + +``` +SomeEnum.SomeEnumVariant +``` + +### Union Types + +If a declaration can have one of multiple types you can denote that with a _union type_: + +``` +union +``` + +Here is a breakdown of the syntax: +* The keyword `union`. +* An opening angle bracket. +* A list of types, which are separated by commas. A trailing comma is allowed. +* A closing angle bracket + +Note that it is preferable to write the common superclass if this is equivalent to the union type. For example, `Number` has the two subclasses `Int` and `Float`. Therefore, it is usually better to write `Number` as the type rather than `union`. Use the union type only when you are not able to handle the later addition of further subclasses of `Number` other than `Int` or `Float`. + +### Callable Types + +A _callable type_ denotes that only values that can be [called][calls] are accepted. This includes: +* [class constructors][class-constructors] +* [constructors of enum variants][enum-variant-constructors] +* [methods][methods] +* [global functions][global-functions] +* [steps][steps] +* [lambdas][lambdas] + +Additionally, a callable types specifies the names and types of parameters and results. Here is the most basic callable type that expects neither parameters nor results: + +``` +() -> () +``` + +Let us break down the syntax: +* A pair of parentheses, which is the list of expected [parameters][parameters]. +* An arrow `->`. +* A pair of parentheses, which is the list of expected [results][results]. + +We can now add some expected [parameters][parameters]: + +``` +(a: Int, b: Int) -> () +``` + +These are the syntactic elements: +* [Parameters][parameters] are written in the first pair of parentheses. +* For each [parameter][parameters], specify: + * Its name. + * A colon. + * Its type. +* Separate [parameters][parameters] by commas. A trailing comma is permitted. + +Finally, we can add some expected [results][results]: + +``` +(a: Int, b: Int) -> (r: Int, s: Int) +``` + +The syntax is reminiscent of the notation for [parameters][parameters]: +* [Results][results] are written in the second pair of parentheses. +* For each [result][results], specify: + * Its name. + * A colon. + * Its type. +* Separate [result][results] by commas. A trailing comma is permitted. + +If exactly one result is expected, the surrounding parentheses may be also removed: + +``` +(a: Int, b: Int) -> r: Int +``` + +### Parenthesized Types + +To improve clarity, parts of a type or the entire type can be enclosed in parentheses. The parentheses have no special meaning and are just meant as a visual guide. Here is an example: + +``` +(Int) +``` + +## Corresponding Python Code + +**Note:** This section is only relevant if you are interested in the [stub language][stub-language]. + +Optionally, [type hints][type-hints] can be used in Python to denote the type of a declaration. This is generally advisable, since IDEs can use this information to offer additional feature, like improved refactorings. Moreover, static type checker like [mypy][mypy] can detect misuse of an API without running the code. We will now briefly describe how to best use Python's [type hints][type-hints] and explain how they relate to Simple-ML types. + +First, to get [type hints][type-hints] in Python closer to the expected behavior, add the following import to your Python file: + +```py +from __future__ import annotations +``` + +Also add the following import, which brings the declarations that are used by the [type hints][type-hints] into scope. You can remove any declaration you do not need: + +```py +from typing import Callable, Optional, Tuple, TypeVar, Union +``` + +The following table shows how Simple-ML types can be written as Python [type hints][type-hints]: + +| Simple-ML Type | Python Type Hint | +|----------------------------------------|--------------------------------------------------------------------| +| `Boolean` | `bool` | +| `Float` | `float` | +| `Int` | `int` | +| `String` | `str` | +| `SomeClass` | `SomeClass` | +| `SomeEnum` | `SomeEnum` | +| `SomeClass?` | `Optional[SomeClass]` | +| `SomeEnum?` | `Optional[SomeEnum]` | +| `SomeSpecialList` | `SomeSpecialList[int]` | +| `SomeOuterClass.SomeInnerClass` | `SomeOuterClass.SomeInnerClass` | +| `SomeEnum.SomeEnumVariant` | `SomeEnum.SomeEnumVariant` | +| `union` | `Union[str, int]` | +| `(a: Int, b: Int) -> r: Int` | `Callable[[int, int], int]` | +| `(a: Int, b: Int) -> (r: Int, s: Int)` | `Callable[[int, int], Tuple[int, int]]` | +| `(SomeClass)` | No exact equivalent. Convert the type without parentheses instead. | + +Most of these are rather self-explanatory. We will, however, cover the translation of [callable types](#callable-types) in a little more detail: In Python, the type hint for a callable type has the following general syntax: + +``` +Callable[, ] +``` + +To get the ``, simply +1. convert the types of the parameters to their Python syntax, +2. separate them all by commas, +3. surround them by square brackets. + +Getting the ` depends on the number of results. If there is only a single result, simply write down its type. If there are multiple types, do this instead: +1. convert the types of the results to their Python syntax, +2. separate them all by commas, +3. add the prefix `Tuple[`, +4. add the suffix `]`. + +[variance]: ./variance.md +[parameters]: ./parameters.md +[results]: ./results.md + +[stub-language]: ../stub-language/README.md +[classes]: ../stub-language/classes.md +[subclassing]: ../stub-language/classes.md#subclassing +[enums]: ../stub-language/enumerations.md +[variants]: ../stub-language/enumerations.md#enum-variants +[type-parameters]: ../stub-language/type-parameters.md +[type-parameter-bounds]: ../stub-language/type-parameters.md#bounds +[declaration-site-variance]: ../stub-language/type-parameters.md#declaration-site-variance +[class-constructors]: ../stub-language/classes.md +[enum-variant-constructors]: ../stub-language/enumerations.md#constructors +[methods]: ../stub-language/classes.md#defining-methods +[global-functions]: ../stub-language/global-functions.md + + +[member-accesses]: ../workflow-language/expressions.md#member-access-of-enum-variants +[null-literal]: ../workflow-language/expressions.md#null-literal +[calls]: ../workflow-language/expressions.md#calls +[steps]: ../workflow-language/steps.md +[lambdas]: ../workflow-language/expressions.md#lambdas + +[mypy]: http://mypy-lang.org/ +[type-hints]: https://docs.python.org/3/library/typing.html diff --git a/docs/DSL/tutorial/common/variance.md b/docs/DSL/tutorial/common/variance.md new file mode 100644 index 000000000..122a8d3e7 --- /dev/null +++ b/docs/DSL/tutorial/common/variance.md @@ -0,0 +1,86 @@ +# Variance + +**Note:** This is an advanced section. Feel free to skip it initially. + +Variance deals with the question which generic types are compatible with each other. We explain this concept using the following [class][classes]: + +``` +class Stack(vararg initialElements: T) { + fun push(element: T) + + fun pop() -> element: T +} +``` + +The [class][classes] is called `Stack` and has a single [type parameter][type-parameters], which is supposed to the denote the [type][types] of the elements of the stack. With its [constructor], we can specify the initial elements of the stack. Moreover, two [methods][methods] are defined on the stack: +* `push` is supposed to add a new element to the top of the stack. +* `pop` is supposed to remove and return the topmost element of the stack. + +We will now try to answer the following two questions: +1. If `A` is a subclass of `B`, can we assign `Stack` to `Stack`? +2. If `B` is a subclass of `A`, can we assign `Stack` to `Stack`? + +## Invariance + +By default, the answer to both questions is "no". The reason for this is that it can allow illegal behavior: + +Say, we expect a `Stack`, but pass a `Stack` (`Int` is a subclass of `Number`). If we can treat the `Stack` as a `Stack`, we are also allowed to add values of type `Float` to it. This would also change the original `Stack`, which now contains illegal floating point values. + +Now, imagine we a `Stack`, but pass a `Stack` (`Number` is a subclass of `Any`). If we can treat the `Stack` as a `Stack`, we could read values from the stack that are not numbers, for example strings, since the original stack can contain `Any` value. + +To sum this up, we cannot assign `Stack` to `Stack` if `A` is a subclass of `B` because we might write to the `Stack` and alter the original `Stack` in an illegal way. Likewise, we cannot assign `Stack` to `Stack` if `B` is a subclass of `A` because we might read something from the `Stack` that is not of type `B`. + +We say, the [type parameter][type-parameters] `T` of the [class][classes] is invariant. It must be matched exactly. The conditions we describe above, however, already give us the information under which circumstances we can loosen this requirement. + +## Covariance + +We now want a `Stack` to be assignable to a `Stack` if `A` is a subclass of `B`. This behavior is called _covariance_ since the type compatibility relation between `Stack` and `Stack` points in the same direction as the type compatibility relation between `A` and `B`. + +As outlined above, we can only allow covariance if we forbid writing access. This means that a [type parameter][type-parameters] that is covariant can only be used for reading. Concretely, a covariant [type parameter][type-parameters] can only be used as the type of a [result][results] not the type of a [parameter][parameters]. We also say the [type parameter][type-parameters] can only be used in the _out-position_ (i.e. as output), which motivates the keyword `out` to denote covariance (see Section [Specifying Variance](#specifying-variance)). + +In the `Stack` example, we can make the [class][classes] covariant by adding the keyword `out` to the [type parameter][type-parameters] `T` and removing the writing [method][methods] `push`: + +``` +class Stack { + fun pop() -> element: T +} +``` + +## Contravariance + +We now want a `Stack` to be assignable to a `Stack` if `B` is a subclass of `A`. This behavior is called _contravariance_ since the type compatibility relation between `Stack` and `Stack` points in the opposite direction as the type compatibility relation between `A` and `B`. + +As outlined above, we can only allow contravariance if we forbid reading access. This means that a [type parameter][type-parameters] that is contravariant can only be used for writing. Concretely, a contravariant [type parameter][type-parameters] can only be used as the type of a [parameter][parameters] not the type of a [result][results]. We also say the [type parameter][type-parameters] can only be used in the _in-position_ (i.e. as input), which motivates the keyword `in` to denote contravariance (see Section [Specifying Variance](#specifying-variance)). + +In the `Stack` example, we can make the [class][classes] contravariant by adding the keyword `in` to the [type parameter][type-parameters] `T` and removing the reading [method][methods] `pop`: + +``` +class Stack { + fun push(element: T) +} +``` + +## Specifying Variance + +The variance of a [type parameter][type-parameters] can either be declared at its [declaration site][declaration-site-variance] or its [use site][use-site-variance]. If it is specified already at the [declaration site][declaration-site-variance], however, [use-site variance][use-site-variance] is no longer available. + +The following table sums up the syntax of [declaration-site variance][declaration-site-variance], where the [class][classes] declaration is changed, and [use-site variance][use-site-variance], where the [type arguments][type-arguments] passed by the [named type][named-types] Refer to the linked documents for more details. + +| Desired Variance | Declaration Site | Use Site | +|------------------|------------------|------------| +| Invariant | `class Stack` | `Stack` | +|Covariant|`class Stack`|`Stack` +|Contravariant|`class Stack`|`Stack`| + +[types]: ./types.md +[named-types]: ./types.md#named-types +[type-arguments]: ./types.md#type-arguments +[use-site-variance]: ./types.md#use-site-variance +[parameters]: ./parameters.md +[results]: ./results.md + +[classes]: ../stub-language/classes.md +[methods]: ../stub-language/classes.md#defining-methods +[subclassing]: ../stub-language/classes.md#subclassing +[type-parameters]: ../stub-language/type-parameters.md +[declaration-site-variance]: ../stub-language/type-parameters.md#declaration-site-variance diff --git a/docs/DSL/tutorial/stub-language/README.md b/docs/DSL/tutorial/stub-language/README.md new file mode 100644 index 000000000..feef403be --- /dev/null +++ b/docs/DSL/tutorial/stub-language/README.md @@ -0,0 +1,21 @@ +# Stub Language + +The stub language is the part of the Simple-ML DSL that is used to integrate functions written in Python into Simple-ML. It describes which functionality that is implemented in Python should be publicly available and how it must be used. If do not want to make functionality that exists in Python accessible in Simple-ML, simply do not write stubs for them. The stub language has the following concepts: + +* [Packages][packages] help avoid conflicts that could arise if two declarations have the same name. +* [Imports][imports] make declarations in other packages accessible. +* [Classes][classes] define custom datatypes that bundle data and operations on this data. +* [Global Functions][global-functions] define operations that do not belong to a specific [class][classes]. +* [Enumerations][enumerations] define custom datatypes with a fixed set of possible variants. +* [Annotations][annotations] attach additional metainformation to declarations. +* [Comments][comments] document the code. + +Files that use the stub language must have the extension `.smlstub`. + +[packages]: ../common/packages.md +[imports]: ../common/imports.md +[classes]: ./classes.md +[global-functions]: ./global-functions.md +[enumerations]: ./enumerations.md +[annotations]: ./annotations.md +[comments]: ../common/comments.md diff --git a/docs/DSL/tutorial/stub-language/annotations.md b/docs/DSL/tutorial/stub-language/annotations.md new file mode 100644 index 000000000..62cb56da9 --- /dev/null +++ b/docs/DSL/tutorial/stub-language/annotations.md @@ -0,0 +1,109 @@ +# Annotations + +Annotations attach additional metainformation to declarations. Annotations must first be [declared](#declaring-an-annotation), so Simple-ML knows the annotation exists and which inputs are expected. Afterwards, annotations can be [called](#calling-an-annotation), which is the step that truly attaches metainformation to declarations. + +## Declaring an Annotation + +### Minimal Example + +Let's look at a minimal example of an annotation: + +```txt +annotation OnlyForExperts +``` + +This declaration of an annotation has the following syntactic elements: +* The keyword `annotation`. +* The name of the annotation, here `OnlyForExperts`. This can be any combination of upper- and lowercase letters, underscores, and numbers, as long as it does not start with a number. However, we suggest to use `UpperCamelCase` for the names of annotations. + +### Parameters + +For the annotation `OnlyForExperts`, no more inputs are required. It is only used to mark declarations. However, say we want to assign a category to a declaration. We could define individual annotations for each category, such as: + +```txt +annotation Model +annotation Data +``` + +However, these annotations are difficult to process since we need to maintain a list of annotations that define such a category. Instead, it is preferable to define a single annotation called `Category` and make it configurable by adding [parameters][parameters] (inputs). Parameters must be declared in the header of the annotation, so [callers](#calling-an-annotation) know they are expected to pass them as an argument. + +In the following example, we define the complete `Category` annotation with a single parameter with name `category` and [type][types] `String`. + +```txt +annotation Category(category: String) +``` + +As we can see from the `OnlyForExperts` example, we can omit the entire parameter list, if the annotation has no parameters. More information about parameters can be found in the [linked document][parameters]. + +## Calling an Annotation + +To attach metainformation to a declaration, the annotation must be called on that declaration. We name the annotated declaration the _target_ of the annotation. Possible targets are + +* [Annotations](#declaring-an-annotation) (yes, annotations can be the target of annotation calls) +* [Attributes][attributes] +* [Classes][classes] +* Compilation units (entire files) +* [Enums][enums] +* [Enum variants][enum-variants] +* [Global functions][global-functions] / [methods][methods] +* [Parameters][parameters] +* [Results][results] +* [Steps][steps] +* [Type parameters][type-parameters] +* [Workflows][workflows] + +The valid targets of an annotation can be restricted with the [`Target`][simpleml-lang-target] annotation. By default all targets are allowed. Likewise, an annotation can only be called once on the same declaration by default, unless the annotation is marked as[Repeatable][simpleml-lang-repeatable]. + +Annotation calls are always located right in front of their target. Exception: In the case of compilations units they are located at the very top of the file. Here is an example that demonstrates how to call the annotation `OnlyForExperts` that we defined above on a [class][classes]: + +```txt +@OnlyForExperts +class VerySpecificMLModel +``` + +Here is a breakdown of the syntax: +* An `@`. +* The name of the called annotation (here `OnlyForExperts`). + +The code `class VerySpecificMLModel` is **not** part of the annotation call but simply the [class declaration][classes] that is targeted by the annotation call. Since the annotation `OnlyForExperts` does not specify parameters, we also need not pass arguments and can omit the entire argument list. + +For an annotation with parameters, such as the `Category` annotation that we defined above, we must pass arguments. The same syntax is used for arguments of annotation calls as for normal [calls][calls]. We can use positional arguments: + +```txt +@Category("model") +class VerySpecificMLModel +``` + +Or we can use named arguments: + +```txt +@Category(category="model") +class VerySpecificMLModel +``` + +The same [restrictions to arguments][argument-restrictions] as for [calls][calls] also apply here. + +## Built-in Annotations + +The package `simpleml.lang` contains several annotations that are processed by Simple-ML. Refer to the [API documentation][simpleml-lang] for more details. Particularly important are the annotations + +* [`Target`][simpleml-lang-target], which can restrict the possible targets of an annotation, and +* [`Repeatable`][simpleml-lang-repeatable], which allows an annotation to be called multiple times on the same declaration. + +[parameters]: ../common/parameters.md +[types]: ../common/types.md +[attributes]: ./classes.md#defining-attributes +[classes]: ./classes.md#defining-classes +[enums]: ./enumerations.md#declaring-an-enumeration +[enum-variants]: ./enumerations.md#enum-variants +[global-functions]: ./global-functions.md +[methods]: ./classes.md#defining-methods +[results]: ../common/parameters.md +[steps]: ../workflow-language/steps.md +[type-parameters]: ./type-parameters.md +[workflows]: ../workflow-language/workflows.md +[simpleml-lang]: ../../../Stdlib/API/simpleml_lang.md +[simpleml-lang-repeatable]: ../../../Stdlib/API/simpleml_lang.md#annotation-repeatable +[simpleml-lang-target]: ../../../Stdlib/API/simpleml_lang.md#annotation-target +[calls]: ../workflow-language/expressions.md#calls +[argument-restrictions]: ../workflow-language/expressions.md#restrictions-for-arguments diff --git a/docs/DSL/tutorial/stub-language/classes.md b/docs/DSL/tutorial/stub-language/classes.md new file mode 100644 index 000000000..e34a7902d --- /dev/null +++ b/docs/DSL/tutorial/stub-language/classes.md @@ -0,0 +1,69 @@ +# Classes + +Classes can be used to define custom datatypes that bundle data and operations on this data. + +## Defining Classes + +To define a class we use the following syntax: + +* The keyword `class`. +* The name of the class ("Lasso" in the following examples). +* To define the constructor of the class we list the _parameters_ (inputs) necessary to create an instance. This list is enclosed in parentheses and separated by commas, `(regularizationStrength: Float)` in the following snippet. For each parameter, we list the name of the parameter followed by a colon and its type. +* Finally, we have the _body_ of the class, which lists the members ([attributes](#defining-attributes) for data and [methods](#defining-methods) for operations on this data, as explained in the following sections) of the class enclosed by curly braces. + +``` +class Lasso(regularizationStrength: Float) { + // ... +} +``` + +### Defining Attributes + +The data of a class is called _attributes_. We differentiate _static attributes_, which are available on the class itself, and _instance attributes_, which are available on instances on the class. Here is the syntax to define attributes: + +* The modifier keyword `static` to define a static attribute (no modifier needed to define an instance attribute). +* The keyword `attr`. +* The name of the attribute ("regularizationStrength" in the following example). +* A colon followed by the type of the attribute ("Float" in the next example). + +``` +class Lasso(regularizationStrength: Float) { + attr regularizationStrength: Float + + // ... +} +``` + +### Defining Methods + +_Methods_ represent operations on the attributes of a class. As with attributes we differentiate _static methods_, which are accessible from the class itself, and _instance methods_, which are available on instances of the class. The syntax to define methods is as follows: + +* The modifier keyword `static` to define a static method (no modifier needed to define an instance method). +* The keyword `fun`. +* The name of the attribute ("fit" in the following example). +* The list of _parameters_ (inputs) enclosed in parentheses and separated by commas (`(features: Table, target: Table)` in the following snippet). For each parameter we list the name of the parameter followed by a colon and its type. +* Optionally, we can list the _results_ (outputs) after the symbol `->`. If this section is missing it means the method does not produce results. The list of results is again enclosed in parentheses and we use commas to separate the entries. If there is exactly one result we can omit the parentheses (see `-> trainedModel: Lasso` in the following example). For each result we specify its name followed by a colon and its type. + +``` +class Lasso(regularizationStrength: Float) { + attr regularizationStrength: Float + + fun fit(features: Table, target: Table) -> trainedModel: Lasso +} +``` + +## Constructors + +**TODO** + +## Subclassing + +**TODO** + +## Using a Class as a Type + +To express that the type of a declaration is some class we simply write the name of the class as the type. For example, we could declare a function that plots the line learned by a ridge regression model like this: + +``` +fun plotRidge(model: Ridge) +``` diff --git a/docs/DSL/tutorial/stub-language/enumerations.md b/docs/DSL/tutorial/stub-language/enumerations.md new file mode 100644 index 000000000..57b4c4bcb --- /dev/null +++ b/docs/DSL/tutorial/stub-language/enumerations.md @@ -0,0 +1,44 @@ +# Enumerations + +An enumeration is a datatype that can take a fixed, finite set of values. The [Ridge model of scikit-learn](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Ridge.html#sklearn.linear_model.Ridge) for instance allows the user to choose a solver by selecting one of the strings "auto", "svd", "cholesky", "lsqr", "sparse_cg", "sag", "saga". However, the string datatype does not prevent the user from passing another invalid string like "automatic", leading to unspecified behavior. To prevent this, in Simple-ML the solver is an enumeration. + +## Declaring an Enumeration + +The syntax to declare an enumeration is as follows: +* The keyword `enum`. +* The name of the enumeration. +* A list of the _instances_, i. e. the valid values of the enumeration enclosed in curly braces and separated by commas. + +Coming back to the ridge solver example from the introduction, we would implement this in Simple-ML as follows, so that only the seven specified values are valid instances of the datatype. + +``` +enum RidgeSolver { + AUTO, + SVD, + CHOLESKY, + LSQR, + SPARSE_CG, + SAG, + SAGA +} +``` + +## Enum Variants + +**TODO** + +### Constructors + +**TODO** + +## Using an Enumeration as a Type + +To express that the type of a declaration is an enumeration we simply write the name of the enumeration as the type. For example, when declaring a [class][classes] called "Ridge" for the ridge regression model we can declare a parameter of type "RidgeSolver" like this: + +``` +class Ridge(solver: RidgeSolver) { + // ... +} +``` + +[classes]: ./classes.md diff --git a/docs/DSL/tutorial/stub-language/global-functions.md b/docs/DSL/tutorial/stub-language/global-functions.md new file mode 100644 index 000000000..87dfa4a7a --- /dev/null +++ b/docs/DSL/tutorial/stub-language/global-functions.md @@ -0,0 +1,19 @@ +# Global Functions + +In order to use a method of a [class][classes] we first must get hold of an instance of this class. Global functions do not have this requirement, so they are prime candidates to implement global utility operations that exist independently of any class. + +## Defining a Global Function + +The syntax to define a global function is as follows: +* The keyword `fun` +* The name of the function ("loadDataset" in the following example) +* The list of _parameters_ (inputs) enclosed in parentheses and separated by commas (`(name: String)` in the following snippet). For each parameter we list the name of the parameter followed by a colon and its type. +* Optionally we can list the _results_ (outputs) after the symbol `->`. If this section is missing it means the global function does not produce results. The list of results is again enclosed in parentheses and we use commas to separate the entries. If there is exactly one result we can omit the parentheses (see `-> dataset: Dataset` in the following example). For each result we specify its name followed by a colon and its type. +* Note that global functions do **not** have a body since they are part of the [stub language][stub-language], which does not deal with implementation. + +``` +fun loadDataset(name: String) -> dataset: Dataset +``` + +[classes]: ./classes.md +[stub-language]: README.md diff --git a/docs/DSL/tutorial/stub-language/type-parameters.md b/docs/DSL/tutorial/stub-language/type-parameters.md new file mode 100644 index 000000000..6e85842ca --- /dev/null +++ b/docs/DSL/tutorial/stub-language/type-parameters.md @@ -0,0 +1,9 @@ +# Type Parameters + +**TODO** + +## Bounds + +## Declaration-Site Variance + +**TODO** diff --git a/docs/DSL/tutorial/workflow-language/README.md b/docs/DSL/tutorial/workflow-language/README.md new file mode 100644 index 000000000..cba2f9292 --- /dev/null +++ b/docs/DSL/tutorial/workflow-language/README.md @@ -0,0 +1,22 @@ +# Workflow Language + +The workflow language is the part of the Simple-ML DSL that is designed to solve specific machine learning problems. It has the following concepts: + +* [Packages][packages] help avoid conflicts that could arise if two declarations have the same name. +* [Imports][imports] make declarations in other packages accessible. +* [Workflows][workflows] define the entry point of a Machine Learning program. +* [Steps][steps] encapsulate parts of a Machine Learning program and make them reusable. +* [Statements][statements] are the instructions that are executed as part of a [workflow][workflows], [step][steps], or [block lambda][block-lambdas]. +* [Expressions][expressions] are computations that produce some value. +* [Comments][comments] document the code. + +Files that use the workflow language must have the extension `.smlflow`. + +[packages]: ../common/packages.md +[imports]: ../common/imports.md +[workflows]: ./workflows.md +[steps]: ./steps.md +[statements]: ./statements.md +[expressions]: ./expressions.md +[block-lambdas]: ./expressions.md#block-lambdas +[comments]: ../common/comments.md diff --git a/docs/DSL/tutorial/workflow-language/expressions.md b/docs/DSL/tutorial/workflow-language/expressions.md new file mode 100644 index 000000000..5adb762bd --- /dev/null +++ b/docs/DSL/tutorial/workflow-language/expressions.md @@ -0,0 +1,568 @@ +# Expressions + +Expressions are the parts of the [workflow language][workflow-language] that evaluate to some value. A multitude of different expression types known from other programming languages are supported by Simple-ML, from basic [literals](#literals) to [lambdas](#lambdas). + +## Literals + +Literals are the basic building blocks of expressions. They describe a fixed, constant value. + +### Int Literals + +Int literals denote integers. They use the expected syntax. For example, the integer three is written as `3`. + +### Float Literals + +Float literals denote floating point numbers. There are two ways to specify them: +* **Decimal form**: One half can be written as `0.5`. Note that neither the integer part nor the decimal part can be omitted, so `.5` and `0.` are syntax errors. +* **Scientific notation**: Writing very large or very small numbers in decimal notation can be cumbersome. In those cases, [scientific notation](https://en.wikipedia.org/wiki/Scientific_notation) is helpful. For example, one thousandth can be written in Simple-ML as `1.0e-3` or `1.0E-3`. You can read this as `1.0 × 10⁻³`. When scientific notation is used, it is allowed to omit the decimal part, so this can be shortened to `1e-3` or `1E-3`. + +### String Literals + +String literals describe text. Their syntax is simply text enclosed by double quotes: `"Hello, world!"`. Various special characters can be denoted with _escape sequences_: + +| Escape sequence | Meaning | +|-----------------|----------------------------------------------------------------------| +| `\b` | Backspace | +| `\t` | Tab | +| `\n` | New line | +| `\f` | Form feed | +| `\r` | Carriage return | +| `\"` | Double quote | +| `\'` | Single quote | +| `\\` | Backslash | +| `\{` | Opening curly brace (used for [template strings](#template-strings)) | +| `\uXXXX` | Unicode character, where `XXXX` is its hexadecimal index | + +String literals can contain also contain raw line breaks: + +``` +"Hello, + +world!" +``` + +In order to interpolate text with other computed values, use [template strings](#template-strings). + +### Boolean Literals + +To work with truthiness, Simple-ML has the two boolean literals `false` and `true`. + +### `null` Literal + +To denote that a value is unknown or absent, use the literal `null`. + +## Operations + +Operations are special functions that can be applied to one or two expressions. Simple-ML has a fixed set of operations that cannot be extended. We distinguish between +* prefix operations (general form ` `), and +* infix operations (general form ` `). + +### Operations on Numbers + +Numbers can be negated using the unary `-` operator: +* The integer negative three is `-3`. +* The float negative three is `-3.0`. + +The usual arithmetic operations are also supported for integers, floats and combinations of the two. Note that when either operand is a float, the whole expression is evaluated to a float. +* Addition: `0 + 5` (result is an integer) +* Subtraction: `6 - 2.9` (result is a float) +* Multiplication: `1.1 * 3` (result is a float) +* Division: `1.0 / 4.2` (result is a float) + +Finally, two numbers can be compared, which results in a boolean. The integer `3` for example is less than the integer `5`. Simple-ML offers operators to do such checks for order: + +* Less than: `5 < 6` +* Less than or equal: `1 <= 3` +* Greater than or equal: `7 >= 7` +* Greater than: `9 > 2` + +### Logical Operations + +To work with logic, Simple-ML has the two boolean literals `false` and `true` as well as operations to work with them: +* (Logical) **negation** (example `not a`): Output is `true` if and only if the operand is false: + +`not a` | false | true +--------|-------|------ +  | true | false + +* **Conjunction** (example `a and b`): Output is `true` if and only if both operands are `true`. Note that the second operand is always evaluated, even if the first operand is `false` and, thus, already determines the result of the expression. The operator is not short-circuited: + +`a and b` | false | true +----------|-------|------ +**false** | false | false +**true** | false | true + +* **Disjunction** (example `a or b`): Output is `true` if and only if at least one operand is `true`. Note that the second operand is always evaluated, even if the first operand is `true` and, thus, already determines the result of the expression. The operator is not short-circuited: + +`a or b` | false | true +----------|-------|----- +**false** | false | true +**true** | true | true + +### Equality Checks + +There are two different types of equality in Simple-ML, _identity_ and _structural equality_. Identity checks if two objects are one and the same, whereas structural equality checks if two objects have the same structure and content. Using a real world example, two phones of the same type would be structurally equal but not identical. Both types of equality checks return a boolean literal `true` if the check was positive and `false` if the check was negative. The syntax for these operations is as follows: + +* Identity: `1 === 2` +* Structural equality: `1 == 2` + +Simple-ML also has shorthand versions for negated equality checks which should be used instead of an explicit logical negation with the `not` operator: + +* Negated identity: `1 !== 2` +* Negated structural equality: `1 != 2` + +### Elvis Operator + +The elvis operator `?:` (given its name because it resembles Elvis's haircut) is used to specify a default value that should be used instead if the left operand is `null`. This operator is not short-circuited, so both operand are always evaluated. In the following example the whole expression evaluates to `nullableExpression` if this value is not `null` and to `42` if it is: + +``` +nullableExpression ?: 42 +``` + +## Template Strings + +[String literals](#string-literals) can only be used to denote a fixed string. Sometimes, however, parts of the string have to be computed and then interpolated into the remaining text. This is done with template strings. Here is an example: + +``` +"1 + 2 = {{ 1 + 2 }}" +``` + +The syntax for template strings is similar to [string literals](#string-literals): They are also delimited by double quotes, the text can contain escape sequences, and raw newlines can be inserted. The additional syntax are _template expressions_, which are any expression enclosed by `{{` and `}}`. There must be no space between the curly braces. + +These template expressions are evaluated, converted to a string and inserted into the template string at their position. The template string in the example above is, hence, equivalent to the [string literal](#string-literals) "1 + 2 = 3". + +## References + +References are used to refer to a declaration, such as a [class][classes] or a [placeholder][placeholders]. The syntax is simply the name of the declaration, as shown in the next snippet where we first declare a [placeholder][placeholders] called `one` and then refer to it when computing the value for the [placeholder][placeholders] called `two`: + +``` +val one = 1; +val two = one + one; +``` + +In order to refer to global declarations in other [packages][packages], we first need to [import][imports] them. + +## Calls + +Calls are used to trigger the execution of a specific action, which can, for example, be the creation of an instance of a [class][classes] or executing the code in a [step][steps]. Let's look at an example: + +First, we show the code of the [step][steps] that we want to call. + +``` +step createDecisionTree(maxDepth: Int = 10) { + // ... do something ... +} +``` + +This [step][steps] has a single [parameter][parameters] `maxDepth`, which must have [type][types] `Int`, and has the default value `10`. Since it has a default value, we are not required to specify a value when we call this [step][steps]. The most basic legal call of the [step][steps] is, thus, this: + +``` +createDecisionTree() +``` + +This calls the [step][steps] `createDecisionTree`, using the default `maxDepth` of `10`. + +The syntax consists of these elements: +* The _callee_ of the call, which is the expression to call (here a [reference](#references) to the [step][steps] `createDecisionTree`) +* The list of arguments, which is delimited by parentheses. In this case the list is empty, so no arguments are passed. + +If we want to override the default value of an optional [parameter][parameters] or if the callee has required [parameters][parameters], we need to pass arguments. We can either use _positional arguments_ or _named arguments_. + +In the case of positional arguments, they are mapped to parameters by position, i.e. the first argument is assigned to the first parameter, the second argument is assigned to the second parameter and so forth. We do this in the following example to set `maxDepth` to 5: + +``` +createDecisionTree(5) +``` + +The syntax for positional argument is simply the expression we want to pass as value. + +Named arguments, however, are mapped to parameters by name. On the one hand, this can improve readability of the code, since the meaning of a value becomes obvious. On the other hand, it allows to override only specific optional parameters and keep the rest unchanged. Here is how to set `maxDepth` to 5 using a named argument: + +``` +createDecisionTree(maxDepth = 5) +``` + +These are the syntactic elements: +* The name of the parameter for which we want to specify a value. +* An equals sign. +* The value to assign to the parameter. + +### Passing Multiple Arguments + +We now add another parameter to the `createDecisionTree` [step][steps]: + +``` +step createDecisionTree(isBinary: Boolean, maxDepth: Int = 10) { + // ... do something ... +} +``` + +This allows us to show how multiple arguments can be passed: + +``` +createDecisionTree(isBinary = true, maxDepth = 5) +``` + +We have already seen the syntax for a single argument. If we want to pass multiple arguments, we just separate them by commas. A trailing comma is allowed. + +### Restrictions For Arguments + +There are some restriction regarding the choice of positional vs. named arguments and passing arguments in general: +* For all [parameters][parameters] of the callee there must be at most one argument. +* For all [required parameters][required-parameters] there must be exactly one argument. +* After a named argument all arguments must be named. +* [Variadic parameters][variadic-parameters] can only be assigned by position. +### Legal Callees + +Depending on the callee, a call can do different things. The following table lists all legal callees and what happens if they are called: + +| Callee | Meaning | +|-------------------------------------|--------------------------------------------------------------------------------------------------------------------------------| +| [Class][classes] | Create a new instance of the class. The class must have a constructor to be callable. The call evaluates to this new instance. | +| [Enum Variant][enum-variants] | Creates a new instance of the enum variant. Enum variants are always callable. The call evaluates to this new instance. | +| [Global Function][global-functions] | Invokes the function and runs the associated Python code. The call evaluates to the result record of the function. | +| [Method][methods] | Invokes the method and runs the associated Python code. The call evaluates to the result record of the method. | +|[Step][steps]|Invokes the step and runs the Simple-ML code in its body. The call evaluates to the result record of the step.|| +|[Block Lambda](#block-lambdas)|Invokes the lambda and runs the Simple-ML code in its body. The call evaluates to the result record of the lambda.|| +|[Expression Lambda](#expression-lambdas)|Invokes the lambda and runs the Simple-ML code in its body. The call evaluates to the result record of the lambda.|| +|Declaration with [Callable Type][callable-types]|Call whatever the value of the declaration is. + +#### Result Record + +The term _result record_ warrants further explanation: A result record maps [results][results] of a +* [global function][global-functions], +* [method][methods], +* [step][steps], or +* [lambda](#lambdas) + +to their computed values. + +If the result record only has a single entry, its value can be accessed directly. Otherwise, the result record must be _deconstructed_ either by an [assignment][assignment-multiple-assignees] (can access multiple results) or by a [member access](#member-access-of-results) (can access a single result). + +## Member Accesses + +A member access is used to refer to members of a complex data structure such as +* a [class][classes], +* an [enum][enums], or +* the [result record](#result-record) of a [call](#calls). + +The general syntax of a member access is this: +``` +. +``` + +Here, the receiver is some expression (the legal choices are explained below), while the member is always a [reference](#references). + +### Member Access of Class Members + +To understand how we can access members of a [class][classes] we must first look briefly at a declaration of the [class][classes] we use in the following examples: + +``` +class DecisionTree() { + static attr verboseTraining: Boolean + + attr maxDepth: Int +} +``` + +This class has a static [attribute][attributes] called `verboseTraining`, which has type `Boolean`. Static means that the attribute is shared between all instances of the class and can be accessed on the class itself, rather than a specific instance. + +Moreover, the class has an instance [attribute][attributes]`maxDepth`, which is an integer. This must be accessed on a specific instance of the class. + +#### Member Access of Static Class Member + +Let us look at how to access the static [attribute][attributes] `verboseTraining` to retrieve its value: + +``` +DecisionTree.verboseTraining +``` + +These are the syntactic elements of this member access: +* The receiver, which is the name of the class (here `DecisionTree`) +* A dot. +* The name of the static member of the class (here `verboseTraining`) + +Note that we cannot access a static member from an instance of the class. We must use the class itself. + +#### Member Access of Instance Class Member + +Contrary to static member accesses, we can only access instance members on an instance of a class: + +``` +DecisionTree().maxDepth +``` + +We now take apart the syntax again: +* The receiver, here a [call](#calls) of the constructor of the class `DecisionTree`. This creates an instance of this class. +* A dot. +* The name of the instance member (here `maxDepth`). + +Note that instance members cannot be accessed from the class itself, but only from its instances. + +##### Null-Safe Member Access + +If an expression could be `null` it cannot be used as the receiver of a regular member access, since `null` does not have members. Instead a null-safe member access must be used. A null-safe member access evaluates to `null` if its receiver is `null`. Otherwise, it evaluates to the accessed member, just like a normal member access. + +The syntax is identical to a normal member access except that we replace the dot with the operator `?.`: + +``` +nullableExpression?.member +``` + +### Member Access of Enum Variants + +A member access can also be used to access the [variants][enum-variants] of an [enum][enums]. Here is the declaration of the [enum][enums] that we use in the example: + +``` +enum SvmKernel { + Linear, + RBF +} +``` + +This [enum][enums] is called `SvmKernel` and has the two [variants][enum-variants] `Linear` and `RBF`. + +We can access the [variant][enum-variants] `Linear` using this member access: + +``` +SvmKernel.Linear +``` + +These are the elements of the syntax: +* The receiver, which is the name of the [enum][enums] (here `SvmKernel`). +* A dot. +* The name of the [variant][enum-variants] (here `Linear`). + +This syntax is identical to the [member access of static class members](#member-access-of-static-class-member). + +### Member Access of Results + +If the [result record](#result-record) that is produced by a [call](#calls) has multiple results, we can use a member access to select a single one. Here is the [global function][global-functions] we use to explain this concept: + +``` +fun divideWithRemainder(dividend: Int, divisor: Int) -> (quotient: Int, remainder: Int) +``` + +The [global function][global-functions] `divideWithRemainder` has two [parameters][parameters], namely `dividend` and `divisor`, both of which have type `Int`. It produces two [results][results], `quotient` and `remainder`, which also have type `Int`. + +If we are only interested in the remainder of `12` divided by `5`, we can use a member access: + +``` +divideWithRemainder(12, 5).remainder +``` + +Here are the syntactic elements: +* The receiver, which is a [call](#calls). +* A dot. +* The name of the result (here `remainder`). + +While it is also possible to access the result by name if the [result record](#result-record) contains only a single entry, there is no need to do so, since this result can be used directly. If you still use a member access and the singular result of the call has the same name as an instance member of the corresponding class, the instance member wins. + +To explain this concept further, we need the following declarations: + +``` +class ValueWrapper { + attr value: Int +} + +fun createValueWrapper() -> value: ValueWrapper +``` + +We first declare a [class][classes] called `ValueWrapper`, which has an [attribute][attributes] `value` of type `Int`. Next, we declare a function, which is supposed to create an instance of the [class][classes] `ValueWrapper` and put it into the [result][results] `value`. + +Let us now look at this member access: + +``` +createValueWrapper().value +``` + +This evaluates to the [attribute][attributes], i.e. an integer, rather than the [result][results], which would be an instance of `ValueWrapper`. + +If you want the result instead, simply omit the member access: + +``` +createValueWrapper() +``` + +## Indexed Accesses + +An indexed access is currently only used to access one value assigned to a [variadic parameter][variadic-parameters]. In the following example, we use an index access to retrieve the first value that is assigned to the [variadic parameter][variadic-parameters] `values` of the step `printFirst`: + +``` +step printFirst(vararg values: Int) { + print(values[0]); +} +``` + +These are the elements of the syntax: +* The name of the variadic parameter (here `values`). +* An opening square bracket. +* The index, which is an expression that evaluates to an integer. The first element has index 0. +* A closing square bracket. + +Note that accessing a value at an index outside the bounds of the value list currently only raises an error at runtime. + +## Chaining + +Multiple [calls](#calls), [member accesses](#member-accesses), and [indexed accesses](#member-accesses) can be chained together. Let us first look at the declaration of the [class][classes] we need for the example: + +``` +class LinearRegression() { + fun drawAsGraph() +} +``` + +This is a [class][classes] `LinearRegression`, which has a constructor and an instance [method][methods] called `drawAsGraph`. + +We can then use those declarations in a [step][steps]: + +``` +step myStep(vararg regressions: LinearRegression) { + regressions[0].drawAsGraph(); +} +``` + +This step is called `myStep` and has a [variadic parameter][variadic-parameters] `regressions` of type `LinearRegression`. This means we can pass an arbitrary number of instances of `LinearRegression` to the step when we [call](#calls) it. + +In the body of the step we then +1. access the first instance that was pass using an [indexed access](#indexed-accesses), +2. access the instance method `drawAsGraph` of this instance using a [member access](#member-accesses), +3. [call](#calls) this method. + +## Lambdas + +If you want to write reusable blocks of code, use a [step][steps]. However, sometimes you need to create a highly application-specific callable that can be passed as argument to some function or returned as the result of a [step][steps]. We will explain this concept by filtering a list. Here are the relevant declarations: + +``` +class IntList { + fun filter(filterFunction: (element: Int) -> shouldKeep: Boolean) -> filteredList: IntList +} + +fun intListOf(vararg elements: Int) -> result: IntList +``` + +First, we declare a [class][classes] `IntList`, which has a single [method][methods] called `filter`. The `filter` method returns a single result called `filteredList`, which is a new `IntList`. `filteredList` is supposed to only contain the elements of the receiving `IntList` for which the `filterFunction` [parameter][parameters] returns `true`. + +Second, we declare a [global function][global-functions] `intListOf` that is supposed to wrap `elements` into an `IntList`. + +Say, we now want to keep only the elements in the list that are less than `10`. We can do this by declaring a [step][steps]: + +``` +step keepLessThan10(a: Int) -> shouldKeep: Boolean { + yield shouldKeep = a < 10; +} +``` + +Here is how to solve the task of keeping only elements below `10` with this [step][steps]: + +``` +intListOf(1, 4, 11).filter(keepLessThan10) +``` + +The [call](#calls) to `intListOf` is just there to create an `IntList` that we can use for filtering. The interesting part is the argument we pass to the `filter` [method][methods], which is simply a reference to the [step][steps] we declared above. + +The problem here is that this solution is very cumbersome and verbose. We need to come up with a name for a [step][steps] that we will likely use only once. Moreover, the step must declare the [types][types] of its [parameters][parameters] and its [results][results] in its header. Finally, the declaration of the step has to happen in a separate location then its use. We can solve those issues with lambdas. + +### Block Lambdas + +We will first rewrite the above solution using a _block lambda_, which is essentially a [step][steps] without a name and more concise syntax that can be declared where it is needed: + +``` +intListOf(1, 4, 11).filter( + (a) { yield shouldKeep = a < 10; } +) +``` + +While this appears longer than the solution with [steps][steps], note that it replaces both the declaration of the [step][steps] as well as the [reference](#references) to it. + +Here are the syntactic elements: +* A list of [parameters][parameters], which is enclosed in parentheses. Individual parameters are separated by commas. +* The _body_, which is a list of [statements][statements] enclosed in curly braces. Note that each [statement][statements] is terminated by a semicolon. + +The results of a block lambda are [declared in its body using assignments][assignments-to-block-lambda-results]. + +### Expression Lambdas + +Often, the body of a [block lambda](#block-lambdas) only consists of yielding a single result, as is the case in the example above. The syntax of [block lambdas](#block-lambdas) is quite verbose for such a common use-case, which is why Simple-ML has _expression lambdas_ as a shorter but less flexible alternative. Using an expression lambda we can rewrite the example above as + +``` +intListOf(1, 4, 11).filter( + (a) -> a < 10 +) +``` + +These are the syntactic elements: +* A list of [parameters][parameters], which is enclosed in parentheses. Individual parameters are separated by commas. +* An arrow `->`. +* The expression that should be returned. + +### Closures + +**Note:** This is advanced concept, so feel free to skip this section initially. + +Both [block lambdas](#block-lambdas) and [expression lambdas](#expression-lambdas) are closures, which means they remember the values of [placeholders][placeholders] and [parameters][parameters] that can be accessed within their body at the time of their creation. Here is an example: + +``` +step lazyValue(value: Int) -> result: () -> storedValue: Int { + yield result = () -> value +} +``` + +This deserves further explanation: We declare a [step][steps] `lazyValue`. It takes a single [required parameter][required-parameters] `value` with type `Int`. It produces a single [result][results] called `result`, which has a [callable type][callable-types] that takes no [parameters] and produces a single [result][results] called `storedValue` with type `Int`. In the [body][step-body] of the [step][steps] we then [assign][assignments-to-step-results] an [expression lambda](#expression-lambdas) to the [result][results] `result`. + +The interesting part here is that we [refer to](#references) to the [parameter][parameters] `value` within the expression of the lambda. Since lambdas are closures, this means the current `value` is stored when the lambda is created. When we later call this lambda, exactly this value is returned. + +### Restrictions + +At the moment, lambdas can only be used if the context determines the type of its parameters. Concretely, this means we can use lambdas in these two places: +* As an argument that is assigned to a [parameter][parameters] with a [type][types] in a [call](#calls). +* As the value that is [assigned to a result of a step][assignments-to-step-results]. +In other cases, declare a step instead and use a [reference](#references) to this step where you would write the lambda. + +## Precedence + +We all know that `2 + 3 * 7` is `23` and not `35`. The reason is that the `*` operator has a higher precedence than the `+` operator and is, therefore, evaluated first. These precedence rules are necessary for all types of expressions listed above and shown in the following list. The higher up an expression is in the list, the higher its precedence and the earlier it is evaluated. Expressions listed beside each other have the same precedence and are evaluated from left to right: + +* **HIGHER PRECEDENCE** +* `()` (parentheses around an expression) +* `1` ([integer literals](#int-literals)), `1.0` ([float literals](#float-literals)), `"a"` ([string literals](#string-literals)), `true`/`false` ([boolean literals](#boolean-literals)), `null` ([null literal](#null-literal)), `someName` ([references](#references)), `"age: {{ age }}"` ([template strings](#template-strings)) +* `()` ([calls](#calls)), `.` ([member accesses](#member-accesses)), `?.` ([null-safe member accesses](#null-safe-member-access)), `[]` ([indexed accesses](#indexed-accesses)) +* `-` (unary, [arithmetic negations](#operations-on-numbers)) +* `?:` ([Elvis operators](#elvis-operator)) +* `*`, `/` ([multiplicative operators](#operations-on-numbers)) +* `+`, `-` (binary, [additive operators](#operations-on-numbers)) +* `<`, `<=`, `>=`, `>` ([comparison operators](#operations-on-numbers)) +* `===`, `==`, `!==`, `!=` ([equality operators](#equality-checks)) +* `not` ([logical negations](#logical-operations)) +* `and` ([conjunctions](#logical-operations)) +* `or` ([disjunctions](#logical-operations)) +* `() -> 1` ([expression lambdas](#expression-lambdas)), `() {}` ([block lambdas](#block-lambdas)) +* **LOWER PRECEDENCE** + +If the default precedence of operators is not sufficient, parentheses can be used to force a part of an expression to be evaluated first. + +[imports]: ../common/imports.md +[packages]: ../common/packages.md +[parameters]: ../common/parameters.md +[required-parameters]: ../common/parameters.md#required-parameters +[optional-parameters]: ../common/parameters.md#optional-parameters +[variadic-parameters]: ../common/parameters.md#variadic-parameters +[results]: ../common/results.md +[types]: ../common/types.md +[callable-types]: ../common/types.md#callable-types + +[classes]: ../stub-language/classes.md +[attributes]: ../stub-language/classes.md#defining-attributes +[methods]: ../stub-language/classes.md#defining-methods +[enums]: ../stub-language/enumerations.md +[enum-variants]: ../stub-language/enumerations.md#enum-variants +[global-functions]: ../stub-language/global-functions.md + +[workflow-language]: ./README.md +[statements]: ./statements.md +[assignment-multiple-assignees]: ./statements.md#multiple-assignees +[assignments-to-step-results]: ./statements.md#yielding-results-of-steps +[assignments-to-block-lambda-results]: ./statements.md#declare-results-of-block-lambdas +[placeholders]: ./statements.md#declaring-placeholders +[steps]: ./steps.md +[step-body]: ./steps.md#statements diff --git a/docs/DSL/tutorial/workflow-language/statements.md b/docs/DSL/tutorial/workflow-language/statements.md new file mode 100644 index 000000000..9f2ff3f9c --- /dev/null +++ b/docs/DSL/tutorial/workflow-language/statements.md @@ -0,0 +1,141 @@ +# Statements + +Statements are used in the [workflow language][workflow-language] to run a specific action. Simple-ML supports only two type of statements, namely +* [expression statements](#expression-statements), which are used to evaluate an expression exactly once and discard any results, and +* [assignments](#assignments), which also evaluate an expression exactly once, but can then [assign selected results to placeholders](#declaring-placeholders) or [assign them to own results](#yielding-results). + +Other types of statements such as +* if-statements to conditionally execute code or +* while-statements to repeatedly execute code + +are not planned since we want to keep the language small and easy to learn. Moreover, we want to refrain from developing yet another general-purpose programming language. Instead, code that depends on such features can be implemented in Python, integrated into Simple-ML using the [stub language][stub-language], and called in a Simple-ML program using the provided statements. + +## Expression Statements + +Expression statements are used to evaluate an [expression][expressions] exactly once. The results of this expression are ignored. Therefore, expression statements are only useful if the expression has side effects. The following snippet demonstrates this by [calling][calls] the `print` function that prints the given string to the console: + +``` +print("Hello, world!"); +``` + +As we can see here, an expression statement has the following syntactic elements: +* The [expression][expressions] to evaluate. +* A semicolon at the end. + +## Assignments + +An assignment evaluates an [expression][expressions], its _right-hand side_, exactly once. This is identical to [expression statements](#expression-statements). However, the results of this expression can then either be [assigned to placeholders](#declaring-placeholders), [assigned to results](#yielding-results), or [ignored](#ignoring-results). + +### Declaring Placeholders + +Placeholders are used to provide a name for a fixed value. This later allows us to use this value without recomputing it. In line with those semantics, placeholders must be given a value exactly once: They must be given a value directly when they are declared and that value cannot be changed afterwards (immutability). + +The next snippet shows how the singular result of an expression (the integer `1`) can be assigned to a placeholder called `one`: + +``` +val one = 1; +``` + +This assignment to a placeholder has the following syntactic elements: + +* The keyword `val`, which indicates that we want to declare a placeholder. +* The name of the placeholder, here `one`. This can be any combination of upper- and lowercase letters, underscores, and numbers, as long as it does not start with a number. However, we suggest to use `lowerCamelCase` for the names of placeholders. +* An `=` sign. +* The expression to evaluate (right-hand side). +* A semicolon at the end. + +#### References to Placeholder + +We can access the value of a placeholder in any statement that follows the assignment of that placeholder in the closest containing [workflow][workflows], [step][steps], or [block lambda][block-lambdas] using a [reference][references]. Here is a basic example, where we print the value of the `one` placeholder (here `1`) to the console: + +``` +step loadMovieRatingsSample(nInstances: Int) { + val one = 1; + print(one); +} +``` + +More information about references can be found in the [linked document][references]. + +### Yielding Results + +In addition to the [declaration of placeholders](#declaring-placeholders), assignments are used to assign a value to a [result of a step](#yielding-results-of-steps) or declare [results of a block lambda](#declare-results-of-block-lambdas). + +#### Yielding Results of Steps + +The following snippet shows how we can assign a value to a declared [result][results] of a [step][steps]: + +``` +step trulyRandomInt() -> result: Int { + yield result = 1; +} +``` + +The assignment here has the following syntactic elements: +* The keyword `yield`, which indicates that we want to assign to a result. +* The name of the result, here `greeting`. This must be identical to one of the names of a declared result in the header of the step. +* An `=` sign. +* The expression to evaluate (right-hand side). +* A semicolon at the end. + +#### Declare Results of Block Lambdas + +Similar syntax is used to yield results of [block lambdas][block-lambdas]. The difference to steps is that block lambdas do not declare their results in their header. Instead the results are declared within the assignments, just like [placeholders](#declaring-placeholders). The block lambda in the following snippet has a single result called `greeting`, which gets the value `"Hello, world!"`: + +``` +() -> { + yield greeting = "Hello, world!"; +} +``` + +The assignment here has the following syntactic elements: +* The keyword `yield`, which indicates that we want to declare a result. +* The name of the result, here `result`. This can be any combination of upper- and lowercase letters, underscores, and numbers, as long as it does not start with a number. However, we suggest to use `lowerCamelCase` for the names of results. +* An `=` sign. +* The expression to evaluate (right-hand side). +* A semicolon at the end. + +### Ignoring Results + +In case we want to ignore a result of the expression on the right-hand side of the assignment we can inserting an underscore (called _wildcard_). The following snippet is equivalent to the [expression statement](#expression-statements) `1;`: + +``` +_ = 1; +``` + +### Multiple Assignees + +So far, the left-hand side of the assignment always had a single assignee. However, when the right-hand side of the assignment produces more than one value, it is possible to freely decide for each value whether it should be [assigned to a placeholder](#declaring-placeholders), [yielded](#yielding-results) or [ignored](#ignoring-results). + +For example, the `split` method in the next example splits a large dataset into two datasets according to a given ratio. We then ignore the first dataset using a [wildcard](#ignoring-results) and [assign the second result to a placeholder](#declaring-placeholders) called `trainingDataset`. Afterwards, we train a `DecisionTree` using the `trainingDataset` and yield the trained model as a result: + +``` +step createModel(fullDataset: Dataset) -> trainedModel: Model { + _, val trainingDataset = fullDataset.split(0.2); + yield trainedModel = DecisionTree().fit(trainingDataset); +} +``` + +Let us sum up the complete syntax of an assignment: +* A comma-separated list of assignees, possibly with a trailing comma (left-hand side). Each entry is one of + * [Placeholder](#declaring-placeholders) + * [Yield](#yielding-results) + * [Wildcard](#ignoring-results) +* An `=` sign. +* The expression to evaluate (right-hand side). +* A semicolon at the end. + + +**There must be at most as many assignees on the left-hand side as the right-hand side has results.** For everything but calls this means only a single assignee can be specified. For calls it depends on the number of declared [results][results] of the callee. + +Assignment happens by index, so the first result is assigned to the first assignee, the second result is assigned to the second assignee, and so forth. If there are more results than assignees, any trailing results are implicitly ignored. + +[results]: ../common/results.md +[stub-language]: ../stub-language/README.md +[workflow-language]: ./README.md +[expressions]: ./expressions.md +[block-lambdas]: ./expressions.md#block-lambdas +[calls]: ./expressions.md#calls +[references]: ./expressions.md#references +[steps]: ./steps.md +[workflows]: ./workflows.md diff --git a/docs/DSL/tutorial/workflow-language/steps.md b/docs/DSL/tutorial/workflow-language/steps.md new file mode 100644 index 000000000..28bfb286e --- /dev/null +++ b/docs/DSL/tutorial/workflow-language/steps.md @@ -0,0 +1,135 @@ +# Steps + +Steps are used to extract a sequence of [statements][statements] from a Machine Learning program to give the sequence a name and make it reusable. In the following discussion we explain how to [declare a step](#declaring-a-step) and how to [call it](#calling-a-step). + +## Declaring a Step + +### Minimal Example + +Let's look at a minimal example of a step: + +``` +step loadMovieRatingsSample() {} +``` + +This declaration of a step has the following syntactic elements: +* The keyword `step`. +* The name of the step, here `loadMovieRatingsSample`. This can be any combination of upper- and lowercase letters, underscores, and numbers, as long as it does not start with a number. However, we suggest to use `lowerCamelCase` for the names of steps. +* The list of parameters (i.e. inputs) of the step. This is delimited by parentheses. In the example above, the step has no parameters. +* The _body_ of the step, which contains the [statements][statements] that should be run when the step is [called](#calling-a-step). The body is delimited by curly braces. In this example, the body is empty, so running this step does nothing. + +### Parameters + +To make a step configurable, add [parameters][parameters] (inputs). We will first show how to [declare parameters](#parameter-declaration) and afterwards how to [refer to them](#references-to-parameters) in the body of the step. +#### Parameter Declaration + +Parameters must be declared in the header of the step so [callers](#calling-a-step) know they are expected to pass them as an argument, and so we can [use them](#references-to-parameters) in the body of the step. + +In the following example, we give the step a single parameters with name `nInstances` and [type][types] `Int`. + +``` +step loadMovieRatingsSample(nInstances: Int) {} +``` + +More information about parameters can be found in the [linked document][parameters]. + +#### References to Parameters + +Within the step we can access the value of a parameter using a [reference][references]. Here is a basic example where we print the value of the `nInstances` parameter to the console: + +``` +step loadMovieRatingsSample(nInstances: Int) { + print(nInstances); +} +``` + +More information about references can be found in the [linked document][references]. + +### Statements + +In order to describe what should be done when the step is executed, we need to add [statements][statements] to its body. The previous example in the section ["References to Parameters"](#references-to-parameters) already contained a statement - an [expression statement][expression-statements] to be precise. Here is another example, this time showing an [assignment][assignments]: + +``` +step loadMovieRatingsSample(nInstances: Int) { + val movieRatingsSample = loadDataset("movieRatings").sample(nInstances = 1000); +} +``` + +More information about statements can be found in the [linked document][statements]. Note particularly, that all statements must end with a semicolon. + +### Results + +[Results][results] (outputs) are used to return values that are produced inside the step back to the caller. First, we show how to [declare the available results](#result-declaration) of the step and then how to [assign a value to them](#assigning-to-results). + +#### Result Declaration + +As with [parameters](#parameters) we first need to declare the available results in the headed. This tells [callers](#calling-a-step) that they can use these results and reminds us to [assign a value to them](#assigning-to-results) in the body of the step. Let's look at an example: + +``` +step loadMovieRatingsSample(nInstances: Int) -> (features: Dataset, target: Dataset) { + val movieRatingsSample = loadDataset("movieRatings").sample(nInstances = 1000); +} +``` + +We added two results to the step: The first one is called `features` and has type `Dataset`, while the second one is called `target` and also has type `Dataset`. + +More information about the declaration of results can be found in the [linked document][results]. + +#### Assigning to Results + +Currently, the program will not compile since we never assigned a value to these results. This can be done with an [assignment][assignments] and the `yield` keyword: + + +``` +step loadMovieRatingsSample(nInstances: Int) -> (features: Dataset, target: Dataset) { + val movieRatingsSample = loadDataset("movieRatings").sample(nInstances = 1000); + yield features = movieRatingsSample.keepAttributes( + "leadingActor", + "genre", + "length" + ); + yield target = movieRatingsSample.keepAttributes( + "rating" + ); +} +``` + +In the assignment beginning with `yield features =` we specify the value of the result called `features`, while the next assignment beginning with `yield target =` assigns a value to the `target` result. + +The order of the [result declarations](#result-declaration) does not need to match the order of assignment. However, **each result musts be assigned exactly once**. Note that unlike the `return` in other programming languages, `yield` does not stop the execution of the step, which allows [assignments][assignments] to different results to be split across multiple [statements][statements]. + +## Visibility + +By default a step can be [imported][imports] in any other file and reused there. We say they have `public` visibility. However, it is possible to restrict the visibility of a step with modifiers: + +``` +internal step internalStep() {} + +private step privateStep() {} +``` + +The step `internalStep` is only visible in files with the same [package][packages]. The step `privateStep` is only visible in the file it is declared in. + +## Calling a Step + +Inside of a [workflow][workflows], another step, or a [lambda][lambdas] we can then [call][calls] a step, which means the step is executed when the call is reached: The results of a step can then be used as needed. In the following example, where we call the step `loadMovieRatingsSample` that we defined above, we [assign the results to placeholders][assignments-to-placeholders]: + +``` +val features, val target = loadMovieRatingsSample(nInstances = 1000); +``` + +More information about calls can be found in the [linked document][calls]. + +[imports]: ../common/imports.md +[parameters]: ../common/parameters.md +[results]: ../common/results.md +[types]: ../common/types.md +[packages]: ../common/packages.md +[statements]: ./statements.md +[assignments]: ./statements.md#assignments +[assignments-to-placeholders]: ./statements.md#assigning-placeholders +[expression-statements]: ./statements.md#expression-statements +[calls]: ./expressions.md#calls +[lambdas]: ./expressions.md#lambdas +[references]: ./expressions.md#references +[workflows]: ./workflows.md diff --git a/docs/DSL/tutorial/workflow-language/workflows.md b/docs/DSL/tutorial/workflow-language/workflows.md new file mode 100644 index 000000000..664619791 --- /dev/null +++ b/docs/DSL/tutorial/workflow-language/workflows.md @@ -0,0 +1,36 @@ +# Workflows + +Workflows are Machine Learning programs designed to solve a specific task. They act as the entry point to start execution. Workflows are not meant to be reusable, instead extract reusable code into a [step][steps]. + +## Syntax + +### Minimal Example + +Let's look at a minimal example of a workflow: + +``` +workflow predictSpeed {} +``` + +This declaration of a workflow has the following syntactic elements: +* The keyword `workflow`. +* The name of the workflow, here `predictSpeed`, which can be any combination of upper- and lowercase letters, underscores, and numbers, as long as it does not start with a number. However, we suggest to use `lowerCamelCase` for the names of workflows. +* The body of the workflow, which contains the [statements][statements] that should be run when the workflow is executed. The body is delimited by curly braces. In this example, the body is empty, so running this workflow does nothing. + +### Statements + +In order to describe what should be done when the workflow is executed, we need to add [statements][statements] to its body, as shown in this example: + +``` +workflow predictSpeed { + val adac = loadDataset("ADAC"); + val adacSample = adac.sample(1000); + + // … +} +``` + +More information about statements can be found in the [linked document][statements]. Note particularly, that all statements must end with a semicolon. + +[steps]: ./steps.md +[statements]: ./statements.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..262c56cad --- /dev/null +++ b/docs/README.md @@ -0,0 +1,9 @@ +## Table of Contents + +* [DSL][dsl]: Documentation of the Safe-DS DSL. +* [Stdlib][stdlib]: Overview of the elements in the Safe-DS standard library. +* [DSL Development Checklist][adding-new-language-concept]: How to add new language concepts to the DSL. + +[dsl]: ./DSL/tutorial/README.md +[stdlib]: ./Stdlib/API/README.md +[adding-new-language-concept]: ./DSL/development/how-to-add-a-new-language-concept.md diff --git a/docs/Stdlib/API/README.md b/docs/Stdlib/API/README.md new file mode 100644 index 000000000..d9ee7a827 --- /dev/null +++ b/docs/Stdlib/API/README.md @@ -0,0 +1,9 @@ +# Simple-ML API Documentation + +## Packages + +* [simpleml.lang](./simpleml_lang.md) + +---------- + +**This file was created automatically. Do not change it manually!** diff --git a/docs/Stdlib/API/simpleml_lang.md b/docs/Stdlib/API/simpleml_lang.md new file mode 100644 index 000000000..9f875e7d8 --- /dev/null +++ b/docs/Stdlib/API/simpleml_lang.md @@ -0,0 +1,286 @@ +# Package `simpleml.lang` + +## Table of Contents + +* Classes + * [`Any`](#class-Any) + * [`Boolean`](#class-Boolean) + * [`Float`](#class-Float) + * [`Int`](#class-Int) + * [`Nothing`](#class-Nothing) + * [`Number`](#class-Number) + * [`String`](#class-String) +* Enums + * [`AnnotationTarget`](#enum-AnnotationTarget) +* Annotations + * [`Constant`](#annotation-Constant) + * [`Deprecated`](#annotation-Deprecated) + * [`Description`](#annotation-Description) + * [`Expert`](#annotation-Expert) + * [`NoSideEffects`](#annotation-NoSideEffects) + * [`Pure`](#annotation-Pure) + * [`PythonModule`](#annotation-PythonModule) + * [`PythonName`](#annotation-PythonName) + * [`Repeatable`](#annotation-Repeatable) + * [`Since`](#annotation-Since) + * [`Target`](#annotation-Target) + +---------- + +## Class `Any` +The common superclass of all classes. + +**Constructor:** _Class has no constructor._ + + +---------- + +## Class `Boolean` +A truth value. + +**Constructor:** _Class has no constructor._ + + +---------- + +## Class `Float` +A floating-point number. + +**Constructor:** _Class has no constructor._ + + +---------- + +## Class `Int` +An integer. + +**Constructor:** _Class has no constructor._ + + +---------- + +## Class `Nothing` +The common subclass of all classes. + +**Constructor:** _Class has no constructor._ + + +---------- + +## Class `Number` +A number. + +**Constructor:** _Class has no constructor._ + + +---------- + +## Class `String` +Some text. + +**Constructor:** _Class has no constructor._ + + +## Enum `AnnotationTarget` +The declaration types that can be targeted by annotations. +### Enum Variant `Annotation` +The annotation can be called on annotations. + +**Parameters:** _None expected._ + + +### Enum Variant `Attribute` +The annotation can be called on attributes. + +**Parameters:** _None expected._ + + +### Enum Variant `Class` +The annotation can be called on classes. + +**Parameters:** _None expected._ + + +### Enum Variant `CompilationUnit` +The annotation can be called on compilation units (i.e. files). + +**Parameters:** _None expected._ + + +### Enum Variant `Enum` +The annotation can be called on enums. + +**Parameters:** _None expected._ + + +### Enum Variant `EnumVariant` +The annotation can be called on enum variants. + +**Parameters:** _None expected._ + + +### Enum Variant `Function` +The annotation can be called on functions. + +**Parameters:** _None expected._ + + +### Enum Variant `Parameter` +The annotation can be called on parameters. + +**Parameters:** _None expected._ + + +### Enum Variant `Result` +The annotation can be called on results. + +**Parameters:** _None expected._ + + +### Enum Variant `Step` +The annotation can be called on steps. + +**Parameters:** _None expected._ + + +### Enum Variant `TypeParameter` +The annotation can be called on type parameters. + +**Parameters:** _None expected._ + + +### Enum Variant `Workflow` +The annotation can be called on workflows. + +**Parameters:** _None expected._ + + + +## Annotation `Constant` +Values assigned to this parameter must be constant. + +**Valid targets:** +* Parameter + +## Annotation `Deprecated` +The declaration should no longer be used. + +**Parameters:** +* `alternative: String? = null` - What to use instead. +* `reason: String? = null` - Why the declaration was deprecated. +* `sinceVersion: String? = null` - When the declaration was deprecated. +* `removalVersion: String? = null` - When the declaration will be removed. + +**Valid targets:** +* Annotation +* Attribute +* Class +* Enum +* EnumVariant +* Function +* Parameter +* Result +* Step +* TypeParameter + +## Annotation `Description` +The purpose of a declaration. + +**Parameters:** +* `description: String` - The purpose of a declaration. + +**Valid targets:** +* Annotation +* Attribute +* Class +* CompilationUnit +* Enum +* EnumVariant +* Function +* Parameter +* Result +* Step +* TypeParameter +* Workflow + +## Annotation `Expert` +This parameter should only be used by expert users. + +**Valid targets:** +* Parameter + +## Annotation `NoSideEffects` +The function has no side effects. + +**Valid targets:** +* Function + +## Annotation `Pure` +The function has no side effects and returns the same results for the same arguments. + +**Valid targets:** +* Function + +## Annotation `PythonModule` +The qualified name of the corresponding Python module (default is the qualified name of the package). + +**Parameters:** +* `qualifiedName: String` - The qualified name of the corresponding Python module. + +**Valid targets:** +* CompilationUnit + +## Annotation `PythonName` +The name of the corresponding API element in Python (default is the name of the declaration in the stubs). + +**Parameters:** +* `name: String` - The name of the corresponding API element in Python. + +**Valid targets:** +* Attribute +* Class +* Enum +* EnumVariant +* Function +* Parameter +* Step +* Workflow + +## Annotation `Repeatable` +The annotation can be called multiple times for the same declaration. + +**Valid targets:** +* Annotation + +## Annotation `Since` +The version in which a declaration was added. + +**Parameters:** +* `version: String` - The version in which a declaration was added. + +**Valid targets:** +* Annotation +* Attribute +* Class +* CompilationUnit +* Enum +* EnumVariant +* Function +* Parameter +* Result +* Step +* TypeParameter +* Workflow + +## Annotation `Target` +The annotation can target these declaration types. If the @Target annotation is not used any declaration type can be targeted. + +**Parameters:** +* `vararg targets: AnnotationTarget` - The valid targets. + +**Valid targets:** +* Annotation + +---------- + +**This file was created automatically. Do not change it manually!** diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..102db82ad --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5872 @@ +{ + "name": "safe-data-science", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "@lars-reimann/prettier-config": "^5.0.0" + }, + "devDependencies": { + "@lars-reimann/eslint-config": "^4.3.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz", + "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "peer": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/runtime": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz", + "integrity": "sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==", + "dev": true, + "peer": true, + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.3.tgz", + "integrity": "sha512-l4ddFwrc9rnR+EJsHsh+TJ4A35YqQz/UqcjtlX2ov53hlJYG5CxtQmNZxyajwDVmCxwy++rtvGU5HazCK4W41Q==", + "dev": true, + "peer": true, + "dependencies": { + "core-js-pure": "^3.20.2", + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "peer": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true, + "peer": true + }, + "node_modules/@lars-reimann/eslint-config": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@lars-reimann/eslint-config/-/eslint-config-4.3.1.tgz", + "integrity": "sha512-AKpMVYR5pgTiha9pPnSmMauJJKs3i0ukLww+yW4lQzpFeMwGNT+idD+8lUAqOhgk/61TLC5cTTX//gHnJXWfjQ==", + "dev": true, + "dependencies": { + "eslint-config-airbnb": "^19.0.2", + "eslint-config-airbnb-typescript": "^14.0.2", + "eslint-config-prettier": "^8.3.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.33.0", + "eslint": "^7.32.0", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.4.1", + "eslint-plugin-react": "^7.26.1", + "eslint-plugin-react-hooks": "^4.2.0", + "eslint-plugin-testing-library": "^5.0.1" + } + }, + "node_modules/@lars-reimann/prettier-config": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@lars-reimann/prettier-config/-/prettier-config-5.0.0.tgz", + "integrity": "sha512-52Ha8xMKpQESiaEzceWgyQb+fuPVD3wl2p6Op1mpLyLj6natjq7Vy8lAmbWS3AbPRjPlJZZHnp/b+sOAOdNqbA==", + "peerDependencies": { + "prettier": ">= 2" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "peer": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true, + "peer": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "peer": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", + "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "4.33.0", + "@typescript-eslint/scope-manager": "4.33.0", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^4.0.0", + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", + "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.33.0", + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/typescript-estree": "4.33.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", + "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "4.33.0", + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/typescript-estree": "4.33.0", + "debug": "^4.3.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", + "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", + "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", + "dev": true, + "peer": true, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", + "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.27.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.27.1.tgz", + "integrity": "sha512-mZ9WEn1ZLDaVrhRaYgzbkXBkTPghPFsup8zDbbsYTxC5OmqrFE7skkKS/sraVsLP3TcT3Ki5CSyEFBRkLH/H/w==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.27.1", + "@typescript-eslint/types": "5.27.1", + "@typescript-eslint/typescript-estree": "5.27.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "5.27.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.27.1.tgz", + "integrity": "sha512-fQEOSa/QroWE6fAEg+bJxtRZJTH8NTskggybogHt4H9Da8zd4cJji76gA5SBlR0MgtwF7rebxTbDKB49YUCpAg==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/types": "5.27.1", + "@typescript-eslint/visitor-keys": "5.27.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "5.27.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.27.1.tgz", + "integrity": "sha512-LgogNVkBhCTZU/m8XgEYIWICD6m4dmEDbKXESCbqOXfKZxRKeqpiJXQIErv66sdopRKZPo5l32ymNqibYEH/xg==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.27.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.27.1.tgz", + "integrity": "sha512-DnZvvq3TAJ5ke+hk0LklvxwYsnXpRdqUY5gaVS0D4raKtbznPz71UJGnPTHEFo0GDxqLOLdMkkmVZjSpET1hFw==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/types": "5.27.1", + "@typescript-eslint/visitor-keys": "5.27.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.27.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.27.1.tgz", + "integrity": "sha512-xYs6ffo01nhdJgPieyk7HAOpjhTsx7r/oB9LWEhwAXgwn33tkr+W8DI2ChboqhZlC4q3TC6geDYPoiX8ROqyOQ==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/types": "5.27.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", + "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/types": "4.33.0", + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "peer": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/array-includes": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", + "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", + "dev": true, + "peer": true + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/axe-core": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.2.tgz", + "integrity": "sha512-LVAaGp/wkkgYJcjmHsoKx4juT1aQvJyPcW09MLCjVTh3V2cc6PnyempiLMNH5iMdfIX/zdbjUx2KDjMLCTdPeA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", + "dev": true, + "peer": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "peer": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "peer": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "peer": true + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "node_modules/core-js-pure": { + "version": "3.22.8", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.8.tgz", + "integrity": "sha512-bOxbZIy9S5n4OVH63XaLVXZ49QKicjowDx/UELyJ68vxfCRpYsbyh/WNZNfEfAk+ekA8vSjt+gCDpvh672bc3w==", + "dev": true, + "hasInstallScript": true, + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "peer": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "peer": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "peer": true + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "peer": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "peer": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "peer": true + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/es-abstract": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "peer": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-airbnb": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", + "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^15.0.0", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5" + }, + "engines": { + "node": "^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.28.0", + "eslint-plugin-react-hooks": "^4.3.0" + } + }, + "node_modules/eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.2" + } + }, + "node_modules/eslint-config-airbnb-base/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-config-airbnb-typescript": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-14.0.2.tgz", + "integrity": "sha512-oaVR63DqpRUiOOeSVxIzhD3FXbqJRH+7Lt9GCMsS9SKgrRW3XpZINN2FO4JEsnaHEGkktumd0AHE9K7KQNuXSQ==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^14.2.1" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.29.3", + "@typescript-eslint/parser": "^4.29.3" + } + }, + "node_modules/eslint-config-airbnb-typescript/node_modules/eslint-config-airbnb-base": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", + "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0", + "eslint-plugin-import": "^2.22.1" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "peer": true, + "dependencies": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "peer": true, + "dependencies": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "dev": true, + "peer": true, + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "peer": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "peer": true + }, + "node_modules/eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/experimental-utils": { + "version": "5.27.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.27.1.tgz", + "integrity": "sha512-Vd8uewIixGP93sEnmTRIH6jHZYRQRkGPDPpapACMvitJKX8335VHNyqKTE+mZ+m3E2c5VznTZfSsSsS5IF7vUA==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/utils": "5.27.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz", + "integrity": "sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/runtime": "^7.16.3", + "aria-query": "^4.2.2", + "array-includes": "^3.1.4", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.3.5", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.7", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.2.1", + "language-tags": "^1.0.5", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.0.tgz", + "integrity": "sha512-RgwH7hjW48BleKsYyHK5vUAvxtE9SMPDKmcPRQgtRCYaZA0XQPt5FSkrU3nhz5ifzMZcA8opwmRJ2cmOO8tr5A==", + "dev": true, + "peer": true, + "dependencies": { + "array-includes": "^3.1.5", + "array.prototype.flatmap": "^1.3.0", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.1", + "object.values": "^1.1.5", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.3", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.5.0.tgz", + "integrity": "sha512-8k1gRt7D7h03kd+SAAlzXkQwWK22BnK6GKZG+FJA6BAGy22CFvl8kCIXKpVux0cCxMWDQUPqSok0LKaZ0aOcCw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "peer": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", + "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "dev": true, + "peer": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-testing-library": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.5.1.tgz", + "integrity": "sha512-plLEkkbAKBjPxsLj7x4jNapcHAg2ernkQlKKrN2I8NrQwPISZHyCUNvg5Hv3EDqOQReToQb5bnqXYbkijJPE/g==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/utils": "^5.13.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0", + "npm": ">=6" + }, + "peerDependencies": { + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "peer": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "peer": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "peer": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "peer": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "peer": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "peer": true + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "peer": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "peer": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "peer": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "peer": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "peer": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "peer": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "peer": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true, + "peer": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "peer": true + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true, + "peer": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "peer": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "dev": true, + "peer": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "peer": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "peer": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "peer": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "peer": true + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "peer": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "peer": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "peer": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "peer": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "peer": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "peer": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "peer": true + }, + "node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "peer": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.0.tgz", + "integrity": "sha512-XzO9luP6L0xkxwhIJMTJQpZo/eeN60K08jHdexfD569AGxeNug6UketeHXEhROoM8aR7EcUoOQmIhcJQjcuq8Q==", + "dev": true, + "peer": true, + "dependencies": { + "array-includes": "^3.1.4", + "object.assign": "^4.1.2" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", + "integrity": "sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==", + "dev": true, + "peer": true + }, + "node_modules/language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "dev": true, + "peer": true, + "dependencies": { + "language-subtag-registry": "~0.3.2" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "peer": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "peer": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "peer": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "peer": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "peer": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true, + "peer": true + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "peer": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "peer": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.hasown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", + "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", + "dev": true, + "peer": true, + "dependencies": { + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "peer": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "peer": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "peer": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "peer": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "peer": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "peer": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", + "peer": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "peer": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peer": true + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "peer": true + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true, + "peer": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "peer": true, + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "peer": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peer": true, + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "peer": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true, + "peer": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "peer": true + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.1", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/table": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "peer": true + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true, + "peer": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "peer": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "peer": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "peer": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", + "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "peer": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true, + "peer": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true, + "peer": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "peer": true + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "peer": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true, + "peer": true + }, + "@babel/highlight": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz", + "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "peer": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "peer": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/runtime": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz", + "integrity": "sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==", + "dev": true, + "peer": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/runtime-corejs3": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.3.tgz", + "integrity": "sha512-l4ddFwrc9rnR+EJsHsh+TJ4A35YqQz/UqcjtlX2ov53hlJYG5CxtQmNZxyajwDVmCxwy++rtvGU5HazCK4W41Q==", + "dev": true, + "peer": true, + "requires": { + "core-js-pure": "^3.20.2", + "regenerator-runtime": "^0.13.4" + } + }, + "@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "peer": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "peer": true + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "peer": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true, + "peer": true + }, + "@lars-reimann/eslint-config": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@lars-reimann/eslint-config/-/eslint-config-4.3.1.tgz", + "integrity": "sha512-AKpMVYR5pgTiha9pPnSmMauJJKs3i0ukLww+yW4lQzpFeMwGNT+idD+8lUAqOhgk/61TLC5cTTX//gHnJXWfjQ==", + "dev": true, + "requires": { + "eslint-config-airbnb": "^19.0.2", + "eslint-config-airbnb-typescript": "^14.0.2", + "eslint-config-prettier": "^8.3.0" + } + }, + "@lars-reimann/prettier-config": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@lars-reimann/prettier-config/-/prettier-config-5.0.0.tgz", + "integrity": "sha512-52Ha8xMKpQESiaEzceWgyQb+fuPVD3wl2p6Op1mpLyLj6natjq7Vy8lAmbWS3AbPRjPlJZZHnp/b+sOAOdNqbA==", + "requires": {} + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "peer": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "peer": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "peer": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true, + "peer": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "peer": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", + "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.33.0", + "@typescript-eslint/scope-manager": "4.33.0", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", + "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==", + "dev": true, + "peer": true, + "requires": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.33.0", + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/typescript-estree": "4.33.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", + "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/scope-manager": "4.33.0", + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/typescript-estree": "4.33.0", + "debug": "^4.3.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", + "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0" + } + }, + "@typescript-eslint/types": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", + "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", + "dev": true, + "peer": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", + "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.27.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.27.1.tgz", + "integrity": "sha512-mZ9WEn1ZLDaVrhRaYgzbkXBkTPghPFsup8zDbbsYTxC5OmqrFE7skkKS/sraVsLP3TcT3Ki5CSyEFBRkLH/H/w==", + "dev": true, + "peer": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.27.1", + "@typescript-eslint/types": "5.27.1", + "@typescript-eslint/typescript-estree": "5.27.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.27.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.27.1.tgz", + "integrity": "sha512-fQEOSa/QroWE6fAEg+bJxtRZJTH8NTskggybogHt4H9Da8zd4cJji76gA5SBlR0MgtwF7rebxTbDKB49YUCpAg==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/types": "5.27.1", + "@typescript-eslint/visitor-keys": "5.27.1" + } + }, + "@typescript-eslint/types": { + "version": "5.27.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.27.1.tgz", + "integrity": "sha512-LgogNVkBhCTZU/m8XgEYIWICD6m4dmEDbKXESCbqOXfKZxRKeqpiJXQIErv66sdopRKZPo5l32ymNqibYEH/xg==", + "dev": true, + "peer": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.27.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.27.1.tgz", + "integrity": "sha512-DnZvvq3TAJ5ke+hk0LklvxwYsnXpRdqUY5gaVS0D4raKtbznPz71UJGnPTHEFo0GDxqLOLdMkkmVZjSpET1hFw==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/types": "5.27.1", + "@typescript-eslint/visitor-keys": "5.27.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.27.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.27.1.tgz", + "integrity": "sha512-xYs6ffo01nhdJgPieyk7HAOpjhTsx7r/oB9LWEhwAXgwn33tkr+W8DI2ChboqhZlC4q3TC6geDYPoiX8ROqyOQ==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/types": "5.27.1", + "eslint-visitor-keys": "^3.3.0" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "peer": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", + "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/types": "4.33.0", + "eslint-visitor-keys": "^2.0.0" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "peer": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peer": true, + "requires": {} + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "peer": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "peer": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "peer": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "peer": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "array-includes": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", + "dev": true, + "peer": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "peer": true + }, + "array.prototype.flat": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", + "dev": true, + "peer": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.flatmap": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", + "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", + "dev": true, + "peer": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + } + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", + "dev": true, + "peer": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "peer": true + }, + "axe-core": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.2.tgz", + "integrity": "sha512-LVAaGp/wkkgYJcjmHsoKx4juT1aQvJyPcW09MLCjVTh3V2cc6PnyempiLMNH5iMdfIX/zdbjUx2KDjMLCTdPeA==", + "dev": true, + "peer": true + }, + "axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", + "dev": true, + "peer": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "peer": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "peer": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "peer": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "peer": true + }, + "confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "core-js-pure": { + "version": "3.22.8", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.8.tgz", + "integrity": "sha512-bOxbZIy9S5n4OVH63XaLVXZ49QKicjowDx/UELyJ68vxfCRpYsbyh/WNZNfEfAk+ekA8vSjt+gCDpvh672bc3w==", + "dev": true, + "peer": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "peer": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "peer": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "peer": true, + "requires": { + "ms": "2.1.2" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "peer": true + }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "peer": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "peer": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "peer": true + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "peer": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "es-abstract": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "peer": true, + "requires": { + "has": "^1.0.3" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "peer": true + }, + "eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "peer": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "peer": true + } + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "peer": true + } + } + }, + "eslint-config-airbnb": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", + "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^15.0.0", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5" + } + }, + "eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-config-airbnb-typescript": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-14.0.2.tgz", + "integrity": "sha512-oaVR63DqpRUiOOeSVxIzhD3FXbqJRH+7Lt9GCMsS9SKgrRW3XpZINN2FO4JEsnaHEGkktumd0AHE9K7KQNuXSQ==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^14.2.1" + }, + "dependencies": { + "eslint-config-airbnb-base": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", + "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + } + } + } + }, + "eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "requires": {} + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "peer": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "peer": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "peer": true, + "requires": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "peer": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "dev": true, + "peer": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "peer": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "peer": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "peer": true + } + } + }, + "eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/experimental-utils": "^5.0.0" + }, + "dependencies": { + "@typescript-eslint/experimental-utils": { + "version": "5.27.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.27.1.tgz", + "integrity": "sha512-Vd8uewIixGP93sEnmTRIH6jHZYRQRkGPDPpapACMvitJKX8335VHNyqKTE+mZ+m3E2c5VznTZfSsSsS5IF7vUA==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/utils": "5.27.1" + } + } + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz", + "integrity": "sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==", + "dev": true, + "peer": true, + "requires": { + "@babel/runtime": "^7.16.3", + "aria-query": "^4.2.2", + "array-includes": "^3.1.4", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.3.5", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.7", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.2.1", + "language-tags": "^1.0.5", + "minimatch": "^3.0.4" + } + }, + "eslint-plugin-react": { + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.0.tgz", + "integrity": "sha512-RgwH7hjW48BleKsYyHK5vUAvxtE9SMPDKmcPRQgtRCYaZA0XQPt5FSkrU3nhz5ifzMZcA8opwmRJ2cmOO8tr5A==", + "dev": true, + "peer": true, + "requires": { + "array-includes": "^3.1.5", + "array.prototype.flatmap": "^1.3.0", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.1", + "object.values": "^1.1.5", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.3", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.7" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "peer": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "resolve": { + "version": "2.0.0-next.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", + "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "dev": true, + "peer": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "peer": true + } + } + }, + "eslint-plugin-react-hooks": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.5.0.tgz", + "integrity": "sha512-8k1gRt7D7h03kd+SAAlzXkQwWK22BnK6GKZG+FJA6BAGy22CFvl8kCIXKpVux0cCxMWDQUPqSok0LKaZ0aOcCw==", + "dev": true, + "peer": true, + "requires": {} + }, + "eslint-plugin-testing-library": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.5.1.tgz", + "integrity": "sha512-plLEkkbAKBjPxsLj7x4jNapcHAg2ernkQlKKrN2I8NrQwPISZHyCUNvg5Hv3EDqOQReToQb5bnqXYbkijJPE/g==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/utils": "^5.13.0" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "peer": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "peer": true + } + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "peer": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "peer": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "peer": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "peer": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "peer": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "peer": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "peer": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "peer": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "peer": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "peer": true + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "peer": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "peer": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "peer": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "peer": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "peer": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "peer": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "peer": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "peer": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true, + "peer": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "peer": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true, + "peer": true + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "peer": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "peer": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "dev": true, + "peer": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "peer": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "peer": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "peer": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "peer": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "peer": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "peer": true + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "peer": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "peer": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "peer": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "peer": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "peer": true + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "peer": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "peer": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "peer": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "peer": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "peer": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "peer": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsx-ast-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.0.tgz", + "integrity": "sha512-XzO9luP6L0xkxwhIJMTJQpZo/eeN60K08jHdexfD569AGxeNug6UketeHXEhROoM8aR7EcUoOQmIhcJQjcuq8Q==", + "dev": true, + "peer": true, + "requires": { + "array-includes": "^3.1.4", + "object.assign": "^4.1.2" + } + }, + "language-subtag-registry": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", + "integrity": "sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==", + "dev": true, + "peer": true + }, + "language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "dev": true, + "peer": true, + "requires": { + "language-subtag-registry": "~0.3.2" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "peer": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "peer": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "peer": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "peer": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "peer": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "peer": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "peer": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "peer": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true, + "peer": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "peer": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "peer": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "peer": true + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "object.fromentries": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "dev": true, + "peer": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "object.hasown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", + "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", + "dev": true, + "peer": true, + "requires": { + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "peer": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "peer": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "peer": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "peer": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "peer": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "peer": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "peer": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "peer": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "peer": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "peer": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "peer": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "peer": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "peer": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "peer": true + }, + "prettier": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", + "peer": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "peer": true + }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "peer": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "peer": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "peer": true + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "peer": true + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true, + "peer": true + }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "peer": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "peer": true + }, + "resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "peer": true, + "requires": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "peer": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "peer": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "peer": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "peer": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "peer": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "peer": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "peer": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "peer": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true, + "peer": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "peer": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "peer": true + } + } + }, + "string.prototype.matchall": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", + "dev": true, + "peer": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.1", + "side-channel": "^1.0.4" + } + }, + "string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "peer": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "peer": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "peer": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "peer": true + }, + "table": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "dev": true, + "peer": true, + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "peer": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "peer": true + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true, + "peer": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "peer": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "peer": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "peer": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "peer": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "peer": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "peer": true + }, + "typescript": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", + "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", + "dev": true, + "peer": true + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "peer": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true, + "peer": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "peer": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "peer": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true, + "peer": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "peer": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..26a9da775 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "safe-data-science", + "version": "0.0.1", + "private": true, + "homepage": "https://github.com/lars-reimann/safe-data-science", + "repository": { + "type": "git", + "url": "https://github.com/lars-reimann/safe-data-science" + }, + "devDependencies": { + "@lars-reimann/eslint-config": "^4.3.1", + "@lars-reimann/prettier-config": "^5.0.0" + } +} diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 000000000..56f6956bc --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,8 @@ +{ + "extends": "./DSL/de.unibonn.simpleml.vscode/tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["./.eslintrc.js", "./DSL/de.unibonn.simpleml.vscode/src/**/*"], + "exclude": [] +}