diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000000..927064e736 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,43 @@ +name: CI + +on: [push, pull_request] + +jobs: + ci: + name: Continuous Integration + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Install OpenJDK 14 and sbt + uses: olafurpg/setup-scala@v5 + with: + java-version: openjdk@1.14 + + - name: Install Dependencies + run: | + cd src + sbt ++2.12.9 update + + - name: Build 'mmt.jar' + run: | + cd src + sbt ++2.12.9 deploy + + - name: Store 'mmt.jar' artifact + uses: actions/upload-artifact@v2 + with: + name: mmt.jar + path: deploy/mmt.jar + + - name: Unit Tests + run: | + cd src + sbt ++2.12.9 test + + - name: Integration Tests + shell: bash + run: | + export TEST_USE_BRANCH=${GITHUB_REF#refs/heads/} + java -cp deploy/mmt.jar info.kwarc.mmt.test.TestRunner diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 66600dd742..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,171 +0,0 @@ - -# +===============================================================+ -# |THIS FILE HAS BEEN AUTO-GENERATED USING `sbt genTravisYML` | -# |ANY CHANGES WILL BE OVERWRITTEN | -# +===============================================================+ - -# these values were configured in src/project/prefix.travis.yml - -# configuration for deploy -env: - global: - - ENCRYPTION_LABEL: "25a07036478c" - - COMMIT_AUTHOR_EMAIL: "45969094+kwarcbot@users.noreply.github.com" - - JAVA_OPTS: "-Xmx8192m" - -# use java, and install sbt on OS X -language: java - - -# meta -- email notification for builds -notifications: - email: - on_success: change - on_failure: always - on_error: always - on_start: never - on_cancel: never - - -# speed up cloning of the git repository -# we only need a clone depth of '1' -git: - depth: 1 - -# cache the dependencies for sbt so that we do not need to re-download them all the time -# adapted from https://www.scala-sbt.org/0.13/docs/Travis-CI-with-sbt.html -cache: - directories: - - $HOME/.ivy2/cache - - $HOME/.sbt - -before_cache: - # Cleanup the cached directories to avoid unnecessary cache updates - - find $HOME/.ivy2/cache -name "ivydata-*.properties" -print -delete - - find $HOME/.sbt -name "*.lock" -print -delete - -# +===============================================================+ -# |Anything below this line has been generated automatically | -# |from src/travis.sbt. | -# +===============================================================+ -before_install: - - 'if [[ "$TRAVIS_OS_NAME" = "osx" ]]; then brew update; brew install sbt; fi' -before_script: - - "export TEST_USE_BRANCH=$TRAVIS_BRANCH; echo TEST_USE_BRANCH=;" -install: - - "cd src && (cat /dev/null | sbt ++2.12.8 update) && cd .." -jobs: - include: - # check that 'sbt genTravisYML' has been run - - dist: trusty - env: - - "INFO='Check that `sbt genTravisYML` has been run'" - - 'SBT_VERSION_CMD="^validate"' - jdk: openjdk8 - language: scala - scala: "2.12.8" - script: - - "cd src && (cat /dev/null | sbt ++2.12.8 genTravisYML) && cd .." - - '(git diff --quiet --exit-code ".travis.yml")' - stage: SelfCheck - # Check that our tests run and the code compiles - - dist: trusty - env: - - "INFO='Check that unit tests run'" - - 'SBT_VERSION_CMD="^validate"' - jdk: openjdk8 - language: scala - scala: "2.12.8" - script: - - "cd src && (cat /dev/null | sbt ++2.12.8 test) && cd .." - stage: CompileAndCheck - - dist: trusty - env: - - "INFO='Check that unit tests run'" - - 'SBT_VERSION_CMD="^validate"' - jdk: openjdk11 - language: scala - scala: "2.12.8" - script: - - "cd src && (cat /dev/null | sbt ++2.12.8 test) && cd .." - - dist: trusty - env: - - "INFO='Check mmt.jar generation and integration tests'" - - 'SBT_VERSION_CMD="^validate"' - jdk: openjdk8 - language: scala - scala: "2.12.8" - script: - - "cd src && (cat /dev/null | sbt ++2.12.8 deploy) && cd .." - - '[[ -f "deploy/mmt.jar" ]]' - - "java -cp deploy/mmt.jar info.kwarc.mmt.test.TestRunner" - - dist: trusty - env: - - "INFO='Check mmt.jar generation and integration tests'" - - 'SBT_VERSION_CMD="^validate"' - jdk: openjdk11 - language: scala - scala: "2.12.8" - script: - - "cd src && (cat /dev/null | sbt ++2.12.8 deploy) && cd .." - - '[[ -f "deploy/mmt.jar" ]]' - - "java -cp deploy/mmt.jar info.kwarc.mmt.test.TestRunner" - # check that the 'apidoc' and 'deployLFCatalog' targets work - - dist: trusty - env: - - "INFO='Check lfcatalog.jar generation using `sbt deployLFCatalog`'" - - 'SBT_VERSION_CMD="^validate"' - jdk: openjdk8 - language: scala - scala: "2.12.8" - script: - - "cd src && (cat /dev/null | sbt ++2.12.8 deployLFCatalog) && cd .." - - '[[ -f "deploy/lfcatalog/lfcatalog.jar" ]]' - stage: DeployCheck - - dist: trusty - env: - - "INFO='Check lfcatalog.jar generation using `sbt deployLFCatalog`'" - - 'SBT_VERSION_CMD="^validate"' - jdk: openjdk11 - language: scala - scala: "2.12.8" - script: - - "cd src && (cat /dev/null | sbt ++2.12.8 deployLFCatalog) && cd .." - - '[[ -f "deploy/lfcatalog/lfcatalog.jar" ]]' - - dist: trusty - env: - - "INFO='Check that apidoc generation works'" - - 'SBT_VERSION_CMD="^validate"' - jdk: openjdk8 - language: scala - scala: "2.12.8" - script: - - "cd src && (cat /dev/null | sbt ++2.12.8 apidoc) && cd .." - - '[[ -d "apidoc" ]]' - - dist: trusty - env: - - "INFO='Check that apidoc generation works'" - - 'SBT_VERSION_CMD="^validate"' - jdk: openjdk11 - language: scala - scala: "2.12.8" - script: - - "cd src && (cat /dev/null | sbt ++2.12.8 apidoc) && cd .." - - '[[ -d "apidoc" ]]' - # deploy the api documentation - - dist: trusty - env: - - "INFO='Auto-deploy API documentation'" - - 'SBT_VERSION_CMD="^validate"' - jdk: openjdk8 - language: scala - scala: "2.12.8" - script: - - "bash scripts/travis/deploy_doc.sh" - stage: deploy -stages: - - name: SelfCheck - - name: CompileAndCheck - - name: DeployCheck - - if: "branch = master" - name: deploy \ No newline at end of file diff --git a/deploy/lib/scala-compiler.jar b/deploy/lib/scala-compiler.jar index 9dfce83fee..a0353e4a5f 100644 Binary files a/deploy/lib/scala-compiler.jar and b/deploy/lib/scala-compiler.jar differ diff --git a/deploy/lib/scala-library.jar b/deploy/lib/scala-library.jar index 8866c078b6..073218a317 100644 Binary files a/deploy/lib/scala-library.jar and b/deploy/lib/scala-library.jar differ diff --git a/deploy/lib/scala-parser-combinators.jar b/deploy/lib/scala-parser-combinators.jar index bd4a5e49f6..c9b80c7bfc 100644 Binary files a/deploy/lib/scala-parser-combinators.jar and b/deploy/lib/scala-parser-combinators.jar differ diff --git a/deploy/lib/scala-reflect.jar b/deploy/lib/scala-reflect.jar new file mode 100644 index 0000000000..785df08a12 Binary files /dev/null and b/deploy/lib/scala-reflect.jar differ diff --git a/deploy/lib/scala-xml.jar b/deploy/lib/scala-xml.jar index fcd9fc8077..81988af6ab 100644 Binary files a/deploy/lib/scala-xml.jar and b/deploy/lib/scala-xml.jar differ diff --git a/deploy/lib/version.txt b/deploy/lib/version.txt new file mode 100644 index 0000000000..b422bd3002 --- /dev/null +++ b/deploy/lib/version.txt @@ -0,0 +1,11 @@ +Don't put version numbers into file names here. +It messes up references every time we upgrade to a new version. +Instead, track the versions here. + +Scala language: 2.12.9 +Scala modules: + xml: 1.0.6 + parser-combinators: 1.0.7 + reflect: 2.12.9 + +xz: 1.8 \ No newline at end of file diff --git a/src/build.sbt b/src/build.sbt index 9e48080b42..a3cb0a398b 100644 --- a/src/build.sbt +++ b/src/build.sbt @@ -1,5 +1,5 @@ import Utils.utils -import sbt.Keys._ +import sbt.Keys.{scalacOptions, _} import scala.io.Source @@ -41,6 +41,13 @@ lazy val mmtMainClass = "info.kwarc.mmt.api.frontend.Run" // ================================= // GLOBAL SETTINGS // ================================= + +// !!!WARNING!!! +// If you update scalaVersion, also +// (1) update apiJars and redownload updated deps +// (2) verify whether there is a Scala paradise plugin available on Maven central for the new Scala version +// Search for "paradise" way to below to find the dependency "org.scalamacros" % "paradise_****" in this build.sbt file. +// scalaVersion in Global := "2.12.9" scalacOptions in Global := Seq( "-feature", "-language:postfixOps", "-language:implicitConversions", "-deprecation", @@ -145,7 +152,7 @@ lazy val src = (project in file(".")). exclusions(excludedProjects). aggregatesAndDepends( mmt, api, - lf, concepts, tptp, owl, mizar, frameit, mathscheme, pvs, metamath, tps, imps, isabelle, odk, specware, stex, mathhub, planetary, interviews, latex, openmath, oeis, repl, got, coq, glf, + lf, concepts, tptp, owl, mizar, frameit, mathscheme, pvs, tps, imps, isabelle, odk, specware, stex, mathhub, planetary, interviews, latex, openmath, oeis, repl, got, coq, glf, tiscaf, lfcatalog, jedit, intellij, argsemcomp ). @@ -364,9 +371,48 @@ lazy val mizar = (project in file("mmt-mizar")). dependsOn(api, lf). settings(mmtProjectsSettings("mmt-mizar"): _*) -lazy val frameit = (project in file("frameit-mmt")). - dependsOn(api, lf). - settings(mmtProjectsSettings("frameit-mmt"): _*) + +// use of MMT in the frameit system, here for ease of deployment but not part of the main mmt target +// reponsible: Navid +// finch is an HTTP server library (https://github.com/finagle/finch), a FrameIT dependency +val finchVersion = "0.32.1" +// Circe is a JSON library (https://circe.github.io/circe/), a FrameIT dependency +val circeVersion = "0.13.0" +lazy val frameit = (project in file("frameit-mmt")) + .dependsOn(api, lf) + .settings(mmtProjectsSettings("frameit-mmt"): _*) + .settings( + libraryDependencies ++= Seq( + // a server infrastructure library + "com.twitter" %% "twitter-server" % "20.7.0", + + // an incarnation of an HTTP server library for the above infrastructure + "com.github.finagle" %% "finchx-core" % finchVersion, + // with ability to automatically encode/decode JSON payloads via the circe library below + "com.github.finagle" %% "finchx-circe" % finchVersion, + "com.github.finagle" %% "finchx-generic" % finchVersion, + + // and with testing abilities + "com.github.finagle" %% "finchx-test" % finchVersion % "test", + "com.github.finagle" %% "finchx-json-test" % finchVersion % "test", + + "org.scalatest" %% "scalatest" % "3.2.0" % "test", + + // a JSON library + "io.circe" %% "circe-generic" % circeVersion, + // with extras to support encoding/decoding a case class hierarchy + "io.circe" %% "circe-generic-extras" % circeVersion, + "io.circe" %% "circe-parser" % circeVersion, + ), + + scalacOptions in Compile ++= Seq( + "-Xplugin-require:macroparadise" + ), + + // in order for @ConfiguredJsonCodec from circe-generic-extras (a FrameIT dependency above) to work + resolvers += Resolver.sonatypeRepo("releases"), + addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full) + ) // plugin for mathscheme-related functionality. Obsolete lazy val mathscheme = (project in file("mmt-mathscheme")). @@ -379,10 +425,10 @@ lazy val pvs = (project in file("mmt-pvs")). settings(mmtProjectsSettings("mmt-pvs"): _*) // plugin for reading metamath -lazy val mmscala = RootProject(uri("https://github.com/UniFormal/mm-scala#master")) +/*lazy val mmscala = RootProject(uri("https://github.com/UniFormal/mm-scala#master")) lazy val metamath = (project in file("mmt-metamath")). dependsOn(api, lf, mmscala). - settings(mmtProjectsSettings("mmt-metamath"): _*) + settings(mmtProjectsSettings("mmt-metamath"): _*) */ // plugin for reading isabelle. Author: Makarius Wenzel // This only works if an Isabelle environment is present. If not, we use an empty dummy project. diff --git a/src/frameit-mmt/README.md b/src/frameit-mmt/README.md new file mode 100644 index 0000000000..38d5c6c601 --- /dev/null +++ b/src/frameit-mmt/README.md @@ -0,0 +1,399 @@ +# frameit-mmt: Server component of FrameIT project + +This is the server component of the [FrameIT project](https://kwarc.info/systems/frameit/), maintained by [@ComFreek](https://github.com/ComFreek). + +## Installation + +1. Get a set of UFrameIT archives you want the server to use: `git clone --recursive https://github.com/UFrameIT/archives archive-root` + + Remember the path you clone this to! + +2. Clone the MMT repository on devel branch: `git clone --branch devel https://github.com/UniFormal/mmt` + +3. Import the source code into a new IntelliJ project: see + +4. Open the just created IntelliJ project and locate `src -> frameit-mmt -> src -> info.kwarc.mmt.frameit.communication.Server` in the project browser and run it via the green triangle: + + ![Project browser showing `info.kwarc.mmt.frameit.communication.Server`](https://i.imgur.com/J75FzWa.png) + + This will invoke compilation and execution of the server in that order. Compilation hopefully works. See below when you get a stack overflow error *at compilation*. Execution is supposed to result in an error since the server expects some command-line arguments upon execution. We will add them next. + +5. Edit the `Server` run configuration + + - first open all run configurations: + + ![run configurations](https://i.imgur.com/nFd8ETr.png) + + - edit the `Server` configuration by adding `-bind :8085 -archive-root ` to its program arguments: + + ![program arguments](https://i.imgur.com/lZahL6C.png) + +6. Rerun the server via the run configuration dropdown (left to green triangle in IntelliJ's menu band) + + The server should now be running. The initial console output should be + + ``` + "C:\Program Files (x86)\OpenJDK\jdk-14.0.1\bin\java.exe" [...] info.kwarc.mmt.frameit.communication.Server + + SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". + SLF4J: Defaulting to no-operation (NOP) logger implementation + SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. + + WARNING: An illegal reflective access operation has occurred + WARNING: Illegal reflective access by com.twitter.jvm.Hotspot (file:/C:/Users/nroux/Desktop/mmt/src/null/Coursier/cache/v1/https/repo1.maven.org/maven2/com/twitter/util-jvm_2.12/20.7.0/util-jvm_2.12-20.7.0.jar) to field sun.management.ManagementFactoryHelper.jvm + WARNING: Please consider reporting this to the maintainers of com.twitter.jvm.Hotspot + WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations + WARNING: All illegal access operations will be denied in a future release + ``` + + Don't worry. The first warning is ignorable — log output is just discarded. The secone one is Twitter's to fix and is under their investigation already (for months, sadly). + +**You're done.** The server should now be available at `http://localhost:8085` and respond to the REST API calls detailled below. + +As a first test, you can try opening . It should output something like + +``` +"\ntheory SituationTheory : http://mathhub.info/FrameIT/frameworld?FactCollection = \n❚" +``` + +## Stack overflow error when compiling + +The Scala compiler sometimes (unreproducibly) runs into stackoverflow errors when compiling, concretely, when typechecking. The Internet does not offer many tips for solving this except increasing the stack size for compilation: + +- +- + +Not sure if it helped in my case or the error just randomly disappeared. + +## REST API + +``` +POST /archive/build-light + no payload +POST /archive/build + no payload + +POST /fact/add + payload: {"label": "some label", "tp": OMDoc JSON term, "df": OMDoc JSON term or null or left out} + return: {"uri": uri to created fact} +GET /fact/list + no payload + return: [ + {"uri": uri to fact, "label": "some label", "tp": {"original": OMDoc JSON term, "simplified": OMDoc JSON term}, df: same as tp or null or left out}, + // repeat for other facts + ] + +GET /scroll/list + [{ + "problemTheory": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem", + "solutionTheory": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Solution", + "label": "OppositeLen", + "description": "Given a triangle ABC right angled at C, the distance AB can be computed from the angle at B and the distance BC", + "requiredFacts": [{ + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pA", + "label": "pA", + "tp": { + "original": OMDoc JSON term, + "simplified": OMDoc JSON term + }, + "df": same as tp or null or left out + } /* more facts */] + } /* more scrolls] + +POST /scroll/check + { + "scroll": { + "problemTheory": "uri to problem theory", + "solutionTheory": "uri to solution theory", + }, + "assignments": [ + ["uri to fact", OMDoc JSON term] + ] + } + return: ??? yet unspecified + +POST /scroll/try-complete + { + "scroll": { + "problemTheory": "uri to problem theory", + "solutionTheory": "uri to solution theory", + }, + "assignments": [ + ["uri to fact", OMDoc JSON term] + ] + } + return: ??? yet unspecified + +POST /scroll/apply + { + "scroll": { + "problemTheory": "uri to problem theory", + "solutionTheory": "uri to solution theory", + }, + "assignments": [ + ["uri to fact", OMDoc JSON term] + ] + } + return: list of new facts (i.e., those received via scroll application by means of a pushout). See /fact/list for the format. + +GET debug/situationtheory/print + no payload + return: string of MMT surface syntax (not to be parsed; debugging only!) +``` + +## Sample output for `scroll/list` + +``` +[ + { + "problemTheory": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem", + "solutionTheory": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Solution", + "label": "OppositeLen", + "description": "Given a triangle ABC right angled at C, the distance AB can be computed from the angle at B and the distance BC", + "requiredFacts": [ + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pA", + "label": "pA", + "tp": { + "original": { + "uri": "http://mathhub.info/MitM/core/geometry?3DGeometry?point", + "kind": "OMS" + }, + "simplified": { + "uri": "http://mathhub.info/MitM/core/geometry?3DGeometry?point", + "kind": "OMS" + } + }, + "df": null + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pB", + "label": "pB", + "tp": { + "original": { + "uri": "http://mathhub.info/MitM/core/geometry?3DGeometry?point", + "kind": "OMS" + }, + "simplified": { + "uri": "http://mathhub.info/MitM/core/geometry?3DGeometry?point", + "kind": "OMS" + } + }, + "df": null + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pC", + "label": "pC", + "tp": { + "original": { + "uri": "http://mathhub.info/MitM/core/geometry?3DGeometry?point", + "kind": "OMS" + }, + "simplified": { + "uri": "http://mathhub.info/MitM/core/geometry?3DGeometry?point", + "kind": "OMS" + } + }, + "df": null + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pdistBC_v", + "label": "pdistBC_v", + "tp": { + "original": { + "uri": "http://mathhub.info/MitM/Foundation?RealLiterals?real_lit", + "kind": "OMS" + }, + "simplified": { + "uri": "http://mathhub.info/MitM/Foundation?RealLiterals?real_lit", + "kind": "OMS" + } + }, + "df": null + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pdistBC", + "label": "pdistBC", + "tp": { + "original": { + "applicant": { + "uri": "http://mathhub.info/FrameIT/frameworld?DistanceFact?distanceFact", + "kind": "OMS" + }, + "arguments": [ + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pB", + "kind": "OMS" + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pC", + "kind": "OMS" + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pdistBC_v", + "kind": "OMS" + } + ], + "kind": "OMA" + }, + "simplified": { + "applicant": { + "uri": "http://mathhub.info/FrameIT/frameworld?DistanceFact?distanceFact", + "kind": "OMS" + }, + "arguments": [ + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pB", + "kind": "OMS" + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pC", + "kind": "OMS" + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pdistBC_v", + "kind": "OMS" + } + ], + "kind": "OMA" + } + }, + "df": null + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pangleABC_v", + "label": "pangleABC_v", + "tp": { + "original": { + "uri": "http://mathhub.info/MitM/Foundation?RealLiterals?real_lit", + "kind": "OMS" + }, + "simplified": { + "uri": "http://mathhub.info/MitM/Foundation?RealLiterals?real_lit", + "kind": "OMS" + } + }, + "df": null + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pangleABC", + "label": "pangleABC", + "tp": { + "original": { + "applicant": { + "uri": "http://mathhub.info/FrameIT/frameworld?AngleFact?angleFact", + "kind": "OMS" + }, + "arguments": [ + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pA", + "kind": "OMS" + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pB", + "kind": "OMS" + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pC", + "kind": "OMS" + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pangleABC_v", + "kind": "OMS" + } + ], + "kind": "OMA" + }, + "simplified": { + "applicant": { + "uri": "http://mathhub.info/FrameIT/frameworld?AngleFact?angleFact", + "kind": "OMS" + }, + "arguments": [ + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pA", + "kind": "OMS" + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pB", + "kind": "OMS" + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pC", + "kind": "OMS" + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pangleABC_v", + "kind": "OMS" + } + ], + "kind": "OMA" + } + }, + "df": null + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pangleBCA", + "label": "pangleBCA", + "tp": { + "original": { + "applicant": { + "uri": "http://mathhub.info/FrameIT/frameworld?AngleFact?angleFact", + "kind": "OMS" + }, + "arguments": [ + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pB", + "kind": "OMS" + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pC", + "kind": "OMS" + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pA", + "kind": "OMS" + }, + { + "float": 90.0, + "kind": "OMF" + } + ], + "kind": "OMA" + }, + "simplified": { + "applicant": { + "uri": "http://mathhub.info/FrameIT/frameworld?AngleFact?angleFact", + "kind": "OMS" + }, + "arguments": [ + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pB", + "kind": "OMS" + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pC", + "kind": "OMS" + }, + { + "uri": "http://mathhub.info/FrameIT/frameworld?OppositeLen_Problem?pA", + "kind": "OMS" + }, + { + "float": 90.0, + "kind": "OMF" + } + ], + "kind": "OMA" + } + }, + "df": null + } + ] + } +] +``` + +## License + +Same as the whole MMT source code with the exception of a snippet copied from "the Internet" into `src/info/kwarc/mmt/frameit/communication/ServerErrorHandler.scala`. The precise source and license is given there. diff --git a/src/frameit-mmt/src/FrameIT.scala b/src/frameit-mmt/src/FrameIT.scala deleted file mode 100644 index 1300911784..0000000000 --- a/src/frameit-mmt/src/FrameIT.scala +++ /dev/null @@ -1,378 +0,0 @@ -package info.kwarc.mmt.frameit - -import info.kwarc.mmt.api._ -import uom._ -import web.{Body, _} -import frontend._ -import info.kwarc.mmt.api.backend.XMLReader -import info.kwarc.mmt.api.checking._ -import info.kwarc.mmt.api.parser.StructureParserContinuations -import info.kwarc.mmt.api.utils.URI -import objects._ -import symbols._ -import modules._ - -import scala.collection._ -import scala.collection.immutable._ - - -case class FrameitError(text : String) extends Error(text) - -class FrameViewer extends Extension { - - def pushout(cpath : CPath, vpaths : MPath*) : Term = { - val comp = controller.get(cpath.parent) match { - case c : Constant => cpath.component match { - case DefComponent => c.df.getOrElse(throw FrameitError("No definition found for constant: " + cpath.parent)) - case TypeComponent => c.tp.getOrElse(throw FrameitError("No type found for constant: " + cpath.parent)) - } - case s => throw FrameitError("Expected component term found " + s.toString) - } - pushout(comp, vpaths : _*) - } - - def pushout(tm : Term, vpaths : MPath*) : Term = { - vpaths.foldLeft(tm)((t, v) => pushoutOne(t, v)) - } - - private def pushoutOne(tm : Term, vpath : MPath) : Term = { - val view = controller.get(vpath) match { - case v : View => v - case s => throw FrameitError("Expected view found " + s.toString) - } - - val rules = makeRules(view) - pushout(tm)(rules) - } - - private def makeRules(v : View) : HashMap[Path, Term]= { - v.from match { - case OMMOD(p) => - var rules = new HashMap[Path,Term] - v.getDeclarations collect { - case c: Constant => - c.df.foreach { t => - c.name match { - case LocalName(ComplexStep(path: MPath) :: ln) => - rules += (p ? ln -> t) - case _ => - } - } - } - rules - case _ => throw FrameitError("view.from not OMMOD " + v.from) - } - } - - private def pushout(t : Term)(implicit rules : HashMap[Path, Term]) : Term = t match { - case OMS(p) => - if (rules.isDefinedAt(p)) rules(p) else t - case OMA(f, args) => OMA(pushout(f), args.map(pushout)) - case OMBIND(b, con, body) => OMBIND(pushout(b), pushout(con), pushout(body)) - case _ => t - } - - private def pushout(con : Context)(implicit rules : HashMap[Path, Term]) : Context = con map (_ map pushout) -} - -class FrameitPlugin extends ServerExtension("frameit") with Logger with MMTTask { - - override val logPrefix = "frameit" - lazy val fv = controller.extman.get(classOf[FrameViewer]).headOption.getOrElse { - val a = new FrameViewer - controller.extman.addExtension(a) - a - }//new FrameViewer(controller) - - /** Server */ - - /* - private def CORS_AllowOrigin(origin : String) = true //for now - - private def checkCORS(tk : HTalk) : HTalk = tk.req.header("Origin") match { - case None => tk - case Some(s) => CORS_AllowOrigin(s) match { - case true => tk.setHeader(" Access-Control-Allow-Origin", s) - case false => tk - } - } - */ - /* - private def GetResponse : HLet = new HSimpleLet { - implicit val ec = scala.concurrent.ExecutionContext.Implicits.global - def act(tk : HTalk) = try { - - val cpathS = tk.req.param("solPath").getOrElse(throw FrameitError("no solPath found")) - val vpathS = tk.req.param("viewPath").getOrElse(throw FrameitError("no viewPath found")) - - val cpath = Path.parse(cpathS) match { - case c : CPath => c - case gn : GlobalName => CPath(gn, DefComponent) //assuming definition - case p => throw FrameitError("Expected CPath or Global Name found " + p) - } - - val vpath = Path.parse(vpathS) match { - case vp : MPath => vp - case p => throw FrameitError("Expected MPath found " + p) - } - val view = controller.get(vpath) match { - case d : View => d - case _ => throw FrameitError("expected view") - } - - val tm = simplify(fv.pushout(cpath, vpath), view.to.toMPath) - var tmS = tm.toString - TextResponse(tmS).aact(tk) - } catch { - case e : Error => log(e.shortMsg);errorResponse(e.shortMsg).aact(tk) - case e : Exception => errorResponse("Exception occured : " + e.getMessage).aact(tk) - } - } - */ - - private def simplify(t : Term, home : MPath) : Term = { - log("Before: " + t.toString) - //val solver = new Solver(controller,cu,rules) - val th = controller.get(home) match { - case thx : Theory => thx - case _ => throw FrameitError("No Theory: " + home) - } - controller.simplifier.apply(th) - val con = (objects.Context(home) :: th.getIncludes.map(p => objects.Context(p))).flatten - //val rules = RuleSet.collectRules(controller,con) - //println(rules.getAll.toList) - //val solver = new Solver(controller,CheckingUnit.byInference(None,con,t),rules) - val traverser = new StatelessTraverser { - override def traverse(t: Term)(implicit con: Context, init: State): Term = t match { - case tm@OMS(p) => - controller.get(p) match { - case const : FinalConstant if const.df.isDefined => - Traverser(this,const.df.get) - case _ => tm - } - case _ => Traverser(this,t) - } - } - var tS = traverser(t,()) - /* - tS = solver.safeSimplifyUntil(tS)({ - case tm : OMLIT => Some(tm) - case tm : UnknownOMLIT => Some(tm) - case _ => None - })(objects.Stack(con),NoHistory)._1 - */ - //val tS = solver.forcesimplify(t)(objects.Stack(con),NoHistory) - tS = controller.simplifier(tS,SimplificationUnit(con, false, true))//controller.simplifier(t, objects.Context(home)) - log("After: " + tS.toString) - tS - } - /* - // Utils - private def bodyAsString(tk: HTalk): String = { - val bodyArray: Array[Byte] = tk.req.octets.getOrElse(throw FrameitError("no body found")) - new String(bodyArray, "UTF-8") - } - - private def errorResponse(text : String) : HLet = { - TextResponse(s"MMT Error in FrameIT extension: $text ") - } - - /** - * A text response that the server sends back to the browser - * - * @param text the message that is sent in the HTTP body - */ - private def TextResponse(text: String): HLet = new HSimpleLet { - def act(tk: HTalk) { - val out = text.getBytes("UTF-8") - checkCORS(tk).setContentLength(out.size) // if not buffered - .setContentType("text/plain; charset=utf8") - .write(out) - } - } - -*/ - - lazy val checker = { - val ret = new MMTStructureChecker(new RuleBasedChecker) - controller.extman.addExtension(ret) - ret - } - implicit lazy val ce : CheckingEnvironment = new CheckingEnvironment(controller.simplifier,ErrorThrower,RelationHandler.ignore, this) - - val dpath = DPath(URI.http colon "cds.omdoc.org") / "FrameIT" - val sitpath = dpath ? "situation_theory" - val viewpath = dpath ? "situation_problem_view" - def view = controller.get(viewpath) match { - case dv : View => dv - case _ => throw new FrameitError("view does not exist!") - } - def sittheory = controller.get(sitpath) match { - case dt : Theory => dt - case _ => throw new FrameitError("situation theory does not exist!") - } - - def getdomcod = { - val dom = view.from match { - case OMMOD(p) => controller.get(p) match { - case th : Theory => th - case _ => throw FrameitError("View expected") - } - case _ => throw FrameitError("Expected MPath, found " + view.from) - } - val cod = view.to match { - case OMMOD(p) => controller.get(p) match { - case th : Theory => th - case _ => throw FrameitError("Theory for situation theory expected") - } - case _ => throw FrameitError("Expected MPath, found " + view.from) - } - (dom,cod) - } - - implicit val unifun : StructuralElement => Unit = x => controller.add(x) - - def apply(request: ServerRequest): ServerResponse = request.path match { - case "init" :: rest => try { - controller.handleLine("build FrameIT mmt-omdoc") - ServerResponse.TextResponse("Success") - } catch { - case e : Exception => ServerResponse.errorResponse("Error initializing: " + e.getMessage) - } - case "pushout" :: rest => if (request.query.trim.startsWith("theory=")) { - try { - val sol = Path.parse(request.query.trim.drop(7)) match { - case m : MPath => controller.get(m) match { - case t : Theory => t - case _ => throw FrameitError("Solution theory not a Theory") - } - case _ => throw FrameitError(request.query.trim.drop(7) + " not an MPath") - } - controller.simplifier(sol) - try { checker.apply(sol) } catch {case e : Exception => - throw new FrameitError("Solution theory does not type check: " + e.getMessage)} - val nodes = sol.getConstants.map(c => { - val tp = c.tp.map(x => simplify(fv.pushout(c.path $ TypeComponent, viewpath), view.to.toMPath)) - val df = c.df.map(x => simplify(fv.pushout(c.path $ DefComponent, viewpath), view.to.toMPath)) - Constant(c.home,c.name,Nil,tp,df,None) - }).map(_.toNode) - ServerResponse.XmlResponse({nodes}) - } catch { - case e : Exception => ServerResponse.errorResponse(e.getMessage) - } - } else ServerResponse.errorResponse("Malformed query") - case "add" :: rest => - try { - request.body.asXML match { - case {seq @ _*} => - val sitth = seq.head - val viewxml = seq.tail.head - val reader = new XMLReader(controller) - val eh = new ErrorHandler { - override protected def addError(e: Error): Unit = ServerResponse.errorResponse(e.shortMsg) - } - implicit val spc : StructureParserContinuations = new StructureParserContinuations(eh) - - try { - reader.readDocument(dpath, sitth) - } catch { - case e: Exception => - throw new FrameitError("Malformed omdoc in situation theory: " + e.getMessage) - } - try { - reader.readDocument(dpath, viewxml) - } catch { - case e: Exception => - throw new FrameitError("Malformed omdoc in view: " + e.getMessage) - } - val (dom,cod) = getdomcod - controller.simplifier(view) - controller.simplifier(dom) - controller.simplifier(cod) - try { checker.apply(dom) } catch {case e : Exception => - throw new FrameitError("Domain of view does not type check: " + e.getMessage)} - try { checker.apply(cod) } catch {case e : Exception => - throw new FrameitError("Codomain of view does not type check: " + e.getMessage)} - try { checker.apply(view) } catch {case e : Exception => - throw new FrameitError("View does not type check: " + e.getMessage)} - try { checker.apply(sittheory) } catch {case e : Exception => - throw new FrameitError("Situation theory does not type check: " + e.getMessage)} - val istotal = dom.getConstants.forall(c => { - // println("Checking " + c.name) - view.getDeclarations.exists(d => d.name == ComplexStep(dom.path) / c.name) - }) - if(!istotal) throw FrameitError("View not total") - ServerResponse.TextResponse("Okay") - case _ => throw new FrameitError("Malformed FrameIT request : not of form ") - } - } catch { - case e : Exception => ServerResponse.errorResponse(e.getMessage) - } - case _ => ServerResponse.errorResponse("Neither \"add\" nor \"pushout\"") - } - - def run(solthS : String, vpathS : String) : String = { - /* - val cpath = Path.parse(cpathS) match { - case c : CPath => c - case gn : GlobalName => CPath(gn, DefComponent) //assuming definition - case p => throw FrameitError("Expected CPath or Global Name found " + p) - } - */ - val sol = Path.parse(solthS) match { - case m : MPath => controller.get(m) match { - case t : Theory => t - case _ => throw FrameitError("Theory expected") - } - case _ => throw FrameitError("Theory expected") - } - val vpath = Path.parse(vpathS) match { - case vp : MPath => vp - case p => throw FrameitError("Expected MPath found " + p) - } - val view = controller.get(vpath) match { - case d : View => d - case _ => throw FrameitError("expected view") - } - val dom = view.from match { - case OMMOD(p) => controller.get(p) match { - case th : Theory => th - case _ => throw FrameitError("Theory expected") - } - case _ => throw FrameitError("Expected MPath found " + view.from) - } - val cod = view.to match { - case OMMOD(p) => controller.get(p) match { - case th : Theory => th - case _ => throw FrameitError("Theory expected") - } - case _ => throw FrameitError("Expected MPath found " + view.from) - } - - val checker = new MMTStructureChecker(new RuleBasedChecker) - controller.extman.addExtension(checker) - implicit val ce : CheckingEnvironment = new CheckingEnvironment(controller.simplifier,ErrorThrower,RelationHandler.ignore, this) - controller.simplifier(view) - controller.simplifier(sol) - controller.simplifier(dom) - controller.simplifier(cod) - //controller.simplifier.flatten(th) - checker.apply(dom) - checker.apply(cod) - checker.apply(view) - checker.apply(sol) - val istotal = dom.getConstants.forall(c => { - // println("Checking " + c.name) - view.getDeclarations.exists(d => d.name == ComplexStep(dom.path) / c.name) - }) - if(!istotal) throw FrameitError("View not total") - sol.getConstants.map(c => { - val tp = c.tp.map(x => simplify(fv.pushout(c.path $ TypeComponent, vpath), view.to.toMPath)) - val df = c.df.map(x => simplify(fv.pushout(c.path $ DefComponent, vpath), view.to.toMPath)) - Constant(c.home,c.name,Nil,tp,df,None) - }).map(_.toNode.toString).mkString("\n") - - //simplify(fv.pushout(cpath, vpath), view.to.toMPath) - } - -} diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/Literals.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/Literals.scala deleted file mode 100644 index 74d7dfdef2..0000000000 --- a/src/frameit-mmt/src/info/kwarc/mmt/frameit/Literals.scala +++ /dev/null @@ -1,43 +0,0 @@ -package info.kwarc.mmt.frameit - -import info.kwarc.mmt.api.objects.{OMLIT, OMS, Term} -import info.kwarc.mmt.api.uom._ -import info.kwarc.mmt.api._ -import info.kwarc.mmt.lf._ - -/** - * Created by raupi on 23.03.16. - */ -object PlanarGeometry { - val _base = DPath(utils.URI("http", "cds.omdoc.org") / "FrameIT") - val path = _base ? "planar_geometry" - val holpath = _base ? "HOL" - val tm = holpath ? "tm" -} - -import info.kwarc.mmt.frameit.PlanarGeometry._ - -import SynOpType._ -import SemOpType._ -import SemanticOperator._ - -//TODO these are [[SemanticOperator]]s now that are independent of specific MMT URIs; the connection between MMT URIs and semantic operators is made in .mmt syntax now -// the corresponding .mmt files have to be adapted - -object DoubleFunctions { - - val R = StandardDouble // realizes Apply(OMS(tm),OMS(path ? "reals") - - object Negative extends Unary(R,R, {case R(x) => -x}) - object Addition extends Binary(R,R,R, {case (R(x),R(y)) => x+y}) - object Multiplication extends Binary(R,R,R, {case (R(x),R(y)) => x*y}) - object Division extends Binary(R,R,R, {case (R(x),R(y)) => x/y}) - object Modulo extends Binary(R,R,R, {case (R(x),R(y)) => x % y}) - object Tangent extends Unary(R,R, {case R(x) => scala.math.tan(scala.math.toRadians(x))}) - object ArcTangent extends Unary(R,R, {case R(x) => scala.math.atan(scala.math.toRadians(x))}) - object Sine extends Unary(R,R, {case R(x) => scala.math.sin(scala.math.toRadians(x))}) - object ArcSine extends Unary(R,R, {case R(x) => scala.math.asin(scala.math.toRadians(x))}) - object Cosine extends Unary(R,R, {case R(x) => scala.math.cos(scala.math.toRadians(x))}) - object ArcCosine extends Unary(R,R, {case R(x) => scala.math.acos(scala.math.toRadians(x))}) - object SquareRoot extends Unary(R,R, {case R(x) => scala.math.sqrt(x)}) -} diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/archives/FrameIT.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/archives/FrameIT.scala new file mode 100644 index 0000000000..38920ea868 --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/archives/FrameIT.scala @@ -0,0 +1,29 @@ +package info.kwarc.mmt.frameit.archives + +import info.kwarc.mmt.api.{DPath, GlobalName, MPath, NamespaceMap, Path} +import info.kwarc.mmt.api.utils.URI + +object FrameIT { + + /** + * Symbols and paths of the FrameIT/FrameWorld archive: + * https://gl.mathhub.info/FrameIT/frameworld + */ + object FrameWorld { + object MetaKeys { + private val _metaAnnotations: MPath = FrameWorld.rootDocument ? "MetaAnnotations" + + val scrollName: GlobalName = _metaAnnotations ? "name" + val problemTheory: GlobalName = _metaAnnotations ? "problemTheory" + val solutionTheory: GlobalName = _metaAnnotations ? "solutionTheory" + val scrollDescription: GlobalName = _metaAnnotations ? "description" + + // TODO not yet realized in formalization + val factLabel: GlobalName = _metaAnnotations ? "factLabel" + } + + val archiveID: String = "FrameIT/frameworld" + val rootDocument: DPath = DPath(URI("http://mathhub.info/FrameIT/frameworld")) + val metaTheoryForSituationTheory: MPath = Path.parseM("http://mathhub.info/FrameIT/frameworld?SituationTheoryMeta", NamespaceMap.empty) + } +} diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/archives/MMT.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/archives/MMT.scala new file mode 100644 index 0000000000..7e34530b9d --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/archives/MMT.scala @@ -0,0 +1,11 @@ +package info.kwarc.mmt.frameit.archives + +import info.kwarc.mmt.api.{DPath, GlobalName} +import info.kwarc.mmt.api.utils.URI + +object MMT { + object LFX { + private val _path = DPath(URI("http://gl.mathhub.info/MMT/LFX")) + val tupleSymbol: GlobalName = (_path / "Sigma") ? "Symbols" ? "Tuple" + } +} diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/archives/MitM.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/archives/MitM.scala new file mode 100644 index 0000000000..cc06619679 --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/archives/MitM.scala @@ -0,0 +1,48 @@ +package info.kwarc.mmt.frameit.archives + +import info.kwarc.mmt.api.{DPath, GlobalName} +import info.kwarc.mmt.api.objects.OMS +import info.kwarc.mmt.api.uom.{RepresentedRealizedType, StandardBool, StandardDouble, StandardInt, StandardPositive, StandardString} +import info.kwarc.mmt.api.utils.URI + +object MitM { + /** + * Symbols and literals of the MitM/Foundation archive. + * + * Partially copied from https://gl.mathhub.info/MitM/Foundation/-/tree/devel/scala/info/kwarc/mmt/mitm/rules. + */ + object Foundation { + val path: DPath = DPath(URI("http", "mathhub.info") / "MitM" / "Foundation") + + private object Math { + val bool: GlobalName = path ? "Logic" ? "prop" + + val nat: GlobalName = path ? "NatLiterals" ? "nat_lit" + val int: GlobalName = path ? "IntLiterals" ? "int_lit" + val pos: GlobalName = path ? "IntLiterals" ? "pos_lit" + val real: GlobalName = path ? "RealLiterals" ? "real_lit" + + val string: GlobalName = path ? "Strings" ? "string" + } + + object BooleanLiterals extends RepresentedRealizedType(OMS(Math.bool), StandardBool) + + object StringLiterals extends RepresentedRealizedType(OMS(Math.string), StandardString) + + object RealLiterals extends RepresentedRealizedType(OMS(Math.real), StandardDouble) { + override def priority: Int = 2 + } + + object IntegerLiterals extends RepresentedRealizedType(OMS(Math.int), StandardInt) { + override def priority: Int = -1 + } + + object PosLiterals extends RepresentedRealizedType(OMS(Math.pos), StandardPositive) { + override def priority: Int = 1 + } + + val ded: GlobalName = path ? "Logic" ? "ded" + val eq: GlobalName = path ? "Logic" ? "eq" + val sketchOperator: GlobalName = path ? "InformalProofs" ? "proofsketch" + } +} \ No newline at end of file diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/archives/package-info.java b/src/frameit-mmt/src/info/kwarc/mmt/frameit/archives/package-info.java new file mode 100644 index 0000000000..bf8fccd2fc --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/archives/package-info.java @@ -0,0 +1,5 @@ +/** + * Objects holding GlobalName/MPath/DPath information of specific archives and utility + * objects with simple unapply methods + */ +package info.kwarc.mmt.frameit.archives; \ No newline at end of file diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/ContentValidator.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/ContentValidator.scala new file mode 100644 index 0000000000..bc3143aae3 --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/ContentValidator.scala @@ -0,0 +1,76 @@ +package info.kwarc.mmt.frameit.business + +import info.kwarc.mmt.api.checking.{CheckingEnvironment, MMTStructureChecker, RelationHandler, RuleBasedChecker} +import info.kwarc.mmt.api.frontend.Controller +import info.kwarc.mmt.api.modules.{Theory, View} +import info.kwarc.mmt.api.objects.OMMOD +import info.kwarc.mmt.api.symbols.{FinalConstant, PlainInclude} +import info.kwarc.mmt.api.{Error, ErrorContainer, MMTTask, MPath, StructuralElement} + +import scala.util.Random + +class ContentValidator(private val ctrl: Controller) { + private val checker = ctrl.extman.get(classOf[MMTStructureChecker]).headOption.getOrElse({ + val checker = new MMTStructureChecker(new RuleBasedChecker) + ctrl.extman.addExtension(checker) + + checker + }) + + private def createFreshCheckingEnv() = { + val errorContainer = new ErrorContainer(None) + val checkingEnv: CheckingEnvironment = new CheckingEnvironment (ctrl.simplifier, errorContainer, RelationHandler.ignore, MMTTask.generic) + + (checkingEnv, errorContainer) + } + + private def checkStructuralElementSynchronously(element: StructuralElement): List[Error] = { + val (checkingEnv, errorContainer) = createFreshCheckingEnv() + checker(element)(checkingEnv) // TODO is this synchronous? + + errorContainer.getErrors + } + + def checkTheory(theory: Theory): List[Error] = checkStructuralElementSynchronously(theory) + + def checkDeclarationAgainstTheory(theory: MPath, decl: FinalConstant): List[Error] = + checkDeclarationAgainstTheory(ctrl.getTheory(theory), decl) + + def checkView(view: View): List[Error] = checkStructuralElementSynchronously(view) + + /** + * Check a single declaration (that has not yet been added to `theory`) against `theory` + * @param theory A theory + * @param decl A "dangling" declaration, i.e. one that has not yet been added to the controller or any theory at all + * @return A list of errors, an empty list upon success of checking + */ + def checkDeclarationAgainstTheory(theory: Theory, decl: FinalConstant): List[Error] = { + assert(decl.home == theory.toTerm) + + val scracthTheoryPath = theory.path ? theory.path.name.prefixOrCreateLastSimpleStep(s"scratch${Random.nextInt()}") + val scratchTheory = Theory.empty(scracthTheoryPath.doc, scracthTheoryPath.name, theory.meta) + + ctrl.add(scratchTheory) + ctrl.add(PlainInclude(theory.path, scratchTheory.path)) + + val scratchConstant = new FinalConstant( + OMMOD(scratchTheory.path), + decl.name, + decl.alias, + decl.tpC.copy, + decl.dfC.copy, + decl.rl, + decl.notC.copy, + decl.vs + ) + + ctrl.add(scratchTheory) + ctrl.add(scratchConstant) + + val errors = checkStructuralElementSynchronously(scratchTheory) + ctrl.delete(scratchTheory.path) + + errors + } +} + diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/DebugUtils.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/DebugUtils.scala new file mode 100644 index 0000000000..7ac41f12be --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/DebugUtils.scala @@ -0,0 +1,17 @@ +package info.kwarc.mmt.frameit.business + +import info.kwarc.mmt.api.frontend.Controller +import info.kwarc.mmt.api.modules.{Module, Theory} +import info.kwarc.mmt.api.presentation.{MMTSyntaxPresenter, RenderingHandler} + +object DebugUtils { + def syntaxPresentRecursively(module: Module)(implicit ctrl: Controller, presenter: MMTSyntaxPresenter, renderingHandler: RenderingHandler): Unit = { + val includes = module match { + case t: Theory => t.getAllIncludesWithoutMeta + case x => x.getAllIncludes + } + + includes.map(_.from).map(ctrl.getAs(classOf[Module], _)).foreach(syntaxPresentRecursively(_)) + presenter(module) + } +} diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/Exceptions.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/Exceptions.scala new file mode 100644 index 0000000000..65dc2e98f7 --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/Exceptions.scala @@ -0,0 +1,13 @@ +package info.kwarc.mmt.frameit.business + +final case class InvalidMetaData(private val message: String = "", + private val cause: Throwable = None.orNull) + extends Exception(message, cause) + +final case class InvalidFactConstant(private val message: String = "", + private val cause: Throwable = None.orNull) + extends Exception(message, cause) + +final case class InvalidScroll(private val message: String = "", + private val cause: Throwable = None.orNull) + extends Exception(message, cause) \ No newline at end of file diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/KnownFact.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/KnownFact.scala new file mode 100644 index 0000000000..c15488e545 --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/KnownFact.scala @@ -0,0 +1,52 @@ +package info.kwarc.mmt.frameit.business + +import info.kwarc.mmt.api.GlobalName +import info.kwarc.mmt.api.frontend.Controller +import info.kwarc.mmt.api.metadata.MetaDatum +import info.kwarc.mmt.api.modules.{Module, Theory} +import info.kwarc.mmt.api.objects.{Context, Obj, Term} +import info.kwarc.mmt.api.symbols.{Constant, FinalConstant, PlainInclude} +import info.kwarc.mmt.api.uom.SimplificationUnit +import info.kwarc.mmt.frameit.archives.FrameIT.FrameWorld.MetaKeys +import info.kwarc.mmt.frameit.archives.MitM.Foundation.StringLiterals + +// TODO choose better name +sealed case class TermPair(original: Term, simplified: Term) + +sealed case class FactReference(uri: GlobalName) + +// TODO choose better name: known in the sense of known to MMT, e.g. problem theories of scrolls also consist of known facts (that are unknown in the sense of game play, of course) +sealed case class KnownFact(uri: FactReference, label: String, tp: TermPair, df: Option[TermPair]) + +object KnownFact { + def apply(uri: GlobalName, label: String, tp: TermPair, df: Option[TermPair]): KnownFact = + KnownFact(FactReference(uri), label, tp, df) + + def fromConstant(c: Constant)(implicit ctrl: Controller): KnownFact = { + val label = c.metadata.get(MetaKeys.factLabel) match { + // fall back to declaration name as label + case Nil => c.name.toString + case MetaDatum(_, StringLiterals(label)) :: Nil => label + case _ => throw InvalidFactConstant("could not create fact from constant", InvalidMetaData(s"Fact declaration contained an invalid label annotation or multiple label annotations, declaration path was: ${c.path}")) + } + + def simplify(obj: Obj): obj.ThisType = { + val ctx = Context(c.path.module) + val simplicationUnit = SimplificationUnit(ctx, expandDefinitions = false, fullRecursion = false) + + ctrl.simplifier.apply(obj, simplicationUnit) + } + + val tp = c.tp + .map(tp => TermPair(tp, simplify(tp))) + .getOrElse(throw InvalidFactConstant("could not create fact from constant as constant has no type component")) + val df = c.df.map(df => TermPair(df, simplify(df))) + + KnownFact(c.path, label, tp, df) + } + + def collectFromTheory(theory: Theory, recurseOnInclusions: Boolean)(implicit ctrl: Controller): List[KnownFact] = theory.getDeclarations.collect { + case c: Constant => List(fromConstant(c)) + case PlainInclude(from, to) if recurseOnInclusions && to == theory.path => collectFromTheory(ctrl.getTheory(from), recurseOnInclusions) + }.flatten +} diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/Scroll.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/Scroll.scala new file mode 100644 index 0000000000..fe982b2a53 --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/Scroll.scala @@ -0,0 +1,60 @@ +package info.kwarc.mmt.frameit.business + +import info.kwarc.mmt.api.frontend.Controller +import info.kwarc.mmt.api.metadata.{MetaData, MetaDatum} +import info.kwarc.mmt.api.modules.Theory +import info.kwarc.mmt.api.objects.OMMOD +import info.kwarc.mmt.api.symbols.Constant +import info.kwarc.mmt.api.{GlobalName, MPath} +import info.kwarc.mmt.frameit.archives.FrameIT.FrameWorld.MetaKeys +import info.kwarc.mmt.frameit.archives.MitM.Foundation.StringLiterals + +import io.circe.generic.extras.auto._ +import info.kwarc.mmt.frameit.communication.PathCodecs._ + +sealed case class Scroll(problemTheory: MPath, solutionTheory: MPath, label: String, description: String, requiredFacts: List[KnownFact]) + +object Scroll { + /** + * Check if the given theory is a solution theory, if so, extract the full scroll information. + * + * We deliberately return an [[Either]] instead of throwing an exception (as [[KnownFact.fromConstant()]] + * does) since we want to call [[fromTheory()]] also on theories to just test whether they are the solution + * theory of a scroll. + * + * @todo rework this, we shouldn't try just because we can + * @param thy solution theory + */ + def fromTheory(thy: Theory)(implicit ctrl: Controller): Either[InvalidMetaData, Scroll] = { + try { + val name = readStringMetaDatum(thy.metadata, MetaKeys.scrollName) + val problemThy = readMPathMetaDatum(thy.metadata, MetaKeys.problemTheory) + val solutionThy = readMPathMetaDatum(thy.metadata, MetaKeys.solutionTheory) + val description = readStringMetaDatum(thy.metadata, MetaKeys.scrollDescription) + + val requiredFacts = ctrl.getTheory(problemThy).getDeclarations.collect { + case c: Constant => KnownFact.fromConstant(c) + } + + Right(Scroll(problemThy, solutionThy, name, description, requiredFacts)) + } catch { + case err: InvalidMetaData => Left(err) + } + } + + private def readSingleMetaDatum(metadata: MetaData, key: GlobalName): MetaDatum = metadata.get(key) match { + case Nil => throw InvalidMetaData(s"No metadatum found for ${key} (expected exactly one matching metadatum entry)") + case List(datum) => datum + case _ => throw InvalidMetaData(s"Multiple metadatums matching for ${key} (expected exactly one matching metadatum entry)") + } + + private def readMPathMetaDatum(metadata: MetaData, key: GlobalName): MPath = readSingleMetaDatum(metadata, key).value match { + case OMMOD(mpath) => mpath + case x => throw InvalidMetaData(s"Expected MPath matching for ${key}, but got ${x}") + } + + private def readStringMetaDatum(metadata: MetaData, key: GlobalName): String = readSingleMetaDatum(metadata, key).value match { + case StringLiterals(str) => str + case x => throw InvalidMetaData(s"Expected string literal matching for ${key}, but got ${x}") + } +} \ No newline at end of file diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/ViewCompletion.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/ViewCompletion.scala new file mode 100644 index 0000000000..96223a22e4 --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/ViewCompletion.scala @@ -0,0 +1,203 @@ +package info.kwarc.mmt.frameit.business + +import info.kwarc.mmt.api.checking.Solver +import info.kwarc.mmt.api.frontend.Controller +import info.kwarc.mmt.api.modules.Theory +import info.kwarc.mmt.api.objects._ +import info.kwarc.mmt.api.symbols.{Constant, IncludeData} +import info.kwarc.mmt.api.{GlobalName, LocalName, MPath} + +import scala.collection.immutable.{List, Nil} + +/** + * Utility methods for view completion, i.e. given arbitrary lists + * of assignments (not necessarily constituting even a partial view), + * infer gaps and types of gaps. + * + * @author first version by Dennis Müller + */ +object ViewCompletion { + private def getAllSymbols(top : MPath)(implicit controller: Controller) = { + var dones : List[MPath] = Nil + def recurse(mp : MPath) : List[GlobalName] = if (dones contains mp) Nil else { + dones ::= mp + val th = controller.getAs(classOf[Theory],mp) + th.getAllIncludes.flatMap { + case IncludeData(_, from, Nil, _, _) => + recurse(from) + case _ => + Nil + } ::: th.getConstants.map(_.path) + } + recurse(top) + } + + private def allSymbols(tm : Term) = { + var symbols : List[GlobalName] = Nil + val traverser = new StatelessTraverser { + override def traverse(t: Term)(implicit con: Context, state: State): Term = t match { + case OMS(p) => + symbols ::= p + t + case _ => Traverser(this,t) + } + } + traverser(tm,()) + symbols.distinct + } + + /** + * Computes the expected type for a constant of type + * @param tp, using assignments stored in + * @param assignments Provided assignments for putative view + * @param metatheory: The meta-theory, whose symbols should be identified. + * @return Some(tpi) if expected type contains no gaps, otherwise [[None]]. + */ + def expectedType(assignments : List[(GlobalName, Term)], metatheory : Option[MPath], tp : Term)(implicit controller: Controller) : Option[Term] = { + val assMap = scala.collection.mutable.HashMap.empty[GlobalName,Term] + assignments.foreach { + case (gn,tm) => assMap(gn) = tm + } + val varMap = scala.collection.mutable.HashMap.empty[GlobalName,LocalName] + val (ret,gaps) = expectedTypeInner( + assMap, + varMap, + metatheory.map(getAllSymbols).getOrElse(Nil), + 0, + tp + ) + if (gaps > 0) None else Some(ret) + } + + /** + * Computes the expected type of tm under assignments in + * @param assMap. + * If gaps are found, new variables are introduced in + * @param varMap. + * @param metaSymbols: symbols from the meta theory that should be the identity. + * @param solveVarOrig: index for introducing new variable names. + * @param tm: Term to compute expected type for + * @return Expected type, and new index for variable names + */ + private def expectedTypeInner(assMap : scala.collection.mutable.HashMap[GlobalName,Term], + varMap : scala.collection.mutable.HashMap[GlobalName,LocalName], + metaSymbols : List[GlobalName], + solveVarOrig : Int, + tm : Term + ) = { + var solveVar = solveVarOrig + val traverser = new StatelessTraverser { + override def traverse(t: Term)(implicit con: Context, state: State): Term = t match { + case OMS(s) if metaSymbols contains s => + t + case OMS(s) if assMap.isDefinedAt(s) => + assMap(s) + case OMS(s) if varMap.isDefinedAt(s) => + OMV(varMap(s)) + case OMS(s) => + val ln = LocalName() / "I" / solveVar.toString + solveVar += 1 + varMap(s) = ln + OMV(ln) + case _ => Traverser(this,t) + } + } + val ret = traverser(tm,()) + (ret,solveVar) + } + + /** + * Attempts to infer missing assignments uniquely implied by + * @param assignments Provided assignments of putative view + * @param metatheory: The meta theory on which all assignments should be the identity. + * @return new assignments as List[(GlobalName, Term)] + * It is guaranteed that the returned list contains no tuple with a GlobalName that already appeared + * in the input assignments. + */ + def closeGaps(assignments : List[(GlobalName, Term)], metatheory : Option[MPath])(implicit controller: Controller) : List[(GlobalName, Term)] = { + val assMap = scala.collection.mutable.HashMap.empty[GlobalName,Term] + assignments.foreach { + case (gn,tm) => assMap(gn) = tm + } + + val varMap = scala.collection.mutable.HashMap.empty[GlobalName,LocalName] + val metaSymbols = metatheory.map(getAllSymbols).getOrElse(Nil) + + // Computes expected type and replaces gaps with variables + var solveVar = 0 + + // Compares expected type and type of image to close gaps + def traverseParallel(tp1 : Term, tp2 : Term) : Unit = (tp1,tp2) match { + case (_,_) if tp1 == tp2 => + case (OMBIND(s1,ct1,bd1), OMBIND(s2,ct2,bd2)) if s1 == s2 => + assert(ct1.length == ct2.length) + ct1.indices.foreach {i => + (ct1(i).tp,ct2(i).tp) match { + case (Some(tm1),Some(tm2)) => traverseParallel(tm1,tm2) + case _ => + } + } + traverseParallel(bd1,bd2) + case (OMA(f1,args1),OMA(f2,args2)) => + assert(args1.length == args2.length) + traverseParallel(f1,f2) + args1.indices.foreach{i => + traverseParallel(args1(i),args2(i)) + } + case (OMV(ln),df) if varMap.values.toList contains ln => + varMap.find(_._2 == ln) match { + case Some((gn,_)) => + if (assMap.isDefinedAt(gn)) { + // TODO check equality? + } else { + assMap(gn) = df + } + } + case _ => // terms differ structurally + } + + // main loop: iterates over all assignments, looking for gaps to close + assignments.foreach { case (sym,df) => + val const = controller.getAs(classOf[Constant],sym) + const.tp match { + case Some(tp) => + val (expectedType,nsolveVar) = expectedTypeInner(assMap,varMap,metaSymbols,solveVar,tp) + if (nsolveVar > solveVar) { // There's at least one gap here + solveVar = nsolveVar + // infer type of df + val context = allSymbols(df).map(_.module).distinct.foldLeft(Context())((c,mp) => c ++ Context(mp)) + val inftp = Solver.infer(controller,context,df,None) + // compare two types to close gaps + inftp match { + case Some(itp) => + traverseParallel(expectedType,itp) + case _ => + } + } + case _ => + } + } + val domain = assignments.map(_._1) + assMap.toList.filterNot(domain contains _._1) + } + + // comboination of closeGaps and expectType + /*def closeGapsAndInfer(domain: Theory, assignments : List[(GlobalName, Term)])(implicit controller: Controller) : List[(GlobalName, Term, Option[Term])]= { + val closedAssignments = assignments ::: closeGaps(assignments, domain.meta) + val closedAssignmentsWithType : List[(GlobalName, Term, Option[Term])] + + // Paths of domain constants still missing an assignment + val stillMissingAssignments = domain.getConstants.map(_.path).toSet.diff(closedAssignments.map(_._1).toSet) + + val remainingFilledAssignments : List[(GlobalName, Term, Option[Term])] = + stillMissingAssignments.flatMap(constantPath => controller.getConstant(constantPath).tp match { + case Some(constantType) => + expectedType(closedAssignments, domain.meta, constantType).map((constantPath, _, None)) + }).toList + + // We shall not have introduced duplicate assignments to constants of the same [[GlobalName]] by the logic above + assert(closedAssignments.map(_._1).toSet.intersect(remainingFilledAssignments.map(_._1).toSet).isEmpty) + + closedAssignments ::: remainingFilledAssignments + }*/ +} diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/package-info.java b/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/package-info.java new file mode 100644 index 0000000000..46a336b5a5 --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/business/package-info.java @@ -0,0 +1,5 @@ +/** + * Data structures and algorithms that are independent of the communication to the game engine + * and independent of any specific archives (except perhaps urtheories). + */ +package info.kwarc.mmt.frameit.business; \ No newline at end of file diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/SFrameITDatastructures.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/SFrameITDatastructures.scala new file mode 100644 index 0000000000..1355e4106c --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/SFrameITDatastructures.scala @@ -0,0 +1,74 @@ +package info.kwarc.mmt.frameit.communication + +import info.kwarc.mmt.api +import info.kwarc.mmt.api.metadata.MetaDatum +import info.kwarc.mmt.api.notations.NotationContainer +import info.kwarc.mmt.api.symbols.{Declaration, FinalConstant, TermContainer, Visibility} +import info.kwarc.mmt.api.{LocalName, MPath, SimpleStep} +import info.kwarc.mmt.api.objects.{OMS, Term} +import info.kwarc.mmt.api.utils.mmt +import info.kwarc.mmt.frameit.archives.FrameIT.FrameWorld +import info.kwarc.mmt.frameit.archives.MitM +import info.kwarc.mmt.frameit.archives.MitM.Foundation.StringLiterals +import info.kwarc.mmt.frameit.business.FactReference +import info.kwarc.mmt.lf.ApplySpine + +/** + * Facts received by the game engine + */ +sealed abstract class SIncomingFact(val label: String) { + protected def getMMTTypeComponent: Option[Term] + protected def getMMTDefComponent: Option[Term] + def toFinalConstant(home: api.objects.Term): FinalConstant = { + val factConstant = new FinalConstant( + home = home, + name = LocalName(SimpleStep(label)), + alias = Nil, + tpC = TermContainer.asParsed(getMMTTypeComponent), + dfC = TermContainer.asParsed(getMMTDefComponent), + rl = None, + notC = new NotationContainer, + vs = Visibility.public + ) + + factConstant.metadata.add(MetaDatum(FrameWorld.MetaKeys.factLabel, MitM.Foundation.StringLiterals(label))) + + factConstant + } +} + +sealed case class SIncomingGeneralFact(override val label: String, tp: Term, df: Option[Term]) extends SIncomingFact(label) { + override protected def getMMTTypeComponent: Option[Term] = Some(tp) + override protected def getMMTDefComponent: Option[Term] = df +} + +sealed case class SIncomingEqFact(override val label: String, lhs: Term, rhs: Term) extends SIncomingFact(label) { + override protected def getMMTTypeComponent: Option[Term] = None // should be inferred + + override protected def getMMTDefComponent: Option[Term] = { + // the [[Term]] "|- lhs ≐ rhs" + val equalityProposition: Term = ApplySpine( + OMS(MitM.Foundation.ded), + ApplySpine( + OMS(MitM.Foundation.eq), + // type + ???, + lhs, + rhs + ) + ) + + Some(ApplySpine( + OMS(MitM.Foundation.sketchOperator), + equalityProposition, + StringLiterals("as sent by Unity") + )) + } +} + +sealed case class SScrollReference(problemTheory: MPath, solutionTheory: MPath) + +/** + * Tentative scroll applications communicated from the game engine to MMT + */ +sealed case class SScrollApplication(scroll: SScrollReference, assignments: List[(FactReference, Term)]) diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/SOMDoc.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/SOMDoc.scala new file mode 100644 index 0000000000..e17d9e7506 --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/SOMDoc.scala @@ -0,0 +1,174 @@ +package info.kwarc.mmt.frameit.communication + +// IMPORTANT: do NOT run IntelliJ's automatic "import clean-up" utility. It will remove necessary imports in this file. +import info.kwarc.mmt.api.objects.{OMA, OMID, OMS, Term} +import info.kwarc.mmt.api.{GlobalName, MPath, NamespaceMap, Path} +import info.kwarc.mmt.frameit.archives.MMT +import info.kwarc.mmt.frameit.archives.MMT.LFX +import info.kwarc.mmt.frameit.archives.MitM.Foundation.{IntegerLiterals, RealLiterals, StringLiterals} +import info.kwarc.mmt.frameit.communication.SOMDoc.{OMDocBridge, STerm} +import info.kwarc.mmt.lf.ApplySpine +import io.circe.generic.extras.ConfiguredJsonCodec +import io.circe.{Decoder, Encoder, HCursor} + +import scala.util.Try + +object PathCodecs { + implicit val mpathEncoder: Encoder[MPath] = Encoder.encodeString.contramap[MPath](_.toString) + implicit val mpathDecoder: Decoder[MPath] = Decoder.decodeString.emapTry { str => { + Try(Path.parseM(str, NamespaceMap.empty)) + }} + + implicit val globalNameEncoder: Encoder[GlobalName] = Encoder.encodeString.contramap[GlobalName](_.toString) + implicit val globalNameDecoder: Decoder[GlobalName] = Decoder.decodeString.emapTry { str => { + Try(Path.parseS(str, NamespaceMap.empty)) + }} +} + +object TermCodecs { + implicit def termDecoder(implicit stermDecoder: Decoder[STerm]): Decoder[Term] = (c: HCursor) => { + stermDecoder(c).map(OMDocBridge.decode) + } + + implicit def termEncoder(implicit stermEncoder: Encoder[STerm]): Encoder[Term] = (tm: Term) => { + stermEncoder(OMDocBridge.encode(tm)) + } +} + +object SOMDoc { + // IMPORTANT: keep the following lines. Do not change unless you know what you're doing + // + // they control how the JSON en- and decoders treat subclasses of [[SimpleOMDoc.STerm]] + import io.circe.Json + import io.circe.generic.extras.Configuration + import io.circe.syntax._ + // IMPORTANT: end + + // vvv DO NOT REMOVE even if IntelliJ marks it as unused + import PathCodecs._ + + implicit val jsonConfig: Configuration = Configuration.default + .withDiscriminator("kind") + .copy(transformConstructorNames = oldCtorName => { + // Cannot declare this in the outer object due to some weird + // errors with circe-generic-extras macro magic + val rewriteMap = Map( + classOf[SOMA] -> "OMA", + classOf[SOMS] -> "OMS", + classOf[SFloatingPoint] -> "OMF", + classOf[SString] -> "OMSTR", + classOf[SInteger] -> "OMI" + ).map { case (key, value) => (key.getSimpleName, value) } + + rewriteMap.getOrElse(oldCtorName, oldCtorName) + }) + + // IMPORTANT: do not naively rename parameter names of the following case classes! + // That would change the derived JSON encoders and decoders, too! + // + // Instead rename and add io.circe's annotation to re-rename back to how the JSON should be. + + @ConfiguredJsonCodec + sealed trait STerm + + @ConfiguredJsonCodec + case class SOMS(uri: GlobalName) extends STerm + + @ConfiguredJsonCodec + case class SOMA(applicant: STerm, arguments: List[STerm]) extends STerm + + @ConfiguredJsonCodec + case class SInteger(value: Int) extends STerm + + @ConfiguredJsonCodec + case class SFloatingPoint(float: Double) extends STerm + + @ConfiguredJsonCodec + case class SString(string: String) extends STerm + + object STermCodecs { + implicit val somsEnc = Encoder[SOMS] + implicit val somsDec = Decoder[SOMS] + + implicit val somaEnc = Encoder[SOMA] + implicit val somaDec = Decoder[SOMA] + + implicit val sintegerEnc = Encoder[SInteger] + implicit val sintegerDec = Decoder[SInteger] + + implicit val sfloatEnc = Encoder[SFloatingPoint] + implicit val sfloatDec = Decoder[SFloatingPoint] + + implicit val sstringEnc = Encoder[SString] + implicit val sstringDec = Decoder[SString] + + implicit val stermEnc = Encoder[STerm] + implicit val stermDec = Decoder[STerm] + } + + final case class ConversionException(private val message: String = "", + private val cause: Throwable = None.orNull) + extends Exception(message, cause) + + + object OMDocBridge { + /*def encode(decl: Declaration): SFinalConstant = decl match { + case f: FinalConstant => f.tp match { + case Some(tp) => SFinalConstant(f.path, encode(tp), f.df.map(encode)) + case _ => throw ConversionException("cannot convert Declaration not containing type to SimpleOMDoc") + } + case _ => throw ConversionException(s"cannot convert declarations other than FinalConstant to SimpleOMDoc; declaration was ${decl}") + } + + def decode(sdecl: SFinalConstant): FinalConstant = { + new FinalConstant( + OMMOD(sdecl.uri.module), + sdecl.uri.name, + alias = Nil, + tpC = TermContainer.asParsed(decode(sdecl.tp)), + dfC = TermContainer.asParsed(sdecl.df.map(decode)), + rl = None, + notC = new NotationContainer, + vs = Visibility.public + ) + }*/ + + def encode(tm: Term): STerm = tm match { + case OMS(path) => SOMS(path) + // special-case LFX' tuples, hacky workaround, TODO: keep? + case OMA(OMS(LFX.tupleSymbol), args) => SOMA(SOMS(LFX.tupleSymbol), args.map(encode)) + + // Only support OMA applications in LF style + case ApplySpine(fun, args) => SOMA(encode(fun), args.map(encode)) + + case IntegerLiterals(value) => SInteger(value.intValue()) // TODO: overflow possible + case RealLiterals(value) => SFloatingPoint(value) + case StringLiterals(value) => SString(value) + + case _ => throw ConversionException(s"encountered term for which there is no SimpleOMDoc analogon: ${tm}") + } + + def decode(stm: STerm): Term = stm match { + case SOMS(uri) => OMS(uri) + case SOMA(fun, arguments) => + // special-case LFX' tuples, hacky workaround, TODO: keep? + if (fun == SOMS(LFX.tupleSymbol)) { + OMA(decode(fun), arguments.map(decode)) + } else { + ApplySpine(decode(fun), arguments.map(decode): _*) + } + case SInteger(value) => IntegerLiterals(value) + case SFloatingPoint(value) => RealLiterals(value) + case SString(value) => StringLiterals(value) + } + } + + object JSONBridge { + //def encodeDeclaration(decl: SFinalConstant): Json = decl.asJson + + def encode(stm: STerm): Json = stm.asJson + def decodeTerm(str: String): STerm = io.circe.parser.decode[STerm](str).getOrElse( + throw ConversionException(s"could not decode string to STerm: ${str}") + ) + } +} diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/Server.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/Server.scala new file mode 100644 index 0000000000..6f424eda09 --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/Server.scala @@ -0,0 +1,67 @@ +package info.kwarc.mmt.frameit.communication + +import java.net.InetSocketAddress + +import com.twitter.finagle.Http +import com.twitter.server.TwitterServer +import com.twitter.util.Await +import info.kwarc.mmt.api._ +import info.kwarc.mmt.api.frontend.{ConsoleHandler, Controller} +import info.kwarc.mmt.api.modules.Theory +import info.kwarc.mmt.api.utils.{File, FilePath} +import info.kwarc.mmt.frameit.archives.FrameIT.FrameWorld + +object Server extends TwitterServer { + override def failfastOnFlagsNotParsed: Boolean = true + + private val bindAddress = flag("bind", new InetSocketAddress(8080), "Bind address") + private val archiveRoot = flag("archive-root", "", "Path to archive root (preferably without spaces), e.g. to a clone of ") + + def main(): Unit = { + val state = initServerState(File(archiveRoot())) + val server = Http.serve(bindAddress(), ServerEndpoints.getServiceForState(state)) + onExit { + server.close() + } + Await.ready(server) + } + + private def initServerState(archiveRoot: File): ServerState = { + val ctrl = new Controller() + ctrl.report.addHandler(ConsoleHandler) + + ctrl.handleLine(s"mathpath archive ${archiveRoot}") + val frameitArchive = ctrl.backend.getArchive(FrameWorld.archiveID).getOrElse { + throw GetError(s"Archive ${FrameWorld.archiveID} could not be found!") + } + + // TODO hack to read latest scroll meta data, should not be needed + // due to https://github.com/UniFormal/MMT/issues/528 + ctrl.handleLine(s"build ${FrameWorld.archiveID} mmt-omdoc Scrolls/OppositeLen.mmt") + + frameitArchive.allContent + + // force-read relational data as somewhere (TODO say where) we use the depstore + // to get meta tags on things + frameitArchive.readRelational(FilePath("/"), ctrl, "rel") + + val situationTheory = Theory.empty( + DPath(frameitArchive.narrationBase), + LocalName("SituationTheory"), + Some(FrameWorld.metaTheoryForSituationTheory) + ) + ctrl.add(situationTheory) + + val state = new ServerState(ctrl, situationTheory.path.parent, situationTheory.path) + + state.contentValidator.checkTheory(situationTheory) match { + case Nil => + state + + case errors => + sys.error("Created situation theory, but cannot successfully typecheck it. Server will not be started. Errors below:") + sys.error(errors.mkString("\n")) + throw new Exception("") + } + } +} diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/ServerEndpoints.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/ServerEndpoints.scala new file mode 100644 index 0000000000..46825ef8bd --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/ServerEndpoints.scala @@ -0,0 +1,252 @@ +package info.kwarc.mmt.frameit.communication + +import cats.effect.IO +import com.twitter.finagle.Service +import com.twitter.finagle.http.{Request, Response} +import info.kwarc.mmt.api._ +import info.kwarc.mmt.api.frontend.Controller +import info.kwarc.mmt.api.metadata.MetaDatum +import info.kwarc.mmt.api.modules.{Theory, View} +import info.kwarc.mmt.api.notations.NotationContainer +import info.kwarc.mmt.api.objects.{OMMOD, Term} +import info.kwarc.mmt.api.ontology.IsTheory +import info.kwarc.mmt.api.presentation.MMTSyntaxPresenter +import info.kwarc.mmt.api.symbols.{Constant, FinalConstant, TermContainer, Visibility} +import info.kwarc.mmt.frameit.archives.FrameIT.FrameWorld +import info.kwarc.mmt.frameit.archives.MitM.Foundation.StringLiterals +import info.kwarc.mmt.frameit.business._ +import info.kwarc.mmt.moduleexpressions.operators.NamedPushoutUtils +import io.circe.Json +import io.finch._ +import io.finch.circe._ + +import scala.util.{Random, Try} + +sealed abstract class ValidationException(message: String, cause: Throwable = None.orNull) + extends Exception(message, cause) + +sealed case class ProcessedFactDebugInfo(tpAST: String, dfAST: String, presentedString: String, omdocXml: String) { + def asJson: Json = Json.obj( + "tpAST" -> Json.fromString(tpAST), + "dfAST" -> Json.fromString(dfAST), + "presentedString" -> Json.fromString(presentedString), + "omdocXml" -> Json.fromString(omdocXml) + ) +} +object ProcessedFactDebugInfo { + def fromConstant(c: Constant)(implicit ctrl: Controller, presenter: MMTSyntaxPresenter): ProcessedFactDebugInfo = { + ProcessedFactDebugInfo( + // avoid bubbling up exceptions to always have at least some debug information instead of none + c.tp.map(_.toString).getOrElse(""), + c.df.map(_.toString).getOrElse(""), + Try(presenter.presentToString(c)).getOrElse(""), + Try(c.toNode.toString()).getOrElse("") + ) + } +} + +final case class FactValidationException(message: String, processedFacts: List[ProcessedFactDebugInfo], cause: Throwable = None.orNull) extends ValidationException(message, cause) { + def asJson: Json = Json.obj( + "message" -> Json.fromString(message), + "processedFacts" -> Json.arr(processedFacts.map(_.asJson) : _*), + "cause" -> Json.fromString(Option(cause).toString) + ) +} + +/** + * A collection of REST routes for our [[Server server]] + */ +object ServerEndpoints extends EndpointModule[IO] { + import io.circe.generic.auto._ + + // import implicits, do NOT remove even if IntelliJ marks those imports as unused + import TermCodecs._ + import PathCodecs._ + import SOMDoc.STermCodecs._ + import ServerErrorHandler._ + + private def getEndpointsForState(state: ServerState) = + printHelp(state) :+: buildArchiveLight(state) :+: buildArchive(state) :+: addFact(state) :+: listFacts(state) :+: listScrolls(state) :+: applyScroll(state) :+: printSituationTheory(state) + + def getServiceForState(state: ServerState): Service[Request, Response] = + getEndpointsForState(state).toServiceAs[Application.Json] + + // ENDPOINTS (all private functions) + // ====================================== + private def printHelp(state: ServerState): Endpoint[IO, String] = get(path("help")) { + Ok(getEndpointsForState(state).toString) + } + + private def buildArchiveLight(state: ServerState): Endpoint[IO, Unit] = post(path("archive") :: path("build-light")) { + state.ctrl.handleLine(s"build ${FrameWorld.archiveID} mmt-omdoc Scrolls/OppositeLen.mmt") + + Ok(()) + } + + private def buildArchive(state: ServerState): Endpoint[IO, Unit] = post(path("archive") :: path("build")) { + state.ctrl.handleLine(s"build ${FrameWorld.archiveID} mmt-omdoc") + + Ok(()) + } + + private def addFact(state: ServerState): Endpoint[IO, FactReference] = post(path("fact") :: path("add") :: jsonBody[SIncomingFact]) { + (fact: SIncomingFact) => { + val factConstant = fact.toFinalConstant(state.situationTheory.toTerm) + + state.synchronized { + state.contentValidator.checkDeclarationAgainstTheory(state.situationTheory, factConstant) match { + case Nil => + // success (i.e. no errors) + state.ctrl.add(factConstant) + Ok(FactReference(factConstant.path)) + + case errors => + NotAcceptable(FactValidationException( + message = "Could not validate fact, errors were:\n\n" + errors.map { + // for [[InvalidUnit]] also elaborate their history for better feedback + case err: InvalidUnit => err.toString + "\n" + err.history + case err => err + }.mkString("\n"), + processedFacts = List(ProcessedFactDebugInfo.fromConstant(factConstant)(state.ctrl, state.presenter)) + )) + } + } + } + } + + private def listFacts(state: ServerState): Endpoint[IO, List[KnownFact]] = get(path("fact") :: path("list")) { + Ok(KnownFact.collectFromTheory(state.situationTheory, recurseOnInclusions = true)(state.ctrl)) + } + + private def printSituationTheory(state: ServerState): Endpoint[IO, String] = get(path("debug") :: path("situationtheory") :: path("print")) { + val stringRenderer = new presentation.StringBuilder + DebugUtils.syntaxPresentRecursively(state.situationTheory)(state.ctrl, state.presenter, stringRenderer) + + Ok(stringRenderer.get) + } + + private def listScrolls(state: ServerState): Endpoint[IO, List[Scroll]] = get(path("scroll") :: path("list")) { + val allTheories = state.ctrl.depstore.getInds(IsTheory).map(_.asInstanceOf[MPath]).map(state.ctrl.getTheory) + + val scrolls = allTheories.flatMap(t => Scroll.fromTheory(t)(state.ctrl) match { + case Right(scroll) => Some(scroll) + case Left(err) => + state.log(s"Ignoring theory ${t} due to error below. Note that theories that are not scrolls also emit such errors.") + state.log(err.toString) + None + }).toList + + Ok(scrolls) + } + + private sealed case class ScrollApplicationNames(view: LocalName, pushedOutView: LocalName, situationTheoryExtension: LocalName) + + /** + * Generate names for the scroll view and the view generated by pushing over it. + */ + private def generateScrollApplicationNames(state: ServerState): ScrollApplicationNames = { + val r = Random.nextInt() + ScrollApplicationNames( + LocalName(s"frameit_scroll_view_${r}"), // TODO: improve this, let game engine dictate one? + LocalName(s"frameit_pushed_scroll_view_${r}"), // TODO: improve this, let game engine dictate one? + LocalName(s"frameit_ext_situation_theory_${r}") + ) + } + + private def applyScroll(state: ServerState): Endpoint[IO, List[KnownFact]] = post(path("scroll") :: path("apply") :: jsonBody[SScrollApplication]) { (scrollApp: SScrollApplication) => { + + val scrollViewDomain = scrollApp.scroll.problemTheory + val scrollViewCodomain = state.situationTheoryPath + + val ScrollApplicationNames(scrollViewName, pushedOutScrollViewName, situationTheoryExtensionName) = generateScrollApplicationNames(state) + + // create view out of [[SScrollApplication]] + val scrollView = new View( + doc = state.situationDocument, + name = scrollViewName, + fromC = TermContainer.asParsed(OMMOD(scrollViewDomain)), + toC = TermContainer.asParsed(OMMOD(scrollViewCodomain)), + dfC = TermContainer.empty(), + isImplicit = false + ) + + val scrollViewAssignments = scrollApp.assignments.map { + case (factRef, assignedTerm) => + // create new assignment + new FinalConstant( + home = scrollView.toTerm, + name = LocalName(ComplexStep(factRef.uri.module) :: factRef.uri.name), + alias = Nil, + tpC = TermContainer.empty(), + dfC = TermContainer.asParsed(assignedTerm), + rl = None, + notC = new NotationContainer, + vs = Visibility.public, + ) + } + + state.ctrl.add(scrollView) + scrollViewAssignments.foreach(state.ctrl.add(_)) + + state.contentValidator.checkView(scrollView) match { + case Nil => + val (situationTheoryExtension, pushedOutView) = NamedPushoutUtils.computeCanonicalPushoutAlongDirectInclusion( + state.ctrl.getTheory(scrollViewDomain), + state.ctrl.getTheory(scrollViewCodomain), + state.ctrl.getTheory(scrollApp.scroll.solutionTheory), + state.situationDocument ? situationTheoryExtensionName, + scrollView, + w_to_generate = state.situationDocument ? pushedOutScrollViewName + ) + + state.ctrl.add(situationTheoryExtension) + state.ctrl.add(pushedOutView) + state.setSituationTheory(situationTheoryExtension) + + Ok(KnownFact.collectFromTheory(situationTheoryExtension, recurseOnInclusions = false)(state.ctrl)) + + + case errors => + state.ctrl.delete(scrollView.path) + + NotAcceptable(FactValidationException( + "View for scroll application does not validate, errors were:\n\n" + errors.mkString("\n"), + scrollViewAssignments.map(d => ProcessedFactDebugInfo.fromConstant(d)(state.ctrl, state.presenter)) + )) + } + }} + /* + private def getHintsForPartialScroll(gameLogic: FrameItLogic): Endpoint[IO,String] = post(path("scroll") :: path("hints") :: stringBody) {data: String => { + /** + * Example: + * { + * domainTheory: http://...?SituationTheory, + * scroll: { + * "problem": http://...?OppositeLen_Problem, + * "solution": // unused + * }, + * assignments: { + * "http://...?pA": {x: ..., y: ..., z: ...} + * ... + * } + * } + */ + JSON.parseFull(data) + Ok("abc") + }} + + def getPushOut(gameLogic: FrameItLogic): Endpoint[IO,String ] = + get( + path("pushout") + ::param("problem") + ::param("solution") + ::param("view") + ){ + (prob : String,sol:String,view:String ) =>{ + val ret = gameLogic.applyScroll(prob, sol,view) + if(ret.isDefined) Ok(ret.get) else BadRequest(new IllegalArgumentException()) + } + } + + */ +} \ No newline at end of file diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/ServerErrorHandler.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/ServerErrorHandler.scala new file mode 100644 index 0000000000..7c7f21f1da --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/ServerErrorHandler.scala @@ -0,0 +1,36 @@ +package info.kwarc.mmt.frameit.communication + +import io.circe.{Encoder, Json} + +/** + * Provides an implicit [[io.circe.Encoder JSON encoder]] for [[Exception exceptions]]. + * + * Import this implicit value before you declare [[io.finch.Endpoint endpoints]] in order + * for exceptions occurring in those endpoints (say, when the JSON payload sent by the HTTP + * client was ill-formed) to be sent back to the client in an informative way. + * + * This helps to deviate from the unhelpful (at least for devs) behavior of Finch to swallow + * all exceptions and to just send "BadRequest" to the client without any further information. + * + * Code copied from Finch's official documentation [1], under Apache License 2.0 [2]. + * + * [1]: https://finagle.github.io/finch/user-guide.html#errors ( + * [2]: https://github.com/finagle/finch/blob/be0e7647b2fd8ac617d668313164e9a8c1c39af7/LICENSE + */ +object ServerErrorHandler { + private def encodeErrorList(es: List[Exception]): Json = { + val messages = es.map(x => Json.fromString(x.getMessage)) + Json.obj("errors" -> Json.arr(messages: _*)) + } + + implicit val encodeException: Encoder[Exception] = Encoder.instance({ + case e: io.finch.Errors => encodeErrorList(e.errors.toList) + case e: io.finch.Error => + e.getCause match { + case e: io.circe.Errors => encodeErrorList(e.errors.toList) + case _ => Json.obj("message" -> Json.fromString(e.getMessage)) + } + case e: FactValidationException => e.asJson + case e: Exception => Json.obj("message" -> Json.fromString(e.getMessage)) + }) +} \ No newline at end of file diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/ServerState.scala b/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/ServerState.scala new file mode 100644 index 0000000000..f26a6a733f --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/ServerState.scala @@ -0,0 +1,40 @@ +package info.kwarc.mmt.frameit.communication + +import info.kwarc.mmt.api +import info.kwarc.mmt.api.{DPath, GeneralError, MPath} +import info.kwarc.mmt.api.frontend.{Controller, Logger, Report} +import info.kwarc.mmt.api.modules.Theory +import info.kwarc.mmt.api.presentation.MMTSyntaxPresenter +import info.kwarc.mmt.frameit.business.ContentValidator +import io.finch.InternalServerError + +/** + * An object wrapping all mutable state our server endpoints below are able to mutate. + * + * It serves encapsulating state to be as immutable as possible. + */ +class ServerState(val ctrl: Controller, val situationDocument: DPath, private var _situationTheoryPath: MPath) extends Logger { + override def logPrefix: String = "frameit-server" + override protected def report: Report = ctrl.report + + val contentValidator : ContentValidator = new ContentValidator(ctrl) + + private var _situationTheory = ctrl.getTheory(situationTheoryPath) + + val presenter : MMTSyntaxPresenter = ctrl.extman.getOrAddExtension(classOf[MMTSyntaxPresenter], "present-text-notations").getOrElse( + throw GeneralError("could not get MMTSyntaxPresenter extension required for printing") + ) + + def situationTheory: Theory = _situationTheory + def situationTheoryPath: MPath = _situationTheoryPath + + def setSituationTheory(newSituationTheory: Theory): Unit = { + _situationTheory = newSituationTheory + _situationTheoryPath = _situationTheoryPath + } + + // make log methods from Logger public by trivially overriding them + // TODO logging does not work currently, messages go nowhere + override def log(e: api.Error): Unit = super.log(e) + override def log(s: => String, subgroup: Option[String] = None): Unit = super.log(s, subgroup) +} \ No newline at end of file diff --git a/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/package-info.java b/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/package-info.java new file mode 100644 index 0000000000..5a6300f550 --- /dev/null +++ b/src/frameit-mmt/src/info/kwarc/mmt/frameit/communication/package-info.java @@ -0,0 +1,4 @@ +/** + * Everything related to the communication with the game engine. + */ +package info.kwarc.mmt.frameit.communication; \ No newline at end of file diff --git a/src/frameit-mmt/test/scala/info/kwarc/mmt/frameit/ViewCompletionTest.scala b/src/frameit-mmt/test/scala/info/kwarc/mmt/frameit/ViewCompletionTest.scala new file mode 100644 index 0000000000..550d83eb35 --- /dev/null +++ b/src/frameit-mmt/test/scala/info/kwarc/mmt/frameit/ViewCompletionTest.scala @@ -0,0 +1,146 @@ +package info.kwarc.mmt.frameit + +import info.kwarc.mmt.api.modules.Theory +import info.kwarc.mmt.api.objects.{OMID, Term} +import info.kwarc.mmt.api.symbols.FinalConstant +import info.kwarc.mmt.api.{GlobalName, LocalName, NamespaceMap, Path} +import info.kwarc.mmt.frameit.business.ViewCompletion +import info.kwarc.mmt.lf.ApplySpine +import info.kwarc.mmt.test.MMTIntegrationTest + +object ViewCompletionTest extends MMTIntegrationTest( + "FrameIT/frameworld" +)(){ + private val frameworldArchiveNS = Path.parseD("http://mathhub.info/FrameIT/frameworld", NamespaceMap.empty) + + override def main(): Unit = { + expectedTypeTests() + closeGapsTests() + } + + /** + * Helper method to quickly get the definiens of a (supposedly) [[FinalConstant]] of a theory. + * @param thy The theory + * @param constant The constant's name, will be converted to [[LocalName]] via its constructor + * @return Upon success, the definiens as a term. Upon failure, an exception. + */ + private def getConstantDefiniens(thy: Theory, constant: String): Term = { + thy.get(LocalName(constant)).asInstanceOf[FinalConstant].df.get + } + + private def expectedTypeTests() { + // We test expected type computation on pseudo views ("assignment lists", i.e. views that may fail to contain + // mappings for arbitrary domain declarations). + // + // These pseudo views are all intended to have this domain: + val domainTheoryP = frameworldArchiveNS ? "OppositeLen_Problem" + val domainTheory = controller.getTheory(domainTheoryP) + + // and this codomain: (does not really exist and does not even need to do so) + val codomainTheoryP = (frameworldArchiveNS / "integrationtests") ? "ExpectedTypeTest_Codomain" + + test("ViewCompletion.expectedType can compute easy homomorphic extension with just constants on RHS of assignments", () => { + val assignments: List[(GlobalName, Term)] = List( + (domainTheoryP ? "pA", OMID(codomainTheoryP ? "pA")), + (domainTheoryP ? "pB", OMID(codomainTheoryP ? "pB")), + (domainTheoryP ? "pC", OMID(codomainTheoryP ? "pC")), + (domainTheoryP ? "pangleABC_v", OMID(codomainTheoryP ? "pangleABC_v")) + ) + + val expectedExpectedType = ApplySpine( + OMID(frameworldArchiveNS ? "AngleFact" ? "angleFact"), + OMID(codomainTheoryP ? "pA"), + OMID(codomainTheoryP ? "pB"), + OMID(codomainTheoryP ? "pC"), + OMID(codomainTheoryP ? "pangleABC_v") + ) + + val actualExpectedType = ViewCompletion.expectedType( + assignments, + domainTheory.meta, + domainTheory.get(LocalName("pangleABC")).asInstanceOf[FinalConstant].tp.get + )(controller) + + assertTermEqual(expectedExpectedType, actualExpectedType.get) + }) + + test("ViewCompletion.expectedType can compute homomorphic extension with more complex expressions", { + // read off complex expression for assignment from the definiens of an existing constant + val angleValue = controller.getAs(classOf[FinalConstant], codomainTheoryP ? "pangleComplexExpression").df.get + + val assignments: List[(GlobalName, Term)] = List( + (domainTheoryP ? "pA", OMID(codomainTheoryP ? "pA")), + (domainTheoryP ? "pB", OMID(codomainTheoryP ? "pC")), // intentionally C for testing purposes + (domainTheoryP ? "pC", OMID(codomainTheoryP ? "pB")), // intentionally B + (domainTheoryP ? "pangleABC_v", angleValue) + ) + + val expectedExpectedType = ApplySpine( + OMID(frameworldArchiveNS ? "AngleFact" ? "angleFact"), + OMID(codomainTheoryP ? "A"), + OMID(codomainTheoryP ? "C"), + OMID(codomainTheoryP ? "B"), + angleValue + ) + + val actualExpectedType = ViewCompletion.expectedType( + assignments, + domainTheory.meta, + domainTheory.get(LocalName("pangleABC")).asInstanceOf[FinalConstant].tp.get + )(controller) + + println(actualExpectedType.contains(expectedExpectedType)) + }) + + test("ViewCompletion.expectedType rightfully fails if gaps remain", () => { + // read off complex expression for assignment from the definiens of an existing constant + val angleValue = controller.getAs(classOf[FinalConstant], codomainTheoryP ? "pangleComplexExpression").df.get + + val assignments: List[(GlobalName, Term)] = List( + (domainTheoryP ? "pA", OMID(codomainTheoryP ? "pA")), + // intentionally B left out + (domainTheoryP ? "pC", OMID(codomainTheoryP ? "pB")), + (domainTheoryP ? "pangleABC_v", angleValue) + ) + + val expectedType = ViewCompletion.expectedType( + assignments, + domainTheory.meta, + domainTheory.get(LocalName("pangleABC")).asInstanceOf[FinalConstant].tp.get + )(controller) + + if (expectedType.isDefined) { + testError("expectedType did not fail even though assignments contained gaps") + } + }) + } + + private def closeGapsTests(): Unit = { + val integrationtestsNS = frameworldArchiveNS / "integrationtests" + val codomainTheory = controller.getTheory(integrationtestsNS ? "CloseGapsTest_Codomain") + val notepadTheory = controller.getTheory(integrationtestsNS ? "CloseGapsTest_TermsNotepad") + + val domainTheoryP = frameworldArchiveNS ? "OppositeLen_Problem" + val domainTheory = controller.getTheory(domainTheoryP) + + test("ViewCompletion.closeGaps closes simple gaps", () => { + val assignments: List[(GlobalName, Term)] = List( + (domainTheoryP ? "pangleABC", OMID(codomainTheory.path ? "complexAngleFact")) + ) + + val expectedClosedGaps = List( + (domainTheoryP ? "pA", getConstantDefiniens(notepadTheory, "expected_gap_pA")), + (domainTheoryP ? "pB", getConstantDefiniens(notepadTheory, "expected_gap_pB")), + (domainTheoryP ? "pC", getConstantDefiniens(notepadTheory, "expected_gap_pC")), + (domainTheoryP ? "pangleABC_v", getConstantDefiniens(notepadTheory, "expected_gap_pangleABC_v")), + ).toSet + + val actualClosedGaps = ViewCompletion.closeGaps( + assignments, + domainTheory.meta + )(controller).toSet + + assertSetEqual(expectedClosedGaps, actualClosedGaps) + }) + } +} diff --git a/src/jEdit-mmt/src/info/kwarc/mmt/jedit/Completion.scala b/src/jEdit-mmt/src/info/kwarc/mmt/jedit/Completion.scala index 2443d7f647..1778681b81 100644 --- a/src/jEdit-mmt/src/info/kwarc/mmt/jedit/Completion.scala +++ b/src/jEdit-mmt/src/info/kwarc/mmt/jedit/Completion.scala @@ -43,7 +43,6 @@ class IDCompletion(view : org.gjt.sp.jedit.View, controller: Controller, constan case _:ImplicitArg => "" case v: Var => " " case p: PresentationMarker => "" - case AttributedObject => "" }.mkString("") val sh = not.parsingMarkers.head match { case d: Delimiter => d.text.length + 1 diff --git a/src/jEdit-mmt/src/info/kwarc/mmt/jedit/ErrorForwarder.scala b/src/jEdit-mmt/src/info/kwarc/mmt/jedit/ErrorForwarder.scala index 461224c77e..3f9d8b626f 100644 --- a/src/jEdit-mmt/src/info/kwarc/mmt/jedit/ErrorForwarder.scala +++ b/src/jEdit-mmt/src/info/kwarc/mmt/jedit/ErrorForwarder.scala @@ -80,7 +80,7 @@ class MMTErrorSource extends DefaultErrorSource("MMT") { /** * sends MMT errors directly to jEdit ErrorList - * @param file the source file, in which the errors are found + * @param mainFile the source file, in which the errors are found */ class ErrorListForwarder(errorSource: MMTErrorSource, controller: Controller, mainFile: File) extends ErrorHandler { /** diff --git a/src/jEdit-mmt/src/info/kwarc/mmt/jedit/MMTToolBar.scala b/src/jEdit-mmt/src/info/kwarc/mmt/jedit/MMTToolBar.scala index 2648c1e8ff..2c44dd682d 100644 --- a/src/jEdit-mmt/src/info/kwarc/mmt/jedit/MMTToolBar.scala +++ b/src/jEdit-mmt/src/info/kwarc/mmt/jedit/MMTToolBar.scala @@ -43,15 +43,18 @@ class MMTToolBar(mmtp: MMTPlugin) extends JToolBar { _.kill } } - val clrButton = Swing.Button("Clear", tooltip = "Clears MMT memory") { - mmtp.errorSource.clear - controller.clear - } val restartButton = Swing.Button("Restart", tooltip = "shutdown MMT plugin and reload everything") { mmtp.stop mmtp.start } + val reopenArchivesButton = Swing.Button("Reopen Archives", tooltip = "Closes and reopens all archives") { + controller.backend.reopenArchives + } + val clrButton = Swing.Button("Clear", tooltip = "Clears MMT memory") { + mmtp.errorSource.clear + controller.clear + } val clrIMG = (new ImageIcon(this.getClass().getResource("/images/clear_button.png"))).getImage() val clrIMGs = clrIMG.getScaledInstance(16, 16, java.awt.Image.SCALE_SMOOTH ) clrButton.setIcon(new ImageIcon(clrIMGs)) @@ -77,6 +80,7 @@ class MMTToolBar(mmtp: MMTPlugin) extends JToolBar { add(buildOpenButton) add(clrFileButton) add(clrButton) + add(reopenArchivesButton) //add(restartButton) // doesn't work correctly } diff --git a/src/latex-mmt/src/info/kwarc/mmt/latex/CompilingLatexPresenter.scala b/src/latex-mmt/src/info/kwarc/mmt/latex/CompilingLatexPresenter.scala index 8dfe65d628..d0220b046e 100644 --- a/src/latex-mmt/src/info/kwarc/mmt/latex/CompilingLatexPresenter.scala +++ b/src/latex-mmt/src/info/kwarc/mmt/latex/CompilingLatexPresenter.scala @@ -177,7 +177,6 @@ class MacroGeneratingPresenter extends Presenter(new MacroUsingPresenter) { case d: Delimiter => doDelim(d) case Var(n, _, Some(sep), _) => "\\mmt@fold{" + doDelim(sep) + "}{" + "#" + n + "}" case Var(n, _, None, _) => "#" + n - case AttributedObject => "#1" //probably wrong case GroupMarker(elements) => "{" + doMarkers(elements) + "}" case ScriptMarker(main, sup, sub, over, under) => var res = doM(main) @@ -254,7 +253,7 @@ class MacroUsingPresenter extends ObjectPresenter { doComplexDefault(p, subs, con, args) } case Some(tP) => - val PragmaticTerm(p, subs, con, args, _, not, _) = tP + val PragmaticTerm(p, subs, con, args, not, _) = tP doComplexWithNotation(t, p, subs, con, args, not) } "\\mmt@group{" + tS + "}" diff --git a/src/mmt-api/resources/mmt-web/script/jobad/modules/interactive-viewing.js b/src/mmt-api/resources/mmt-web/script/jobad/modules/interactive-viewing.js index 8b17b0ca55..8cad29edbe 100644 --- a/src/mmt-api/resources/mmt-web/script/jobad/modules/interactive-viewing.js +++ b/src/mmt-api/resources/mmt-web/script/jobad/modules/interactive-viewing.js @@ -28,7 +28,7 @@ var interactiveViewing = { }, navigateServer: function(uri) { - url = '/:admin?navigate ' + uri; + url = '/:action?navigate ' + uri; $.ajax({ 'url': url }); }, diff --git a/src/mmt-api/resources/mmtrc b/src/mmt-api/resources/mmtrc index b14c15bd08..ea480f91e7 100644 --- a/src/mmt-api/resources/mmtrc +++ b/src/mmt-api/resources/mmtrc @@ -13,7 +13,7 @@ thygraph info.kwarc.mmt.api.ontology.TheoryGraphExporter decltree info.kwarc.mmt.api.ontology.DeclarationTreeExporter index info.kwarc.mmt.api.archives.OMDocImporter pvs-omdoc info.kwarc.mmt.pvs.Plugin -diagram info.kwarc.mmt.moduleexpressions.diagdefinition.DiagramDefinition +diagram info.kwarc.mmt.moduleexpressions.publication.DiagramPublisher sms info.kwarc.mmt.stex.SmsGenerator tex-deps info.kwarc.mmt.stex.DepsGenerator @@ -23,6 +23,7 @@ tikzsvg info.kwarc.mmt.stex.TikzSvg stex-omdoc info.kwarc.mmt.stex.STeXImporter alltex info.kwarc.mmt.stex.AllTeX allpdf info.kwarc.mmt.stex.AllPdf +localpaths info.kwarc.mmt.stex.LocalPaths dummy info.kwarc.mmt.api.archives.DummyBuildTarget diff --git a/src/mmt-api/resources/tests/tests.txt b/src/mmt-api/resources/tests/tests.txt index 3cbe377aa8..b11908d095 100644 --- a/src/mmt-api/resources/tests/tests.txt +++ b/src/mmt-api/resources/tests/tests.txt @@ -1,4 +1,5 @@ info.kwarc.mmt.test.APITest info.kwarc.mmt.test.LFTest info.kwarc.mmt.odk.ODKTest -info.kwarc.mmt.odk.MitMTest \ No newline at end of file +info.kwarc.mmt.odk.MitMTest +info.kwarc.mmt.odk.LATIN2Test \ No newline at end of file diff --git a/src/mmt-api/resources/versioning/system.txt b/src/mmt-api/resources/versioning/system.txt index 49e3587fb6..e88320d7c3 100644 --- a/src/mmt-api/resources/versioning/system.txt +++ b/src/mmt-api/resources/versioning/system.txt @@ -1 +1 @@ -19.0.0 +20.0.0 diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/Component.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/Component.scala index fba2e9d37d..c64fa64baa 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/Component.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/Component.scala @@ -117,9 +117,10 @@ object NotationComponent { } } +case object MetaDataComponent extends ComponentKey("metadata") + // the following components are used only by change management case object PatternBodyComponent extends ComponentKey("pattern-body") -case object MetaDataComponent extends ComponentKey("metadata") object TermComponent { private val components = List(TypeComponent,DefComponent,DomComponent,CodComponent) diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/MMTTask.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/MMTTask.scala index 90a87eda93..261c64396e 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/MMTTask.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/MMTTask.scala @@ -12,7 +12,7 @@ trait MMTTask extends Killable { listeners.foreach {l => l(a)} } /** get all reports in reverse chronological order */ - def getReports = updates + def getReports: List[MMTTaskProgress] = updates /** the listeners to which updates are sent */ private var listeners : List[MMTTaskProgressListener] = Nil @@ -25,7 +25,7 @@ object MMTTask { * * it's preferable to write a new subclass of MMTTask, but sometimes a dummy task is more convenient */ - def generic = new MMTTask {} + def generic: MMTTask = new MMTTask {} } /** see [[MMTTask]] */ @@ -38,7 +38,7 @@ trait MMTTaskProgress trait MMTInterpretationProgress extends MMTTaskProgress { def element: StructuralElement - def sourceLine = { + def sourceLine: Option[Int] = { parser.SourceRef.get(element) map {sref => sref.region.start.line } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/Path.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/Path.scala index c57dafdfd1..ff047d4fd7 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/Path.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/Path.scala @@ -232,7 +232,16 @@ case class LocalName(steps: List[LNStep]) extends SlashFunctions[LocalName] { def toPath : String = steps.map(_.toPath).mkString("", "/", "") /** human-oriented string representation of this name, no encoding, possibly shortened */ override def toString : String = toStr(false) - def toStr(implicit shortURIs: Boolean) = steps.map(_.toStr).mkString("", "/", "") + def toStr(implicit shortURIs: Boolean) = steps.map(_.toStr).mkString("", "/", "") + + def prefixOrCreateLastSimpleStep(prefix: String): LocalName = { + val newSteps: List[LNStep] = steps match { + case beginning :+ SimpleStep(name) => beginning :+ SimpleStep(prefix + name) + case List(ComplexStep(mpath)) => List(ComplexStep(mpath.parent ? mpath.name.prefixOrCreateLastSimpleStep(prefix))) + case _ => steps :+ SimpleStep(prefix) + } + LocalName(newSteps) + } } /** a step in a LocalName */ diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/archives/BuildQueue.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/archives/BuildQueue.scala index 3ec06ce26c..b2aab2acaf 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/archives/BuildQueue.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/archives/BuildQueue.scala @@ -500,7 +500,7 @@ class BuildQueue extends ServerExtension("queue") with BuildManager { "finished" -> JSONArray(fs: _*)) } - def apply(request: ServerRequest): ServerResponse = request.path match { + def apply(request: ServerRequest): ServerResponse = request.pathForExtension match { case List("clear") => clear ServerResponse.JsonResponse(JSONNull) diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/archives/Export.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/archives/Export.scala index 911e92eecd..d7d4d921ba 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/archives/Export.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/archives/Export.scala @@ -97,7 +97,7 @@ trait Exporter extends BuildTarget {self => /** returns the output file that this exporter uses for some module */ protected def getOutFileForModule(p: MPath): Option[File] = { controller.backend.findOwningArchive(p).map {arch => - (arch / contentExporter.outDim / archives.Archive.MMTPathToContentPath(p.mainModule)).addExtension(outExt) + (arch / contentExporter.outDim / archives.Archive.MMTPathToContentPath(p.mainModule)).setExtension(outExt) } } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/archives/Git.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/archives/Git.scala index 32ba474e97..f0bad2aee0 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/archives/Git.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/archives/Git.scala @@ -37,5 +37,5 @@ object UnixGit extends Git { * @param sh the path to git's bash (may contain spaces), defaults to "sh" */ class WindowsGit(sh: String = "sh") extends Git { - def toArgs(args: String*) = List(sh, "--login", "-c", UnixGit.toArgs(args:_*).mkString("\"", " ", "\"")) + def toArgs(args: String*) = gitPath :: args.toList } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/archives/MathHub.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/archives/MathHub.scala index 69caf9eee4..4b1d56b751 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/archives/MathHub.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/archives/MathHub.scala @@ -316,13 +316,13 @@ class MathHub(val controller: Controller, var local: File, var remote: URI, var } else { // try to clone the repository or fail log(s"trying to clone $id") - val success = git(local, "clone", rp, id).success - if (!success) { + val cloneResult = git(local, "clone", rp, id) + if (!cloneResult.success) { + logError(s"git clone $rp failed") + logError(s"Error: `${cloneResult.toString}`") if (lp.exists) { - logError(s"git clone $rp failed, deleting $lp") + logError(s"Target directory $lp already exists, perhaps that was the source of the error, deleting $lp") lp.deleteDir - } else { - logError(s"git clone $rp failed") } return None } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/archives/Relational.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/archives/Relational.scala index cfc3332535..4976867aed 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/archives/Relational.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/archives/Relational.scala @@ -2,9 +2,6 @@ package info.kwarc.mmt.api.archives import info.kwarc.mmt.api._ import frontend._ -import parser._ -import utils._ -import documents._ /** an object to extract dependencies from a controller */ object Relational { @@ -12,35 +9,36 @@ object Relational { def getArchives(controller: Controller): List[Archive] = controller.backend.getStores.collect { case a: Archive => a } - def close[A](inMap: Map[A, Set[A]]): Map[A, Set[A]] = { - var changed = true - var m = inMap - while (changed) { - changed = false - m = m.map(p => (p._1, { - val n: Set[A] = p._2.flatMap(m).union(p._2) - if (n.size != p._2.size) changed = true - n - })) - } - m.filter(p => p._2.contains(p._1)) - } - def topsort[A](controller: Controller, m: Map[A, Set[A]]): List[Set[A]] = { if (m.isEmpty) Nil else { val (noDeps, rest) = m.partition(_._2.isEmpty) if (noDeps.isEmpty) { - controller.report(new Error("cyclic deps: " + close(m)) {}) + controller.report(new Error(shortMsg = "cyclic dependencies: " + m) {}) List(Set.empty, m.keySet) } else { - val fst = noDeps.keySet + val fst : Set[A] = noDeps.keySet fst :: topsort(controller, rest.map(p => (p._1, p._2.diff(fst)))) } } } - def flatTopsort[A](controller: Controller, m: Map[A, Set[A]]): List[A] = + def flatTopsort[A](controller: Controller, m: Map[A, Set[A]]): List[A] = { topsort(controller, m).flatMap(_.toList.sortBy(_.toString)) + } + + def newFlatTopsort[A](controller: Controller, m: Map[A, Set[A]]): List[A] = { + def depclosuresize(start : A) : Int = { + if (m.keySet.contains(start)) { + var sz = 1 + for (k <- m(start)) { + sz += depclosuresize(k) + } + sz + } else { 0 } + } + + topsort(controller, m).flatMap(_.toList.sortBy(d => depclosuresize(d))) + } } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/archives/ScalaCode.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/archives/ScalaCode.scala index e6660ed4f1..054e1279f1 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/archives/ScalaCode.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/archives/ScalaCode.scala @@ -3,7 +3,7 @@ package info.kwarc.mmt.api.archives import info.kwarc.mmt.api._ import info.kwarc.mmt.api.frontend._ import info.kwarc.mmt.api.utils._ - +// DELETE this along with the Integrator object? FR 2020-07-08 trait ScalaCode {self: Archive => private var loader: java.net.URLClassLoader = null diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/backend/Backend.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/backend/Backend.scala index 6263765622..5de5819eef 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/backend/Backend.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/backend/Backend.scala @@ -36,6 +36,10 @@ class Backend(extman: ExtensionManager, val report: info.kwarc.mmt.api.frontend. /** retrieves all Stores */ def getStores: List[Storage] = stores + def clear { + stores.foreach(_.clear) + } + /** releases all resources held by storages */ def cleanup { stores.foreach(_.destroy) @@ -142,6 +146,12 @@ class Backend(extman: ExtensionManager, val report: info.kwarc.mmt.api.frontend. } val arch = new Archive(root, properties, report) addStore(arch) + arch.properties.get("classpath").foreach {cp => + val rF = root / cp + log("loading realization archive" + rF) + val ra = new RealizationArchive(rF) + addStore(ra) + } List(arch) case None => log(root + " is not an archive - recursing") @@ -188,12 +198,17 @@ class Backend(extman: ExtensionManager, val report: info.kwarc.mmt.api.frontend. /** unregisters all archives */ def closeAllArchives { - stores foreach { - case a: Archive => closeArchive(a.id) - case _ => - } + getArchives foreach {a => closeArchive(a.id)} } + /** closes all archives, then opens them again */ + def reopenArchives { + val paths = getArchives.map(_.root) + closeAllArchives + paths foreach {p => openArchive(p)} + } + + /** retrieve an [[Archive]] by its id */ def getArchive(id: String): Option[Archive] = stores collectFirst { case a: Archive if a.properties.get("id").contains(id) => a @@ -256,13 +271,6 @@ class Backend(extman: ExtensionManager, val report: info.kwarc.mmt.api.frontend. } } - /** creates and registers a RealizationArchive */ - def openRealizationArchive(file: File) { - log("loading realization archive" + file) - val ra = new RealizationArchive(file) - addStore(ra) - } - /** auxiliary function of openArchive */ private def extractMar(file: File, newRoot: File) { log("unpacking archive " + file + " to " + newRoot) diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/backend/Storage.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/backend/Storage.scala index e1af184485..8f7df01c23 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/backend/Storage.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/backend/Storage.scala @@ -51,76 +51,11 @@ abstract class Storage { //TODO: method for querying // def query(q: ???): Iterator[Path] - /** called to release all held resources, override as needed */ - def destroy {} -} + /** called to reset this class, override to forget all cached information, e.g., files loaded from disk */ + def clear {} -/** a variant of a [[Storage]] that loads Scala objects from the class path */ -trait RealizationStorage { - /** the class loaded used to load semantic objects - * - * By making this a 'def', every load may creates a fresh class loader. - * That can be helpful if new class files are produced at run time (e.g., with the [[ScalaCompiler]]). - */ - def loader: java.lang.ClassLoader - /** - * @param p the path to use in error messages - */ - def loadObject(p: MPath): SemanticObject = { - val cls = SemanticObject.mmtToJava(p) - loadSemanticObject(cls, p) - } - - /** gets the object for a java class name (cls must be in Scala's syntax for java .class files) */ - def loadClass(cls: String, p: Path): Class[_] = { - try { - Class.forName(cls, true, loader) - } catch { - case e: ClassNotFoundException => - throw NotApplicable("class " + cls + " not found") - case e: ExceptionInInitializerError => - throw BackendError(s"class $cls for $p exists, but an error occurred when initializing it", p).setCausedBy(e) - case e: LinkageError => - throw BackendError(s"class $cls for $p exists, but an error occurred when linking it", p).setCausedBy(e) - case e: Error => - throw BackendError(s"class $cls for $p exists, but: " + e.getMessage, p).setCausedBy(e) - } - } - /** gets the object for a java class name (cls must be in Scala's syntax for java .class files) */ - protected def loadSemanticObject(cls: String, p: Path): SemanticObject = { - val c = loadClass(cls, p) - val r = try { - c.getField("MODULE$").get(null) - } catch { - case e: Exception => - throw BackendError(s"class $cls for $p exists, but an error occurred when accessing the Scala object", p).setCausedBy(e) - } - r match { - case r: SemanticObject => - try { - r.init - } catch { - case e: Exception => - throw BackendError(s"semantic object $cls for $p exists, but an error occurred when initializing it", p).setCausedBy(e) - } - r - case _ => - throw BackendError(s"object $cls for $p exists, but it is not an instance of SemanticObject", p) - } - } -} - - -/** a Storage that retrieves file URIs from the local system */ -case class LocalSystem(base: URI) extends Storage { - val localBase = URI(Some("file"), None, Nil, abs = true, None, None) - - def load(path: Path)(implicit controller: Controller) { - val uri = base.resolve(path.doc.uri) - val _ = getSuffix(localBase, uri) - val file = new java.io.File(uri.toJava) - loadXML(uri, path.doc, File.Reader(file)) - } + /** called to release all held resources before forgetting this class, override as needed */ + def destroy {} } /** a Storage that retrieves repository URIs from the local working copy */ @@ -187,12 +122,71 @@ class ArchiveNarrationStorage(a: Archive, folderName: String) extends {val nBase } } +/** a variant of a [[Storage]] that loads Scala objects from the class path */ +trait RealizationStorage { + def getLoader: java.lang.ClassLoader + // this must be a val to make sure all classes are loaded by the same class loader (the same class loaded by different class loaders are distinct) + private var loader = getLoader + /** the only way to unload classes is to let them and the class loader be garbage-collected; so this method creates a fresh copy of the class loader */ + def resetLoader { + loader = getLoader + } + /** + * @param p the path to use in error messages + */ + def loadObject(p: MPath): SemanticObject = { + val cls = SemanticObject.mmtToJava(p) + loadSemanticObject(cls, p) + } + + /** gets the object for a java class name (cls must be in Scala's syntax for java .class files) */ + def loadClass(cls: String, p: Path): Class[_] = { + try { + Class.forName(cls, true, loader) + } catch { + case e: ClassNotFoundException => + throw NotApplicable("class " + cls + " not found") + case e: ExceptionInInitializerError => + throw BackendError(s"class $cls for $p exists, but an error occurred when initializing it", p).setCausedBy(e) + case e: LinkageError => + throw BackendError(s"class $cls for $p exists, but an error occurred when linking it", p).setCausedBy(e) + case e: Error => + throw BackendError(s"class $cls for $p exists, but: " + e.getMessage, p).setCausedBy(e) + } + } + /** gets the object for a java class name (cls must be in Scala's syntax for java .class files) */ + protected def loadSemanticObject(cls: String, p: Path): SemanticObject = { + val c = loadClass(cls, p) + val r = try { + c.getField("MODULE$").get(null) + } catch { + case e: Exception => + throw BackendError(s"class $cls for $p exists, but an error occurred when accessing the Scala object", p).setCausedBy(e) + } + r match { + case r: SemanticObject => + try { + r.init + } catch { + case e: Exception => + throw BackendError(s"semantic object $cls for $p exists, but an error occurred when initializing it", p).setCausedBy(e) + } + r + case _ => + throw BackendError(s"object $cls for $p exists, but it is not an instance of SemanticObject", p) + } + } +} + /** loads a realization from a Java Class Loader and dynamically creates a [[uom.RealizationInScala]] for it */ class RealizationArchive(file: File) extends Storage with RealizationStorage { - override def toString = "RealizationArchive for " + file + override def toString = "realization archive for " + file - // this must be a val to make sure all classes are loaded by the same class loader (the same class loaded by different class loaders are distinct) - val loader: ClassLoader = try { + override def clear { + resetLoader + } + + def getLoader: ClassLoader = try { val optCl = Option(getClass.getClassLoader) // the class loader that loaded this class, may be null for bootstrap class loader optCl match { @@ -235,5 +229,18 @@ object DefaultRealizationLoader extends Storage with RealizationStorage { def load(path: Path)(implicit controller: Controller) { throw NotApplicable("can only load rules") } - def loader: ClassLoader = this.getClass.getClassLoader + def getLoader: ClassLoader = this.getClass.getClassLoader +} + +/** a Storage that retrieves file URIs from the local system */ +case class LocalSystem(base: URI) extends Storage { + val localBase = URI(Some("file"), None, Nil, abs = true, None, None) + + def load(path: Path)(implicit controller: Controller) { + val uri = base.resolve(path.doc.uri) + val _ = getSuffix(localBase, uri) + val file = new java.io.File(uri.toJava) + loadXML(uri, path.doc, File.Reader(file)) + } } + diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/checking/Interpreter.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/checking/Interpreter.scala index 2b0878f252..23bc500ea3 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/checking/Interpreter.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/checking/Interpreter.scala @@ -28,6 +28,14 @@ abstract class Interpreter extends Importer { /** object interpretation */ def apply(pu: ParsingUnit)(implicit errorCont: ErrorHandler): CheckingResult + /** convenience method for parsing and checking a term in context */ + def fromObjectString(context: Context, term: String, errorCont: ErrorHandler = ErrorThrower): CheckingResult = { + val iic = new InterpretationInstructionContext(controller.getNamespaceMap) + val pu = ParsingUnit(SourceRef.anonymous(term), context, term, iic) + val cr = apply(pu)(errorCont) + cr + } + def simplifier = controller.simplifier /** converts the interface of [[Importer]] to the one of [[Parser]] */ diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/checking/MMTStructureChecker.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/checking/MMTStructureChecker.scala index ceab043eed..00d46f2b46 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/checking/MMTStructureChecker.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/checking/MMTStructureChecker.scala @@ -103,7 +103,8 @@ class MMTStructureChecker(objectChecker: ObjectChecker) extends Checker(objectCh val rules = RuleSet.collectRules(controller,Context.empty) implicit val env = new ExtendedCheckingEnvironment(ce, objectChecker, rules, th.path) implicit val task = ce.task - checkContext(Context.empty, controller.getExtraInnerContext(th)) + val c = controller.getExtraInnerContext(th) + checkContextTop(Context.empty, c) } private def prepareCheck(e: StructuralElement)(implicit ce: CheckingEnvironment): (Context, ExtendedCheckingEnvironment) = { @@ -143,7 +144,7 @@ class MMTStructureChecker(objectChecker: ObjectChecker) extends Checker(objectCh UncheckedElement.set(d) } // check children - val additionalContext = checkContext(context, controller.getExtraInnerContext(c)) + val additionalContext = checkContextTop(context, controller.getExtraInnerContext(c)) val (contextI, envI) = prepareCheckExtendContext(context, env, additionalContext) logGroup { tDecls foreach {d => @@ -382,7 +383,7 @@ class MMTStructureChecker(objectChecker: ObjectChecker) extends Checker(objectCh checkTheory(Some(CPath(t.path, TypeComponent)), context, OMMOD(mt)) contextMeta = contextMeta ++ mt } - checkContext(contextMeta, t.parameters) + checkContextTop(contextMeta, t.parameters) t.df map {d => checkTheory(Some(CPath(t.path, DefComponent)), contextMeta++t.parameters, d)} // this is redundant on a clean check because e is empty then; case v: View => @@ -669,7 +670,7 @@ class MMTStructureChecker(objectChecker: ObjectChecker) extends Checker(objectCh cpath foreach {cp => setAnalyzed(cp, tR)} tR case ComplexTheory(body) => - val bodyR = checkContext(context, body) + val bodyR = checkContextTop(context, body) val tR = ComplexTheory(bodyR) cpath foreach {cp => setAnalyzed(cp, tR)} tR @@ -780,7 +781,7 @@ class MMTStructureChecker(objectChecker: ObjectChecker) extends Checker(objectCh */ private def checkTermTop(context: Context, t: Term)(implicit env: ExtendedCheckingEnvironment): (Term, Boolean) = { env.ce.errorCont.mark - val tR = checkTerm(context, t) + val tR = checkTerm(context, t)(env,t) (tR, env.ce.errorCont.noErrorsAdded) } @@ -789,10 +790,11 @@ class MMTStructureChecker(objectChecker: ObjectChecker) extends Checker(objectCh * * @param context the context * @param s the term + * @param rootObj the root term on which the method was called * @return the reconstructed term */ //TODO make more reusable (maybe by moving to RuleBasedChecker?) - private def checkTerm(context: Context, s: Term)(implicit env: ExtendedCheckingEnvironment): Term = { + private def checkTerm(context: Context, s: Term)(implicit env: ExtendedCheckingEnvironment, rootObj: Obj): Term = { s match { case OMMOD(p) => if (p.doc.uri.scheme contains "scala") { @@ -846,11 +848,11 @@ class MMTStructureChecker(objectChecker: ObjectChecker) extends Checker(objectCh case OML(name, tp, df,_,_) => OML(name, tp.map(checkTerm(context, _)), df.map(checkTerm(context, _))) case OMV(name) => if (!context.isDeclared(name)) - env.errorCont(InvalidObject(s, "variable is not declared")) + env.errorCont(InvalidObject(rootObj, s"variable $name is not declared")) s case ComplexTerm(c, subs, bound, args) => val subsR = subs map { case Sub(v, t) => Sub(v, checkTerm(context, t)) } - val boundR = checkContext(context, bound) + val boundR = checkContextTop(context, bound) val argsR = args map { a => checkTerm(context ++ bound, a) } env.pCont(c) ComplexTerm(c, subsR, boundR, argsR).from(s) @@ -860,7 +862,7 @@ class MMTStructureChecker(objectChecker: ObjectChecker) extends Checker(objectCh OMA(fR, argsR).from(s) case OMBINDC(bin, con, args) => val binR = checkTerm(context, bin) - val conR = checkContext(context, con) + val conR = checkContextTop(context, con) val contextCon = context ++ conR val argsR = args map {a => checkTerm(contextCon, a)} OMBINDC(binR, conR, argsR).from(s) @@ -893,6 +895,9 @@ class MMTStructureChecker(objectChecker: ObjectChecker) extends Checker(objectCh } } + private def checkContextTop(context: Context, con: Context)(implicit env: ExtendedCheckingEnvironment): Context = { + checkContext(context, con)(env, con) + } /** Checks structural well-formedness of a context relative to a context. * * @param context the context of con @@ -900,7 +905,7 @@ class MMTStructureChecker(objectChecker: ObjectChecker) extends Checker(objectCh * @return the reconstructed context * if context is valid, then this succeeds iff context ++ con is valid */ - private def checkContext(context: Context, con: Context)(implicit env: ExtendedCheckingEnvironment): Context = { + private def checkContext(context: Context, con: Context)(implicit env: ExtendedCheckingEnvironment, rootObj: Obj): Context = { var ret = Context.empty con.foreach {vd => val currentContext = context ++ ret @@ -1007,7 +1012,7 @@ class MMTStructureChecker(objectChecker: ObjectChecker) extends Checker(objectCh // finally, check the individual maps in subs subs.map { case Sub(n, t) => - val tR = checkTerm(context ++ to, t) + val tR = checkTerm(context ++ to, t)(env, subs) Sub(n, tR) } } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/checking/Rules.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/checking/Rules.scala index dd5eed11c7..c5ffb240a4 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/checking/Rules.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/checking/Rules.scala @@ -220,9 +220,10 @@ abstract class InferenceAndTypingRule(h: GlobalName, t: GlobalName) extends Infe /** * @param tp the expected type * pre: if provided, tp is covered - * @param covered whether tm is covered (if true and tp provided, typing is covered too) + * @param covered whether tm is covered * @return the inferred type and the result of type-checking - * post: if the latter is Some(true), typing is covered wrt to the provided and the returned type + * post: if the former is Some(tpI), typing tm:tpI is covered + * if tp provided and the latter is Some(true), tm:tp is covered */ def apply(solver: Solver, tm: Term, tp: Option[Term], covered: Boolean)(implicit stack: Stack, history: History): (Option[Term], Option[Boolean]) diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/checking/Solver.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/checking/Solver.scala index 050580ce68..ad4acf6eef 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/checking/Solver.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/checking/Solver.scala @@ -43,9 +43,7 @@ import scala.collection.mutable.HashSet * Unsolvable constraints are delayed and reactivated if later solving of unknowns provides further information. * * @param controller an MMT controller that is used to look up Rule's and Constant's. No changes are made to the controller. - * @param context the constant context - * @param initUnknowns the unknown context - * unknown variables may occur in the types of later unknowns. + * @param checkingUnit the judgment to check * * See [[CheckingUnit]] for the semantics of the contexts. * @@ -954,7 +952,7 @@ object Solver { } /** create an unknown whose solution may contain certain variables */ - def makeUnknown(name: LocalName, args: List[LocalName]) = OMAorAny(OMV(name), args.map(OMV(_))) + def makeUnknown(name: LocalName, args: List[Term]) = OMAorAny(OMV(name), args) /** * tests a term for the occurrence of an unknown variables that can be isolated by applying solution rules diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/checking/SolverAlgorithms.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/checking/SolverAlgorithms.scala index 5ea5ddc54a..be61312cfb 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/checking/SolverAlgorithms.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/checking/SolverAlgorithms.scala @@ -242,7 +242,7 @@ trait SolverAlgorithms {self: Solver => case tm => logAndHistoryGroup { history += "trying typing rules" - //TODO something is weird here: only the first applicable rule is every tried; is tryAllRules redundant? + //TODO something is weird here: only the first applicable rule is ever tried; is tryAllRules redundant? val tmT = tryAllRules(typingRules,tp) {(rule,tpS,h) => try { rule(this)(tm,tpS)(stack,h) @@ -1195,7 +1195,7 @@ trait SolverAlgorithms {self: Solver => } /** simplifies two terms until a rule is applicable and then applies that rule - * @param A the type of rules + * @tparam A the type of rules * @param rules the rules to try * @param tm1 the first term * @param tm2 the second term diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/Controller.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/Controller.scala index 30046c94da..57488b8a68 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/Controller.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/Controller.scala @@ -664,6 +664,7 @@ class Controller(report_ : Report = new Report) extends ROController with Action /** clears the state */ def clear { memory.clear + backend.clear notifyListeners.onClear } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/ExtensionManager.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/ExtensionManager.scala index 9255749cc8..b36e60505a 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/ExtensionManager.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/ExtensionManager.scala @@ -335,7 +335,7 @@ class ExtensionManager(controller: Controller) extends Logger { ExecFileCompanion, ScalaCompanion, MBTCompanion, InspectDefineCompanion, DefineCompanion, EndDefineCompanion, DoCompanion, CheckCompanion, CheckTermCompanion, NavigateCompanion, CompareCompanion, - ShowArchivesCompanion, LocalCompanion, AddArchiveCompanion, AddMathPathFSCompanion, AddMathPathJavaCompanion, ReadCompanion, + ShowArchivesCompanion, LocalCompanion, AddArchiveCompanion, AddMathPathFSCompanion, ReadCompanion, ServerInfoActionCompanion, ServerOnCompanion, ServerOffCompanion, MMTInfoCompanion, MMTLegalCompanion, MMTVersionCompanion, ClearConsoleCompanion, PrintAllCompanion, PrintAllXMLCompanion, PrintConfigCompanion, HelpActionCompanion, ShowLMHCompanion, SetLMHRootCompanion, LMHInitCompanion, LMHOpenCompanion, LMHUseCompanion, LMHInstallCompanion, LMHListCompanion, LMHPullCompanion, LMHPushCompanion, LMHSetRemoteCompanion, LMHListRemoteCompanion, diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/Log.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/Log.scala index 43c96a7311..c3c1f87f53 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/Log.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/Log.scala @@ -13,19 +13,19 @@ trait Logger { def logPrefix: String /** logs a message with this logger's logprefix */ - protected def log(s: => String, subgroup: Option[String] = None) = + protected def log(s: => String, subgroup: Option[String] = None): Unit = report(logPrefix + subgroup.map("-" + _).getOrElse(""), s) /** temporary logging - always logged */ // calls to this method are for debugging; if they are committed, they should be removed - protected def logTemp(s: => String) = + protected def logTemp(s: => String): Unit = report("temp", s"($logPrefix) $s") /** log as an error message */ - protected def logError(s: => String) = report("error", s"($logPrefix) $s") + protected def logError(s: => String): Unit = report("error", s"($logPrefix) $s") /** logs an error - always logged */ - protected def log(e: Error) = report(e) + protected def log(e: Error): Unit = report(e) /** wraps around a group to create nested logging */ protected def logGroup[A](a: => A): A = { diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/actions/MathPathAction.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/actions/MathPathAction.scala index 9db9703231..3d3cb9c1dd 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/actions/MathPathAction.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/frontend/actions/MathPathAction.scala @@ -8,7 +8,7 @@ import info.kwarc.mmt.api.parser.ParsingStream import info.kwarc.mmt.api.utils.{File, FilePath, URI} /** Shared base class for Actions updating the mathpath */ -sealed abstract class MathPathAction extends Action {} +sealed abstract class MathPathAction extends Action case object ShowArchives extends MathPathAction with ResponsiveAction { def apply() { @@ -60,15 +60,6 @@ object AddMathPathFSCompanion extends ActionCompanion("add mathpath entry for a def parserActual(implicit state: ActionState) = uri ~ file ^^ { case u ~ f => AddMathPathFS(u, f) } } -case class AddMathPathJava(javapath: File) extends MathPathAction { - def apply() {controller.backend.openRealizationArchive(javapath)} - def toParseString = "mathpath java " + javapath -} -object AddMathPathJavaCompanion extends ActionCompanion("add catalog entry for realizations in Java", "mathpath java") { - import Action._ - def parserActual(implicit state: ActionState) = file ^^ { f => AddMathPathJava(f) } -} - case class Read(file: File, interpret: Boolean) extends MathPathAction { def apply() { if (!file.isFile) @@ -96,15 +87,8 @@ trait MathPathActionHandling {self: Controller => /** add an archive plus its optional classpath and notify listeners, handling [[AddArchive]] */ def addArchive(root: File): List[Archive] = { val archs = backend.openArchive(root) - archs.foreach { a => - report("user", "opened archive " + a.root) - a.properties.get("classpath").foreach { cp => - val rF = a.root / cp - backend.openRealizationArchive(rF) - report("user", "... with realizations in folder " + rF) - } - notifyListeners.onArchiveOpen(a) - } + archs.foreach {a => report("user", "opened archive " + a.root)} + archs.foreach {a => notifyListeners.onArchiveOpen(a)} // TODO this should happen in backend archs } } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/gui/BackendPane.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/gui/BackendPane.scala index 372d5e1af7..1bafcbf3b2 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/gui/BackendPane.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/gui/BackendPane.scala @@ -13,7 +13,7 @@ class BackendPane(backend: Backend) extends {val ta = new JTextArea} with JScrol case a: Archive => s += "archive " + a.id + " " + a.rootString + "\n" case ra: RealizationStorage => - s += ra.toString + s += ra.toString + "\n" case l: LocalCopy => s += "mathpath fs " + l.localBase + " " + l.base + "\n" case a => diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/metadata/Metadata.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/metadata/Metadata.scala index 903cea7fe0..93f2b43b50 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/metadata/Metadata.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/metadata/Metadata.scala @@ -30,13 +30,13 @@ class MetaData { } def update(key: GlobalName, values: Obj*) { delete(key) - values map {value => add(new MetaDatum(key, value))} + values foreach { value => add(MetaDatum(key, value))} } def update(key: GlobalName, value: URI) { delete(key) add(Link(key, value)) } - def keys = data.map(_.key).distinct + def keys: List[GlobalName] = data.map(_.key).distinct /** get all metadata except tags */ def getAll : List[MetaDatum] = data.filter(_.value != null) /** get metadata for a certain key */ @@ -51,7 +51,7 @@ class MetaData { case _ => Nil } def toNode = if (data.isEmpty) Nil else {data.map(_.toNode)} - override def toString = data.map(_.toString).mkString(", ") + override def toString: String = data.map(_.toString).mkString(", ") } /** helper object */ @@ -84,7 +84,7 @@ object MetaData { def apply(pairs : MetaDatum*) : MetaData = { val metadata = new MetaData metadata.add(pairs: _*) - return metadata + metadata } } @@ -93,13 +93,13 @@ object MetaData { * @param key the key, must be a symbol in the theory of the respective metadata theory * @param value the object (may be null, which indicates tags) */ -class MetaDatum(val key: GlobalName, val value: Obj) { - def toNode = this match { +case class MetaDatum(key: GlobalName, value: Obj) { + def toNode: Elem = this match { case Link(key, uri) => case Tag(key) => case _ => {value.toOBJNode} } - override def toString = { + override def toString: String = { if (value == null) key.toString else {key.toString + " -> " + value.toString} } @@ -107,7 +107,7 @@ class MetaDatum(val key: GlobalName, val value: Obj) { /** helper object */ object MetaDatum { - val keyBase = documents.NarrativeMetadata.keyBase + val keyBase: MPath = documents.NarrativeMetadata.keyBase /** parses a MetaDatum */ def parse(node: Node, nsMap: NamespaceMap) : MetaDatum = xml.trimOneLevel(node) match { case => @@ -116,10 +116,10 @@ object MetaDatum { case => val key = Path.parseS(xml.attr(node, "property"), nsMap(keyBase)) Tag(key) - case n: Elem if n.label == "meta" && n.child.length >= 1 => + case n: Elem if n.label == "meta" && n.child.nonEmpty => // some text is split into several child nodes val key = Path.parseS(xml.attr(node, "property"), nsMap(keyBase)) - val literal = n.child(0) + val literal = n.child.head val value = if (literal.isInstanceOf[Elem] && n.child.length == 1) Obj.parseTerm(literal, nsMap) else diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/modules/Theory.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/modules/Theory.scala index 5ff00d947c..132650b8cd 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/modules/Theory.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/modules/Theory.scala @@ -8,40 +8,79 @@ import utils._ /** convenience functions for explicitly omitting constructor arguments (default arguments tend to mask implementation errors) */ object Theory { - def noMeta: Option[MPath] = None - def noBase = new TermContainer - def noParams = new ContextContainer + def noMeta: Option[MPath] = None - def apply(doc: DPath, n: LocalName, mt: Option[MPath], params: ContextContainer = noParams, df: TermContainer = noBase) = - new Theory(doc, n, mt, params, df) - - def empty(doc: DPath, n: LocalName, mt: Option[MPath]) = apply(doc, n, mt) + def noBase = new TermContainer + + def noParams = new ContextContainer + + def apply(doc: DPath,n: LocalName,mt: Option[MPath],params: ContextContainer = noParams,df: TermContainer = noBase) = + new Theory(doc,n,mt,params,df) + + def empty(doc: DPath,n: LocalName,mt: Option[MPath]) = apply(doc,n,mt) /** if this theory is flattened already, an iterator over all constants is a set of theories - * @param paths the theories and the morphisms via which they are visible (if not direct) - * @param lup lookup to retrieve included theories + * + * @param paths the theories and the morphisms via which they are visible (if not direct) + * @param lup lookup to retrieve included theories * @param withIncludes if false, only local declarations; if true, also included declarations - * @param seen theories to exclude (used by recursive implementation) + * @param seen theories to exclude (used by recursive implementation) */ - // not used, does not work yet - def flatIterator(paths: Iterable[(MPath,Option[Term])], lup: Lookup, withIncludes: Boolean = true, seen: List[MPath] = Nil): Iterator[(Declaration,Option[Term])] = { - var seenHere: List[MPath] = seen - paths.iterator.flatMap {case (p,via) => - if (seen.contains(p)) Iterator.empty else { - seenHere ::= p - val thy = lup.getAs(classOf[Theory], p) - val mtI = thy.meta.iterator.flatMap {m => - flatIterator(List((m,None)), lup, false, seenHere) + + import scala.collection.mutable.HashSet + + private def flatDeclarations(lup: Lookup,from: MPath,via: Option[Term],withMeta: Boolean,withIncludes: Boolean,seen: HashSet[MPath]): List[(Declaration,Option[Term])] = { + if (seen.contains(from)) return Nil + seen += from + val thy = lup.getAs(classOf[Theory],from) + val mtDs = if (withMeta) { + thy.meta.toList.flatMap {m => + flatDeclarations(lup,m,None,true,true,seen) + } + } else Nil + mtDs ::: thy.getDeclarations.flatMap { + case Include(id) => + if (seen.contains(id.from)) Nil + else { + flatDeclarations(lup,id.from,id.df,false,true,seen) } - mtI ++ thy.getDeclarations.iterator.flatMap { - case Include(id) => - flatIterator(List((id.from, id.df)), lup, false, seenHere) - case rc: RuleConstant => Iterator((rc,via)) - case c: Constant => Iterator((c,via)) - case d: DerivedDeclaration => Iterator.empty - case s: Structure => Iterator.empty + case rc: RuleConstant => List((rc,via)) + case c: Constant => List((c,via)) + case d: DerivedDeclaration => Nil + case s: Structure => Nil + } + } + + /** if this theory is flattened already, an iterator over all constants is a theory + * + * @param path the theory + * @param lup lookup to retrieve included theories + * @param withMeta if true, also declarations of meta-theory + * @param withIncludes if true, also included declarations + * @return all pairs of included declaration and (for defined includes) the mediating morphism + */ + def flatDeclarations(lup: Lookup, path: MPath, withMeta: Boolean = false, withIncludes: Boolean = false): List[(Declaration,Option[Term])] = { + val seen = new HashSet[MPath] + flatDeclarations(lup, path, None, withMeta, withIncludes, seen) + } + + /** list of all undefined constants and their types (with definitions expanded) + * contains: constants from included theories + * does not contain: constants from meta-theory, defined constants + */ + def primitiveConstants(path: MPath, lup: Lookup): List[(GlobalName,Option[Term])] = { + var defined: List[GlobalName] = Nil + flatDeclarations(lup, path, false, true) flatMap { + case (c: Constant, None) => + c.df match { + case None => + val tpE = c.tp map {t => lup.ExpandDefinitions(t, p => defined.contains(p))} + Iterator((c.path, tpE)) + case Some(df) => + defined ::= c.path + Iterator.empty } - } + case _ => Iterator.empty } } } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/modules/View.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/modules/View.scala index 8c78a8a61f..f30d690c8f 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/modules/View.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/modules/View.scala @@ -44,7 +44,7 @@ class View(doc : DPath, name : LocalName, val fromC : TermContainer, val toC : T object View { def apply(doc : DPath, name : LocalName, from : Term, to : Term, isImplicit: Boolean) = - new View(doc, name, TermContainer(from), TermContainer(to), new TermContainer(), isImplicit) - def apply(doc : DPath, name : LocalName, from : Term, to : Term, df: Option[Term], isImplicit: Boolean) = - new View(doc, name, TermContainer(from), TermContainer(to), TermContainer(df), isImplicit) + new View(doc, name, TermContainer(from), TermContainer(to), TermContainer.empty(), isImplicit) + def apply(doc : DPath, name : LocalName, from : Term, to : Term, df: TermContainer, isImplicit: Boolean) = + new View(doc, name, TermContainer(from), TermContainer(to), df, isImplicit) } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Arity.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Arity.scala index cec0844ad0..cbc795b1ad 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Arity.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Arity.scala @@ -9,7 +9,6 @@ import objects._ * @param subargs arguments before the context * @param variables the variable positions in order * @param arguments arguments after the context - * @param attribution true if it can attribute an object * * special cases and analogs in the OpenMath role system for a symbol s: * no arguments, variables: constant, s @@ -19,12 +18,12 @@ import objects._ */ case class Arity(subargs: List[ArgumentComponent], variables: List[VariableComponent], - arguments: List[ArgumentComponent], attribution: Boolean) { + arguments: List[ArgumentComponent]) { def components = subargs ::: variables ::: arguments def length = components.length - def isConstant = (! attribution) && subargs.isEmpty && arguments.isEmpty && variables.isEmpty - def isApplication = (! attribution) && subargs.isEmpty && variables.isEmpty && (! arguments.isEmpty) - def isPlainBinder = (! attribution) && subargs.isEmpty && (! variables.isEmpty) && arguments.length == 1 + def isConstant = subargs.isEmpty && arguments.isEmpty && variables.isEmpty + def isApplication = subargs.isEmpty && variables.isEmpty && (! arguments.isEmpty) + def isPlainBinder = subargs.isEmpty && (! variables.isEmpty) && arguments.length == 1 def numSeqSubs = subargs.count(_.isInstanceOf[SeqArg]) def numSeqArgs = arguments.count(_.isInstanceOf[SeqArg]) def numSeqVars = variables.count { @@ -172,7 +171,7 @@ case class Arity(subargs: List[ArgumentComponent], * * pre: canHandle(vars, args) == true */ - def flatten(markers: List[Marker], subs: Int, vars: Int, args: Int, attrib: Boolean) : List[Marker] = { + def flatten(markers: List[Marker], subs: Int, vars: Int, args: Int) : List[Marker] = { val (perSeqSub, seqSubCutOff) = distributeArgs(subargs, args) val (perSeqVar, seqVarCutOff) = distributeVars(vars) val (perSeqArg, seqArgCutOff) = distributeArgs(arguments, args) @@ -193,7 +192,6 @@ case class Arity(subargs: List[ArgumentComponent], val first = remap(v.number) utils.insertSep((0 until length).toList.map(i => v.makeCorrespondingSingleVar(first+i,remap)), sep) } - case AttributedObject => if (attrib) List(AttributedObject) else Nil case d: Delimiter => List(d) case p: PresentationMarker => List(p flatMap flattenOne) @@ -220,9 +218,8 @@ case class Arity(subargs: List[ArgumentComponent], object Arity { import CommonMarkerProperties.noProps - def constant = Arity(Nil,Nil, Nil,false) - def plainApplication = Arity(Nil, Nil, List(SimpSeqArg(1,Delim(""), noProps)), false) - def plainBinder = Arity(Nil, List(Var(1,false,Some(Delim("")), noProps)), List(SimpArg(2, noProps)), false) - def attribution = Arity(Nil, Nil, List(SimpArg(1, noProps)),true) + def constant = Arity(Nil,Nil, Nil) + def plainApplication = Arity(Nil, Nil, List(SimpSeqArg(1,Delim(""), noProps))) + def plainBinder = Arity(Nil, List(Var(1,false,Some(Delim("")), noProps)), List(SimpArg(2, noProps))) } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Notation.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Notation.scala index 64c2bf522e..bccf23b59e 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Notation.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Notation.scala @@ -6,9 +6,9 @@ case class InvalidNotation(msg: String) extends java.lang.Throwable /** * scope where a notation is applicable - * variant : optionally the name of this notation variant - * languages : the languages where this notation is applicable (e.g. tex, mmt, lf, mathml) - * priority : the priority of this notation when looking for a default notation + * variant: optionally the name of this notation variant + * languages: the languages where this notation is applicable (e.g. tex, mmt, lf, mathml) + * priority: the priority of this notation when looking for a default notation */ case class NotationScope(variant : Option[String], languages : List[String], priority : Int) { def toNode = if (a.number > lastVar) args ::= a @@ -61,8 +61,6 @@ case class TextNotation(fixity: Fixity, precedence: Precedence, meta: Option[MPa } case v: VariableComponent => vars ::= v - case AttributedObject => - attrib += 1 case _ => } // note: now, if varPositions.isEmpty, then subs.isEmpty @@ -91,32 +89,32 @@ case class TextNotation(fixity: Fixity, precedence: Precedence, meta: Option[MPa var lastSub = subs.lastOption.map(_.number).getOrElse(0) val implsBeforeVar = ((lastSub+1) until firstVar).toList.map {i => ImplicitArg(i)} subs = subs ::: implsBeforeVar - // the attribution - val attribution = attrib > 0 - Arity(subs.distinct, vars.distinct, args.distinct, attribution) + Arity(subs.distinct, vars.distinct, args.distinct) } /** @return the list of markers that should be used for parsing */ lazy val parsingMarkers = markers flatMap { case _:PresentationMarker => Nil // there should not be any presentation markers in notations used for parsing case _:ImplicitArg => Nil // remove the implicit markers - case AttributedObject => Nil // attributed variables are handled explicitly by variable parsing case v:VerbalizationMarker => v.toParsing case m => List(m) } lazy val presentationMarkers = PresentationMarker.introducePresentationMarkers(markers) - def toText = { + def toText: String = { val (fixityString, argumentString) = fixity.asString val metaStr = meta.map("meta " + _.toPath).getOrElse("") - val precStr = if (precedence != Precedence.integer(0)) " prec " + precedence.toString else "" - val fixStr = if (fixityString == "mixfix") "" else " %%"+fixityString - metaStr + fixStr + " " + argumentString + precStr + + val precStr = if (precedence != Precedence.integer(0)) "prec " + precedence.toString else "" + val fixStr = if (fixityString == "mixfix") "" else "%%"+fixityString + val blockStr = if (block) "block" else "" + + List(metaStr, blockStr, fixStr, argumentString, precStr).filter(_.nonEmpty).mkString(" ") } override def toString = toText + " (markers are: " + markers.map(_.toString).mkString(" ") + ")" def toNode = { val (fixityString, argumentString) = fixity.asString {scope.toNode} } @@ -129,16 +127,15 @@ case class TextNotation(fixity: Fixity, precedence: Precedence, meta: Option[MPa case a: ImplicitArg => case _:SeqArg => return i+1 case _: Var => return i+1 - case AttributedObject => case d: Delimiter => return i case _:PresentationMarker => // impossible case _:VerbalizationMarker => //impossible } i } - /** there are argumetns before the first delimiter */ + /** there are arguments before the first delimiter */ def isLeftOpen = openArgs(false) > 0 - /** there are argumetns after the last delimiter */ + /** there are arguments after the last delimiter */ def isRightOpen = openArgs(true) > 0 /** true if there is definitely a delimiter (i.e., not just a sequence separator) */ @@ -147,25 +144,24 @@ case class TextNotation(fixity: Fixity, precedence: Precedence, meta: Option[MPa case _ => false } - /** @return true if this arity can present ComplexTerm(name, subs, vars, args) and has an attribution if necessary */ - def canHandle(subs: Int, vars: Int, args: Int, att: Boolean) = { + /** @return true if this arity can present ComplexTerm(name, subs, vars, args) */ + def canHandle(subs: Int, vars: Int, args: Int) = { (arity.numNormalSubs == subs || (arity.numNormalSubs < subs && arity.numSeqSubs >= 1)) && (arity.numNormalVars == vars || (arity.numNormalVars < vars && arity.numSeqVars >= 1)) && (arity.numNormalArgs == args || (arity.numNormalArgs < args && arity.numSeqArgs >= 1)) && - (hasDelimiter || arity.numSeqArgs == 0 || args > arity.numNormalArgs + arity.numSeqArgs) && // this hacky case precludes confusion when flexary operators would disappear in the presentation - (! att || arity.attribution) + (hasDelimiter || arity.numSeqArgs == 0 || args > arity.numNormalArgs + arity.numSeqArgs) // this hacky case precludes confusion when flexary operators would disappear in the presentation } } object TextNotation { /** helpful when constructing notations programmatically */ - def fromMarkers(prec: Precedence, meta: Option[MPath], scope : NotationScope = NotationScope.default)(ms: Any*): TextNotation = { + def fromMarkers(prec: Precedence, meta: Option[MPath], block: Boolean = false, scope : NotationScope = NotationScope.default)(ms: Any*): TextNotation = { val markers : List[Marker] = ms.toList map { case i: Int => SimpArg(i) case m: Marker => m case s: String => Marker.parse(s) } - new TextNotation(Mixfix(markers), prec, meta, scope) + new TextNotation(Mixfix(markers), prec, meta, block, scope) } private def parseScope(n : scala.xml.Node) : NotationScope = { @@ -195,6 +191,11 @@ object TextNotation { case "" => None case s => Some(Path.parseM(s, nsMap)) } + val block = utils.xml.attr(n, "block") match { + case "true" => true + case "" | "false" => false + case s => throw ImplementationError("illegal boolean: " + s) + } val scope = n.child.find(_.label == "scope") match { case None => NotationScope.default case Some(s) => parseScope(s) @@ -213,7 +214,7 @@ object TextNotation { } } val fixity = FixityParser.parse(fixityString, arguments) - new TextNotation(fixity, precedence, meta, scope) + new TextNotation(fixity, precedence, meta, block, scope) case _ => throw ParseError("invalid notation:\n" + n) } @@ -232,6 +233,11 @@ object TextNotation { throw ParseError("theory expected") case _ => None } + val block = if (tokens.headOption contains "block") { + tokens = tokens.tail + true + } else + false val i = tokens.indexOf("prec") val prec = if (i != -1) { val rest = tokens.drop(i) @@ -252,7 +258,7 @@ object TextNotation { ("mixfix", tokens) } val fixity = FixityParser.parse(fixityString, arguments) - new TextNotation(fixity, prec, meta) + new TextNotation(fixity, prec, meta, block) } /** true if both notations expect the exact same markers */ diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/notations/NotationComponents.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/notations/NotationComponents.scala index ffde6bca69..cd69fb75bf 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/notations/NotationComponents.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/notations/NotationComponents.scala @@ -104,8 +104,11 @@ import CommonMarkerProperties._ /** bundles minor properties of markers to make the interfaces more robust */ case class CommonMarkerProperties(precedence: Option[Precedence], localNotations: Option[LocalNotationInfo]) { def *(remap: Int => Int) = copy(localNotations = localNotations.map(_ * remap)) - def asStringPrefix = localNotations.map(lni => "%L" + lni + "_").getOrElse("") - def addLocalNotationInfo(lni: LocalNotationInfo) = copy(localNotations = Some(lni)) + def asStringPrefix = { + var p = "" + localNotations.foreach(lni => p = "%L" + lni + "_") + p + } } object LocalNotationInfo { @@ -131,20 +134,23 @@ case class LocalNotationInfo(argument: Int, role: LocalNotationInfo.Role, replac def *(remap: Int => Int) = copy(argument = remap(argument)) } -sealed abstract class ArgumentMarker extends Marker with ArgumentComponent { +sealed abstract class ChildMarker extends Marker with ArityComponent { val number: Int - val properties: CommonMarkerProperties def precedence = properties.precedence +} +sealed abstract class ArgumentMarker extends ChildMarker with ArgumentComponent { /** a copy of this marker with the field properties.localNotations set */ - def addLocalNotationInfo(lni: LocalNotationInfo): ArgumentMarker = this match { - case am: SimpArg => am.copy(properties = properties.addLocalNotationInfo(lni)) - case am: SimpSeqArg => am.copy(properties = properties.addLocalNotationInfo(lni)) - case am: LabelArg => am.copy(properties = properties.addLocalNotationInfo(lni)) - case am: LabelSeqArg => am.copy(properties = properties.addLocalNotationInfo(lni)) - case am: ImplicitArg => am.copy(properties = properties.addLocalNotationInfo(lni)) + def changeProperty(change: CommonMarkerProperties => CommonMarkerProperties): ArgumentMarker = this match { + case am: SimpArg => am.copy(properties = change(properties)) + case am: SimpSeqArg => am.copy(properties = change(properties)) + case am: LabelArg => am.copy(properties = change(properties)) + case am: LabelSeqArg => am.copy(properties = change(properties)) + case am: ImplicitArg => am.copy(properties = change(properties)) } + /** a copy of this marker with the field properties.localNotations set */ + def addLocalNotationInfo(lni: LocalNotationInfo): ArgumentMarker = changeProperty(_.copy(localNotations = Some(lni))) } /** an argument @@ -171,7 +177,7 @@ sealed abstract class SeqArg extends ArgumentMarker { val sep: Delim def makeCorrespondingArg(n: Int, remap: Int => Int): Arg override def toString = properties.asStringPrefix + number.toString + sep + "…" - override def isSequence = true + override def isSequenceVia = Some(sep) } /** normal arguments */ @@ -202,14 +208,13 @@ case class SimpSeqArg(number: Int, sep: Delim, properties: CommonMarkerPropertie /** bundles flags for properties of [[LabelArg]] and [[LabelSeqArg]] * @param typed types are required * @param defined definitions are required - * @param dependent definitions are required */ -case class LabelInfo(typed: Boolean, defined: Boolean, dependent: Boolean) { - override def toString = (if (typed) "T" else "") + (if (defined) "D" else "") + (if (dependent) "d" else "") +case class LabelInfo(typed: Boolean, defined: Boolean) { + override def toString = (if (typed) "T" else "") + (if (defined) "D" else "") } object LabelInfo { - def none = LabelInfo(false,false,false) + def none = LabelInfo(false,false) } /** OML arguments, possibly with required type/definiens @@ -226,6 +231,7 @@ case class LabelArg(number : Int, info: LabelInfo, properties: CommonMarkerPrope case class LabelSeqArg(number: Int, sep: Delim, info: LabelInfo, properties: CommonMarkerProperties) extends SeqArg { override def toString = properties.asStringPrefix + "L" + number.toString + info.toString + sep + "…" def makeCorrespondingArg(n: Int, remap: Int => Int) = LabelArg(n, info, properties*remap) + override def isSequence = true } /** a variable binding @@ -235,16 +241,12 @@ case class LabelSeqArg(number: Int, sep: Delim, info: LabelInfo, properties: Com * @param sep if given, this is a variable sequence with this separator; * for typed variables with the same type, only the last one needs a type */ -case class Var(number: Int, typed: Boolean, sep: Option[Delim], properties: CommonMarkerProperties = noProps) extends Marker with VariableComponent { - def precedence = properties.precedence +case class Var(number: Int, typed: Boolean, sep: Option[Delim], properties: CommonMarkerProperties = noProps) extends ChildMarker with VariableComponent { override def toString = properties.asStringPrefix + "V" + number.toString + (if (typed) "T" else "") + (sep.map(_.toString + "…").getOrElse("")) - override def isSequence = sep.isDefined + override def isSequenceVia = sep def makeCorrespondingSingleVar(n: Int, remap: Int => Int) = Var(n, typed, None, properties*remap) def *(remap: Int => Int): Var = copy(number = remap(number), properties = properties * remap) -} - -case object AttributedObject extends Marker { - override def toString = "%a" + def info = LabelInfo(typed, false) } /** Verbalization markers occur in verbalization notations @@ -600,7 +602,8 @@ object PresentationMarker { */ sealed trait ArityComponent { val number: Int - def isSequence: Boolean = false + def isSequenceVia: Option[Delim] = None + def isSequence: Boolean = isSequenceVia.isDefined def precedence: Option[Precedence] //=None } sealed trait ArgumentComponent extends ArityComponent @@ -613,9 +616,10 @@ object Marker { charAt(s,i).map(p).getOrElse(false) } + import StringSplit.At2 def parse(s: String) : Marker = s match { - case s : String if s.startsWith("%L") => - // %L`i[d|c][!]_m resulting in LocalNotationInfo(i,[d|c],[!]) added to marker m, i.e., argument i determines notations to use for argument m + case At2("%L", _) => + // %L`i[d|c][!]_m resulting in LocalNotationInfo(i,[d|c],[!]) added to marker m, i.e., previous argument i determines notations to use for argument m var i = 2 while (charAtIs(s, i, _.isDigit)) {i+=1} val n = s.substring(2,i).toInt @@ -633,17 +637,18 @@ object Marker { if (! (charAt(s,i) contains '_')) throw ParseError(s"'_' expected at position $i: $s") val lni = LocalNotationInfo(n, role, replace) parse(s.substring(i+1)) match { - case am: ArgumentMarker => am.addLocalNotationInfo(lni) - case _ => throw ParseError("only argument markers can use local notations" + s) + case am: ArgumentMarker => + if (am.number < n) + throw ParseError(s"local notations for argument ${am.number} must be obtained from previous argument, not argument $n: $s") + am.addLocalNotationInfo(lni) + case _ => throw ParseError("only argument markers can use local notations: " + s) } - case s if s.startsWith("%i") => - val base = parse(s.substring(2)) - base match { + case At2("%i",rest) => + parse(rest) match { case d: Delim => InstanceName(d) case _ => throw ParseError("%i must be followed by plain delimiter: " + s) } case "%n" => SymbolName() - case "%a" => AttributedObject case s: String if s.startsWith("%D") || s.startsWith("%R") => // Ds ---> delimiter s (used to escape special delimiters) // Rs ---> same as Ds but associates to the right @@ -653,9 +658,9 @@ object Marker { val assocLeft = s(1) == 'D' Delim(s.substring(2), assocLeft) } - case s: String if s.startsWith("%I") => + case At2("%I", rest) => // In or Gn ---> implicit argument or implicit guard - val n = stringToInt(s.substring(2)).getOrElse(throw ParseError("not a valid marker " + s)) + val n = stringToInt(rest).getOrElse(throw ParseError("not a valid marker " + s)) ImplicitArg(n,noProps) case s: String if s.startsWith("V") && charAtIs(s, 1, _.isDigit) => //Vn ---> variable @@ -695,17 +700,14 @@ object Marker { d = d.substring(1) defined = true } + val li = LabelInfo(typed,defined) if (d.endsWith("…")) { - var dependent = false if (d.startsWith("d")) { - d = d.substring(1) - dependent = true + throw ParseError("'d' not supported anymore, use %B at the beginning of the marker instead") } val sep = d.dropRight(1) - val li = LabelInfo(typed,defined,dependent) LabelSeqArg(n,Delim(sep),li,noProps) } else { - val li = LabelInfo(typed,defined,false) LabelArg(n,li,noProps) } case s: String if s.endsWith("…") && charAtIs(s,0,_.isDigit) => diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/notations/NotationContainer.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/notations/NotationContainer.scala index 296876702f..ebad74cec8 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/notations/NotationContainer.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/notations/NotationContainer.scala @@ -176,6 +176,9 @@ class NotationContainer extends ComponentContainer { } object NotationContainer { + def empty(): NotationContainer = new NotationContainer + + @deprecated("use NotationContainer.empty()", "v20.0.0") def apply(): NotationContainer = new NotationContainer def apply(tnOpt: Option[TextNotation]): NotationContainer = { val nc = apply() diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/notations/NotationExtension.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/notations/NotationExtension.scala index ecc9364c97..65326a1985 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/notations/NotationExtension.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/notations/NotationExtension.scala @@ -180,7 +180,7 @@ object FixityParser { } } -case class PragmaticTerm(op: GlobalName, subs: Substitution, con: Context, args: List[Term], attribution: Boolean, notation: TextNotation, pos: List[Position]) { +case class PragmaticTerm(op: GlobalName, subs: Substitution, con: Context, args: List[Term], notation: TextNotation, pos: List[Position]) { require(1 + subs.length + con.length + args.length == pos.length, "Positions don't match number of arguments (op, subs, context and args)") def term = ComplexTerm(op, subs, con, args) } @@ -190,9 +190,10 @@ case class PragmaticTerm(op: GlobalName, subs: Substitution, con: Context, args: * It is returned by a FixityParser and used in a TextNotation */ abstract class NotationExtension extends Rule { + /** true if this can be used to destruct a term */ def isApplicable(t: Term): Boolean /** called to construct a term after a notation produced by this was used for parsing */ - def constructTerm(op: GlobalName, subs: Substitution, con: Context, args: List[Term], attrib: Boolean, not: TextNotation) + def constructTerm(op: GlobalName, subs: Substitution, con: Context, args: List[Term], not: TextNotation) (implicit unknown: () => Term): Term def constructTerm(fun: Term, args: List[Term]): Term /** called to deconstruct a term before presentation */ @@ -202,14 +203,14 @@ abstract class NotationExtension extends Rule { /** the standard mixfix notation for a list of [[Marker]]s */ object MixfixNotation extends NotationExtension { def isApplicable(t: Term) = true - def constructTerm(op: GlobalName, subs: Substitution, con: Context, args: List[Term], attrib: Boolean, not: TextNotation) + def constructTerm(op: GlobalName, subs: Substitution, con: Context, args: List[Term], not: TextNotation) (implicit unknown: () => Term) = ComplexTerm(op, subs, con, args) def constructTerm(fun: Term, args: List[Term]) = OMA(fun, args) def destructTerm(t: Term)(implicit getNotations: GlobalName => List[TextNotation]): Option[PragmaticTerm] = t match { case ComplexTerm(op, subs, con, args) => getNotations(op).foreach {not => - if (not.canHandle(subs.length, con.length, args.length, false)) { - return Some(PragmaticTerm(op, subs, con, args, false, not, Position.positions(t))) + if (not.canHandle(subs.length, con.length, args.length)) { + return Some(PragmaticTerm(op, subs, con, args, not, Position.positions(t))) } } return None @@ -240,7 +241,7 @@ class HOASApplySpine(app: GlobalName) { } /** tuple of HOAS symbol names */ -case class HOAS(apply: GlobalName, bind: GlobalName, typeAtt: GlobalName) { +case class HOAS(apply: GlobalName, bind: GlobalName) { val applySpine = new HOASApplySpine(apply) } @@ -256,27 +257,18 @@ case class HOAS(apply: GlobalName, bind: GlobalName, typeAtt: GlobalName) { class HOASNotation(val hoas: HOAS) extends NotationExtension { override def priority = 1 def isApplicable(t: Term) = t.head match { - case Some(h) => List(hoas.apply, hoas.typeAtt) contains h + case Some(h) => h == hoas.apply case None => false } - def constructTerm(op: GlobalName, subs: Substitution, con: Context, args: List[Term], attrib: Boolean, not: TextNotation) - (implicit unknown: () => Term) : Term = { - if (attrib) { - val ptp = if (subs.isEmpty && con.isEmpty && args.isEmpty) - OMS(op) - else - constructTerm(op, subs, con, args, false, not) - hoas.typeAtt(ptp) - } else { + def constructTerm(op: GlobalName, subs: Substitution, con: Context, args: List[Term], not: TextNotation)(implicit unknown: () => Term) : Term = { // for now: strict form treats substitution as extra arguments val subargs = subs.map(_.target) if (con.isEmpty) - hoas.apply(OMS(op) :: subargs ::: args) - else { - hoas.apply(OMS(op) :: subargs ::: List(hoas.bind(con, args))) - } - } + hoas.apply(OMS(op) :: subargs ::: args) + else { + hoas.apply(OMS(op) :: subargs ::: List(hoas.bind(con,args))) + } } def constructTerm(fun: Term, args: List[Term]) = hoas.apply(fun::args) @@ -284,9 +276,9 @@ class HOASNotation(val hoas: HOAS) extends NotationExtension { case hoas.applySpine(OMS(op), rest, appPos) => val notations = getNotations(op) MyList(notations) mapFind {not => - if (not.canHandle(0,0,rest.length, false)) { + if (not.canHandle(0,0,rest.length)) { // OMA(apply, op, args) <--> OMA(op, args) - val appTerm = PragmaticTerm(op, Substitution.empty, Context.empty, rest, false, not, appPos) + val appTerm = PragmaticTerm(op, Substitution.empty, Context.empty, rest, not, appPos) Some(appTerm) } else rest.reverse match { case OMBINDC(OMS(hoas.bind), con, args) :: _ => @@ -295,21 +287,14 @@ class HOASNotation(val hoas: HOAS) extends NotationExtension { val opSubsPos = (0 until 1+subs.length).toList.map(i => Position(1+i)) val conArgsPos = (0 until con.length+args.length).toList.map(i => Position(rest.length+1) / (i+1)) val bindPos = opSubsPos ::: conArgsPos - if (not.canHandle(subs.length, con.length, args.length, false)) { - val bindTerm = PragmaticTerm(op, subs, con, args, false, not, bindPos) + if (not.canHandle(subs.length, con.length, args.length)) { + val bindTerm = PragmaticTerm(op, subs, con, args, not, bindPos) Some(bindTerm) } else None case _ => None } } - case OMA(OMS(hoas.typeAtt), List(tp)) => tp match { - case OMS(op) => - Some(PragmaticTerm(op, Substitution(), Context(), Nil, true, null, List(Position(1)))) - case OMA(OMS(hoas.apply), OMS(op)::rest) => - Some(PragmaticTerm(op, Substitution(), Context(), rest, true, null, (0 until rest.length+1).toList.map(i => Position(1) / (i+1)))) - case _ => None - } case _ => None } } @@ -345,15 +330,7 @@ class NestedHOASNotation(obj: HOAS, meta: HOAS) extends NotationExtension { private def binding(con: Context, scope: Term)(implicit unknown: () => Term): Term = con.foldRight(scope) {case (next, sofar) => binding(next, sofar)} - def constructTerm(op: GlobalName, subs: Substitution, con: Context, args: List[Term], attrib: Boolean, not: TextNotation - )(implicit unknown: () => Term) : Term = { - if (attrib) { - val ptp = if (subs.isEmpty && con.isEmpty && args.isEmpty) - OMS(op) - else - constructTerm(op, subs, con, args, false, not) - meta.apply(OMS(obj.typeAtt), ptp) - } else { + def constructTerm(op: GlobalName, subs: Substitution, con: Context, args: List[Term], not: TextNotation)(implicit unknown: () => Term) : Term = { // 2 components are considered to be meta-arguments // - the subargs // - if there are no variables, the leading implicit args @@ -370,7 +347,6 @@ class NestedHOASNotation(obj: HOAS, meta: HOAS) extends NotationExtension { } else if (objArgs.length == 1) { application(opmeta, binding(con, objArgs.head)) } else throw InvalidNotation("") - } } def constructTerm(fun: Term, args: List[Term]) = meta.apply(fun::args) @@ -407,19 +383,18 @@ class NestedHOASNotation(obj: HOAS, meta: HOAS) extends NotationExtension { val numSubArgs = metaArgs.length - numLeadingImplArgs val subargs = metaArgs.take(numSubArgs) val args = metaArgs.drop(numSubArgs) ::: objArgs - if (not.canHandle(subargs.length,0,args.length, false)) { + if (not.canHandle(subargs.length,0,args.length)) { // List(), List(4), ..., List(4, ..., 4) - val tP = PragmaticTerm(op, subargs.map(Sub(OMV.anonymous, _)), Nil, args, false, not, opMetaPos ::: objArgPos) + val tP = PragmaticTerm(op, subargs.map(Sub(OMV.anonymous, _)), Nil, args, not, opMetaPos ::: objArgPos) Some(tP) } else if (objArgs.length == 1) { val (con, scope) = unbinding(objArgs.last) - if (not.canHandle(metaArgs.length, con.length, 1, false)) { + if (not.canHandle(metaArgs.length, con.length, 1)) { // List(), List(4,2), ..., List(4,2,...,4,2) val conPaths = (0 until con.length).toList.map(i => (0 until i).toList.flatMap(_ => List(4,2))) val conPos = conPaths.map(p => Position(5) / p / 4 / 1) val scopePos = Position(5) / conPaths.last / 4 / 2 - val tP = PragmaticTerm(op, metaArgs.map(Sub(OMV.anonymous, _)), con, List(scope), false, - not, opMetaPos ::: conPos ::: List(scopePos)) + val tP = PragmaticTerm(op, metaArgs.map(Sub(OMV.anonymous, _)), con, List(scope), not, opMetaPos ::: conPos ::: List(scopePos)) Some(tP) } else None diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Pragmatics.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Pragmatics.scala index 251e4843b1..5969da2640 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Pragmatics.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Pragmatics.scala @@ -53,9 +53,8 @@ class Pragmatics extends ChangeListener { else applicable.maxBy(_.priority) } - def makeStrict(level: Option[MPath], op: GlobalName, subs: Substitution, con: Context, args: List[Term], attrib: Boolean, not: TextNotation - )(implicit newUnkwown: () => Term) : Term = { - applicableByLevel(level).constructTerm(op, subs, con, args, attrib, not) + def makeStrict(level: Option[MPath], op: GlobalName, subs: Substitution, con: Context, args: List[Term], not: TextNotation)(implicit newUnkwown: () => Term) : Term = { + applicableByLevel(level).constructTerm(op, subs, con, args, not) } /** @@ -122,13 +121,6 @@ class Pragmatics extends ChangeListener { case Nil => OMA(fun, args) } } - - object StrictTyping { - def unapply(t: Term): Option[Term] = t match { - case OMA(OMS(s), List(tp)) if hoass.exists(_.typeAtt == s) => Some(tp) - case _ => None - } - } } /** apply/unapply for a pragmatic OMA under a given list of HOAS apply operators */ diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Precedence.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Precedence.scala index 2314f4218e..9c3bdef947 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Precedence.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/notations/Precedence.scala @@ -72,7 +72,7 @@ object InfInt { /** * binding precedences of operators are represented - * @param p the precedence, smaller precedence means weaker binding + * @param prec the precedence, smaller precedence means weaker binding * @param loseTie tie-breaking flag for comparisons */ sealed case class Precedence(prec : InfInt, loseTie : Boolean) extends scala.math.Ordered[Precedence] { diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/objects/AnonymousModules.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/objects/AnonymousModules.scala index 96360ef6a7..d1d5a3acb7 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/objects/AnonymousModules.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/objects/AnonymousModules.scala @@ -1,10 +1,13 @@ package info.kwarc.mmt.api.objects import info.kwarc.mmt.api._ +import info.kwarc.mmt.api.modules.{Module, Theory, View} import info.kwarc.mmt.api.notations._ -import info.kwarc.mmt.api.symbols.OMLReplacer +import info.kwarc.mmt.api.symbols.{OMLReplacer, TermContainer} import info.kwarc.mmt.api.utils._ +import scala.collection.mutable + /** auxiliary class for storing lists of declarations statefully without giving it a global name * * anonymous modules are object that can be converted into these helper classes using the objects [[AnonymousTheory]] and [[AnonymousMorphism]] @@ -18,7 +21,7 @@ trait AnonymousBody extends ElementContainer[OML] with DefaultLookup[OML] with S } /** a theory given by meta-theory and body */ -case class AnonymousTheory(val mt: Option[MPath], val decls: List[OML]) extends AnonymousBody { +case class AnonymousTheory(mt: Option[MPath], val decls: List[OML]) extends AnonymousBody { def rename(oldNew: (LocalName,Term)*) = { val sub: Substitution = oldNew.toList map {case (old,nw) => Sub(old, nw)} val trav = symbols.OMLReplacer(sub) @@ -105,15 +108,17 @@ object AnonymousMorphismCombinator { sealed abstract class DiagramElement extends ShortURIPrinter { def label: LocalName def toTerm: Term + def getBody: AnonymousBody } /** a node in an [[AnonymousDiagram]] * @param label the label of this node * @param theory the theory attached to this node (the same theory may be attached to multiple nodes) */ case class DiagramNode(label: LocalName, theory: AnonymousTheory) extends DiagramElement { - def toTerm = OML(label, Some(TheoryType(Nil)), Some(theory.toTerm)) - def toStr(implicit shortURIs: Boolean) = s"$label:THY=${theory.toStr}" - def canEqual(a: Any) = a.isInstanceOf[DiagramNode] + override def getBody: AnonymousBody = theory + override def toTerm: Term = OML(label, Some(TheoryType(Nil)), Some(theory.toTerm)) + override def toStr(implicit shortURIs: Boolean) = s"$label:THY=${theory.toStr}" + def canEqual(a: Any): Boolean = a.isInstanceOf[DiagramNode] override def equals(that: Any): Boolean = that match { case that: DiagramNode => (that.label == this.label) && (that.theory == this.theory) @@ -128,11 +133,12 @@ case class DiagramNode(label: LocalName, theory: AnonymousTheory) extends Diagra * @param morphism the morphism attached to this arrow (the same theory may be attached to multiple nodes) */ case class DiagramArrow(label: LocalName, from: LocalName, to: LocalName, morphism: AnonymousMorphism, isImplicit: Boolean) extends DiagramElement { - def toTerm = { + override def getBody: AnonymousBody = morphism + override def toTerm: Term = { val f = if (isImplicit) Some("implicit") else None OML(label, Some(MorphType(OML(from), OML(to))), Some(morphism.toTerm), None, f) } - def toStr(implicit shortURIs: Boolean) = { + override def toStr(implicit shortURIs: Boolean): String = { val a = if (isImplicit) "-i->" else "--->" s"$label:$from$a$to=${morphism.toStr}" } @@ -141,7 +147,11 @@ case class DiagramArrow(label: LocalName, from: LocalName, to: LocalName, morphi * @param nodes the nodes * @param arrows the arrows * @param distNode the label of a distinguished node to be used if this diagram is used like a theory - * invariant: codomain of distArrow is distNode + * + * Invariants: + * - invariant: codomain of distArrow is distNode + * - among all [[DiagramNode diagram nodes]] and [[DiagramArrow diagram arrows]] the labels are unique + * in other words: ''getElements.map(_.label)'' is a sequence without duplicates */ case class AnonymousDiagram(nodes: List[DiagramNode], arrows: List[DiagramArrow], distNode: Option[LocalName]) { def getNode(label: LocalName): Option[DiagramNode] = nodes.find(_.label == label) @@ -193,19 +203,168 @@ case class AnonymousDiagram(nodes: List[DiagramNode], arrows: List[DiagramArrow] } def getDistArrowWithNodes: Option[(DiagramNode,DiagramNode,DiagramArrow)] = getDistArrow flatMap {a => getArrowWithNodes(a.label)} - def getElements = nodes:::arrows + def getElements: List[DiagramElement] = nodes ::: arrows + + def getElement(label: LocalName): Option[DiagramElement] = getNode(label) match { + case Some(node) => + Some(node) + case None => getArrow(label) match { + case Some(arrow) => + Some(arrow) + case None => + None + } + } + /** replaces every label l with r(l) */ - def relabel(r: LocalName => LocalName) = { + def relabel(r: LocalName => LocalName) : AnonymousDiagram = { val nodesR = nodes.map(n => n.copy(label = r(n.label))) val arrowsR = arrows.map(a => a.copy(label = r(a.label), from = r(a.from), to = r(a.to))) AnonymousDiagram(nodesR, arrowsR, distNode map r) } - def union(that: AnonymousDiagram) = { - new AnonymousDiagram((this.nodes ::: that.nodes).groupBy(_.label).map(_._2.head).toList, (this.arrows ::: that.arrows).groupBy(_.label).map(_._2.head).toList, None) + + def toTerm: Term = AnonymousDiagramCombinator(nodes, arrows, distNode) + override def toString: String = nodes.mkString(", ") + "; " + arrows.mkString(", ") + "; " + distNode.toList.mkString("") + + /** + * Transform the anonymous diagram into a list of in-memory [[Module modules]]. + * + * Note that the modules do *not* get registered to the [[info.kwarc.mmt.api.frontend.Controller]] or any other + * entity. They are solely in-memory representations. + * + * @param labeller A function determining the fully qualified path to the generated modules given the "anonymous" names of the diagram elements in the anonymous diagram. + * @return List of in-memory modules, not yet registered anywhere. + */ + def toModules(labeller: LocalName => MPath): List[Module] = { + getElements.map { + case DiagramNode(name, anonymousTheory) => + val newName = labeller(name) + + Theory( + newName.doc, + newName.name, + anonymousTheory.mt, + df = TermContainer.asParsed(anonymousTheory.toTerm) + ) + + case DiagramArrow(name, from, to, anonMor, isImplicit) => + val newName = labeller(name) + + View( + newName.doc, + newName.name, + OMMOD(labeller(from)), + OMMOD(labeller(to)), + TermContainer.asParsed(anonMor.toTerm), + isImplicit + ) + } + } + + /** + * Compute the union with another diagram. + * + * @return A name clash occurs if there is an element in ''otherDiagram'' with a label l such that in the current diagram there is a non-equal element with the same label. + * If such a name clash occurs, [[None]] is returned. + * Otherwise, the union is returned. + * + */ + def ++(otherDiagram: AnonymousDiagram): Option[AnonymousDiagram] = { + val otherElements: Map[LocalName, DiagramElement] = + otherDiagram.getElements.groupBy(_.label).mapValues(_.head) + + for ((otherName, otherElement) <- otherElements) { + getElement(otherName) match { + case Some(ourElement) if ourElement != otherElement => + return None // name clash + + case _ => // continue + } + } + + Some(AnonymousDiagram( + // compute the unions of nodes and arrows, respectively + (nodes ::: otherDiagram.nodes).groupBy(_.label).map(_._2.head).toList, + (arrows ::: otherDiagram.arrows).groupBy(_.label).map(_._2.head).toList, + None + )) + } + + /** + * Get all transitive-reflexive diagram element dependencies for `element` categorizied into + * [[DiagramNode]] dependencies and [[DiagramArrow]] dependencies. + */ + private def getDependenciesFor(element: DiagramElement): (Set[DiagramNode], Set[DiagramArrow]) = { + val deps = BreadthFirstSearch.collectBounded( + all = getElements.toSet, + initial = Seq(element), + explorer = (elem: DiagramElement, _: Set[DiagramElement], remainingElems: Set[DiagramElement]) => { + // If (T in deps) is included in (S in remainingElements), get T into inclusionDeps + // TODO: Dependency tracking for anonymous modules does not account for defined views currently! + val inclusionDeps = remainingElems.filter(_.getBody.getDeclarations.exists { + case IncludeOML(OML(label, _, _, _, _), _) if label == elem.label => true + case _ => false + }) + + // If (T in deps) occurs as domain or codomain of an arrow in remainingElements, get T into arrowDeps + val arrowDeps = remainingElems.filter { + case DiagramArrow(_, from, to, _, _) if from == elem.label || to == elem.label => true + case _ => false + } + + inclusionDeps ++ arrowDeps + } + ) + + (deps.collect { case x: DiagramNode => x }, deps.collect { case x: DiagramArrow => x }) + } + + /** + * Compute the dependencies-aware difference ''this\labels''. + */ + def --(labels: Seq[LocalName]): AnonymousDiagram = { + val (nodeDepsLists, arrowDepsLists) = labels.flatMap(getElement).map(getDependenciesFor).unzip + + // flatten the sets to one set, respectively + val nodeDeps = nodeDepsLists.reduceLeftOption((x, y) => x ++ y).getOrElse(Set()) + val arrowDeps = arrowDepsLists.reduceLeftOption((x, y) => x ++ y).getOrElse(Set()) + + AnonymousDiagram( + nodes = nodes.toSet.diff(nodeDeps).toList, + arrows = arrows.toSet.diff(arrowDeps).toList, + distNode = distNode.filterNot(distNodeLabel => { + nodeDeps.exists(_.label == distNodeLabel) + }) + ) } - def toTerm = AnonymousDiagramCombinator(nodes, arrows, distNode) - override def toString = nodes.mkString(", ") + "; " + arrows.mkString(", ") + "; " + distNode.toList.mkString("") + def -(otherDiagram: AnonymousDiagram): AnonymousDiagram = { + val actualLabelsToRemove = otherDiagram.getElements.mapOrSkip(otherElement => + getElement(otherElement.label) match { + case Some(ourElement) if ourElement == otherElement => + otherElement.label + + case _ => throw SkipThis + } + ) + + this -- actualLabelsToRemove + } + + def -(label: LocalName): AnonymousDiagram = { + this -- List(label) + } +} + +object AnonymousDiagram { + def union(diagrams: AnonymousDiagram*): Option[AnonymousDiagram] = { + val firstDiag :: remainingDiags = diagrams.toList + + remainingDiags.foldLeft[Option[AnonymousDiagram]](Some(firstDiag))((optionDiag, diag) => optionDiag match { + case None => None + case Some(diagPrime) => diagPrime ++ diag + }) + } } /** bridges between [[AnonymousDiagram]] and [[Term]] */ @@ -237,7 +396,7 @@ object AnonymousDiagramCombinator { } case _ => return None } - val ad = new AnonymousDiagram(nodes.reverse, arrows.reverse, dN) + val ad = AnonymousDiagram(nodes.reverse, arrows.reverse, dN) Some(ad) case _ => None } @@ -297,6 +456,18 @@ object DerivedOMLFeature { } } +/** + * An inclusion of a [[Theory]] or [[AnonymousTheory]] as a [[OML]] to be used as a declaration in an [[AnonymousBody]]. + * + * @example + * ''' + * includeOml match { + * case IncludeOML(OMPMOD(theory, args), df) => /* inclusion of a (possibly parametric) theory */ + * case IncludeOML(anonTheoryName, df) => /* inclusion of an anonymous theory in the same diagram */ + * } + * ''' + * + */ object IncludeOML extends DerivedOMLFeature(IncludeVarDecl.feature) with DerivedOMLFeature.Unnamed object StructureOML extends DerivedOMLFeature("structure") with DerivedOMLFeature.Named diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/objects/ModuleOperators.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/objects/ModuleOperators.scala index 86b67ff8de..62e2810bd2 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/objects/ModuleOperators.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/objects/ModuleOperators.scala @@ -395,8 +395,11 @@ object ModExp extends uom.TheoryScala { val anonymoustheory = _path ? "anonymoustheory" val anonymousmorphism = _path ? "anonymousmorphism" val anonymousdiagram = _path ? "anonymousdiagram" + val theorytype = _path ? "theory" val morphtype = _path ? "morphism" + val diagramtype = _path ? "diagram" + val identity = _path ? "identity" val composition = _path ? "composition" val structuralinclude = _path ? "structuralinclude" @@ -499,6 +502,10 @@ object TheoryType { } } +object DiagramType { + def apply(): Term = OMS(ModExp.diagramtype) +} + /** An OMM represents the application of a morphism to a term */ object OMM { val path = ModExp.morphismapplication diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/objects/Obj.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/objects/Obj.scala index dfe6cb9866..216de7d302 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/objects/Obj.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/objects/Obj.scala @@ -89,6 +89,14 @@ abstract class Obj extends Content with ontology.BaseType with ShortURIPrinter w def copyFrom(o: Obj) { metadata = o.metadata } + /** applies copyFrom and returns this + * + * @return this object but with the metadata from o + */ + def from(o: Obj): this.type = { + copyFrom(o) + this + } } trait ThisTypeTrait @@ -110,14 +118,6 @@ sealed abstract class Term extends Obj with ThisTypeTrait { case OMS(p) => p.module / p.name case _ => mmt.mmtbase ? Obj.toPathEncoding(this) } - /** applies copyFrom and returns this - * - * @return this object but with the metadata from o - */ - def from(o: Term): this.type = { - copyFrom(o) - this - } } /** @@ -291,7 +291,7 @@ object OMATTRMany { } } -/** The joint methods of OMLIT and UnknownOMLIT */ +/** The joint methods of [[OMLIT]] and [[UnknownOMLIT]] */ sealed trait OMLITTrait extends Term { def synType: Term /** canonical string representation of this literal */ @@ -396,7 +396,7 @@ case class OML(name: LocalName, tp: Option[Term], df: Option[Term], nt: Option[T /** * Get a [[VarDecl]] representation of this OML, e.g. for insertion into a [[Context]]. */ - def vd = VarDecl(name, featureOpt, tp, df, nt) + def vd = VarDecl(name, featureOpt, tp, df, nt).from(this) private[objects] def freeVars_ = vd.freeVars def head = None def subobjects = subobjectsNoContext(vd.tp.toList ::: vd.df.toList) diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/ontology/RelationalExtractor.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/ontology/RelationalExtractor.scala index 841ac090f4..555aea01dc 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/ontology/RelationalExtractor.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/ontology/RelationalExtractor.scala @@ -90,7 +90,9 @@ object MMTExtractor extends RelationalExtractor { case f => TheoryExp.simplify(s.from).toMPath } if (s.isInclude) { - f(Includes(t.path, from)) + if (!s.isGenerated) + // we do not export transitive includes + f(Includes(t.path, from)) } else { f(dec) f(HasDomain(s.path, from)) diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/ontology/Search.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/ontology/Search.scala index e8d023acd9..8ab9821117 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/ontology/Search.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/ontology/Search.scala @@ -35,7 +35,7 @@ case class TermPattern(qvars: Context, query: Term) object TermPattern { val qvarBinder = utils.mmt.mmtcd ? "qvar" val qvarMarkers = List(Delim("$"), Var(1, false, Some(Delim(","))), Delim(":"), SimpArg(2)) - val qvarNot = new TextNotation(Mixfix(qvarMarkers), Precedence.infinite, None) + val qvarNot = new TextNotation(Mixfix(qvarMarkers), Precedence.infinite, None, false) val qvarRule = ParsingRule(qvarBinder, Nil, qvarNot) class RemoveUnknowns(unk: Context) extends StatelessTraverser { def traverse(t: Term)(implicit con : Context, init : State) = t match { diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/ontology/TheoryGraph.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/ontology/TheoryGraph.scala index dcae7ff345..1393fb1af4 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/ontology/TheoryGraph.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/ontology/TheoryGraph.scala @@ -222,11 +222,12 @@ class TheoryGraphFragment(theories: Iterable[Path], views: Iterable[Path], tg: T addNodeIfNeeded(to) addEdge(Some(view), from, to, "view", false) case _ => - throw GeneralError("domain/codomain of view not known: " + view) + throw GeneralError("domain/codomain of view not loaded (did you load the relational data?): " + view) } } // all the links from/out of the minimal theories that aren't part of the minimal views + // TODO make more preactical choices for enriching the graph theories.foreach {from => tg.edgesFrom(from) foreach {case (to, etos) => val externalNode = addNodeIfNeeded(to) // true if the partner node is from a different document diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/parser/ActiveNotation.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/parser/ActiveNotation.scala index 9563cfa067..a75e8991d3 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/parser/ActiveNotation.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/parser/ActiveNotation.scala @@ -87,51 +87,34 @@ class ActiveNotation(scanner: Scanner, val rules: List[ParsingRule], val backtra val prepicked = delim.text.substring(token.word.length).toList.map(c => Delim(c.toString)) left = prepicked ::: left } - /** pick all available Token's as SimpArg(n) or LabelArg(n, typed, defined) */ - private def PickAll(n: Int, isOML:Option[LabelInfo]) : FoundArg = isOML match { - case None => FoundSimpArg(scanner.pick(numCurrentTokens), n) - case Some(io) => FoundOML(scanner.pick(numCurrentTokens),n,io) + /** pick all available Token's as a FoundSimpArg */ + private def PickAll(m: ChildMarker) = { + val ts = scanner.pick(numCurrentTokens) + FoundSimp(ts, m) } - /** pick exactly ns.length available Tokens as ns.map(Arg(_)) or ns.map(LabelArg(_,...)) */ - private def PickSingles(ns: List[(Int,Option[LabelInfo])]) = { - val fs = ns reverseMap {case (n,isOML) => - isOML match { - case None => FoundSimpArg(scanner.pick(1), n) - case Some(io) => FoundOML(scanner.pick(1), n, io) - } + /** pick exactly ms.length available Tokens, each as as FoundSimp */ + private def PickSingles(ms: List[ChildMarker]) { + val fs = ms reverseMap {m => + FoundSimp(scanner.pick(1), m) } found = fs ::: found } - - private def PickAllSeq(n: Int, infoOpt:Option[LabelInfo]) = { - val ts = scanner.pick(numCurrentTokens) - val a = infoOpt match { - case None => FoundSimpArg(ts, n) - case Some(info) => FoundOML(ts, n, info) - } - found.headOption match { - case Some(FoundSimpSeqArg(m, args)) if m == n && infoOpt.isEmpty => - found = FoundSimpSeqArg(n, args ::: List(a.asInstanceOf[FoundSimpArg])) :: found.tail - case Some(FoundSeqOML(m, args, info)) if m == n && infoOpt.isDefined => - found = FoundSeqOML(n, args ::: List(a.asInstanceOf[FoundOML]), info) :: found.tail - case _ => - val f = infoOpt match { - case None => FoundSimpSeqArg(n, List(a.asInstanceOf[FoundSimpArg])) - case Some(info) => FoundSeqOML(n, List(a.asInstanceOf[FoundOML]), info) - } - found ::= f + /** like pickAll, but appends to a previously started sequence or starts a new sequence */ + private def PickAllSeq(m: ChildMarker) { + val f = PickAll(m) + (found.headOption, m) match { + case (Some(FoundSeq(s, args)), m) if s.number == m.number => + found = FoundSeq(s, args ::: List(f)) :: found.tail + case (_, m) => + found ::= FoundSeq(m, List(f)) } } - - private def SeqDone(n: Int, isOML:Option[LabelInfo]) { + /** inserts an empty sequence if a sequence is ended before any elements were found */ + private def SeqDone(m: ChildMarker) { found.headOption match { - case Some(FoundSimpSeqArg(m, _)) if m == n => - case Some(FoundSeqOML(m,_,_)) if m == n => + case Some(FoundSeq(s, _)) if s.number == m.number => case _ => - val f = isOML match { - case None => FoundSimpSeqArg(n, Nil) - case Some(info) => FoundSeqOML(n,Nil,info) - } + val f = FoundSeq(m, Nil) found ::= f } } @@ -154,20 +137,18 @@ class ActiveNotation(scanner: Scanner, val rules: List[ParsingRule], val backtra Applicable } - private def inSeqArg(n: Int) = found match { - case FoundSimpSeqArg(m, _) :: _ if m == n => true - case FoundSeqOML(m,_,_) :: _ if m == n => true + private def inSeq(m: ChildMarker) = found match { + case (fs: FoundSeq) :: _ if fs.number == m.number => true case _ => false } - /** splits a List[Marker] into - * a List[Int] (possibly Nil) that corresponds to a List[Arg] - * and the remaining List[Marker] + /** splits a List[Marker] into a list of a simple (plain, label, or variable) markers and the remaining markers */ - private def splitOffArgs(ms: List[Marker], ns: List[(Int,Option[LabelInfo])] = Nil) : (List[(Int,Option[LabelInfo])], List[Marker]) = ms match { - case (la:LabelArg) :: rest => splitOffArgs(rest, (la.number,Some(la.info)) :: ns) - case SimpArg(n, _) :: rest => splitOffArgs(rest, (n,None) :: ns) - case Delim("%w") :: rest => splitOffArgs(rest, ns) + private def splitOffSimples(ms: List[Marker], ns: List[ChildMarker] = Nil) : (List[ChildMarker], List[Marker]) = ms match { + case (la:LabelArg) :: rest => splitOffSimples(rest, la :: ns) + case (vm @ Var(n, typed, None, _)) :: rest => splitOffSimples(rest, vm :: ns) + case (sa:SimpArg) :: rest => splitOffSimples(rest, sa :: ns) + case Delim("%w") :: rest => splitOffSimples(rest, ns) case rest => (ns.reverse, rest) } @@ -181,6 +162,7 @@ class ActiveNotation(scanner: Scanner, val rules: List[ParsingRule], val backtra def applicable(currentToken: Token, futureTokens: String): Applicability = { //shortcut: true if delimiter s matches at the currentIndex def matches(s: String) = ActiveNotation.matches(s, currentToken.word, futureTokens) + /* DELETE after testing 2020-07-07 // now the actual applicability check in 2 cases // first: if we are in a bound variable, step through the corresponding state machine val result: Applicability = found match { @@ -237,90 +219,70 @@ class ActiveNotation(scanner: Scanner, val rules: List[ParsingRule], val backtra } if (result != null) return result // second: otherwise, try to match the current Token against an upcoming delimiter - splitOffArgs(left) match { - // the notation expects some arguments followed by the current token: all previously shifted tokens become arguments - case (ns, Delimiter(s) :: _) if matches(s) => - ns match { - // no arguments expected: just consume the delimiter - case Nil => - onApplyI {currentIndex => - deleteDelim(currentIndex) - } - // create a single arg with all available tokens - case List((n,isOML)) if numCurrentTokens > 0 => - onApplyI {currentIndex => - found ::= PickAll(n,isOML) - delete(1) - deleteDelim(currentIndex) - } - case _ => - if (ns.length == numCurrentTokens) { - // create a single arg for each available token - onApplyI {currentIndex => - PickSingles(ns) - delete(numCurrentTokens) - deleteDelim(currentIndex) - } - } else if (ns.length > numCurrentTokens) { - // if more tokens available than needed, merge all remaining tokens into the last argument - //TODO use matched tokens as delimiters, merge in between them - onApplyI {currentIndex => - PickSingles(ns.init) - PickAll(ns.length,ns.last._2) - delete(numCurrentTokens) - deleteDelim(currentIndex) - } - } else { - NotApplicable - } - } - // the notation expects a sequence whose separator is the current token: all previously shifted tokens become the first element, and we start the sequence - case (Nil, SimpSeqArg(n, Delimiter(s),_) :: _) if matches(s) => - onApply { - PickAllSeq(n,None) - addPrepickedDelims(Delim(s), currentToken) - } - case (Nil, (lsa @ LabelSeqArg(n,Delimiter(s),_,_)) :: _) if matches(s) => - onApply { - PickAllSeq(n,Some(lsa.info)) - addPrepickedDelims(Delim(s),currentToken) - } - // the notation expects a sequence followed by the current token: end the sequence - case (Nil, SimpSeqArg(n, Delimiter(t),_) :: Delimiter(s) :: _) if ! matches(t) && matches(s) => - if (numCurrentTokens > 0) { - //picks the last element of the sequence (possibly the only one) + */ + splitOffSimples(left) match { + // the notation expects some arguments followed by the current token: all previously shifted tokens become arguments + case (ns,Delimiter(s) :: _) if matches(s) => + ns match { + // no arguments expected: just consume the delimiter + case Nil => onApplyI {currentIndex => - PickAllSeq(n,None) - delete(1) deleteDelim(currentIndex) } - } else if (numCurrentTokens == 0) { - //picks nothing and finds an empty sequence + // create a single arg with all available tokens + case List(n) if numCurrentTokens > 0 => onApplyI {currentIndex => - SeqDone(n,None) + found ::= PickAll(n) delete(1) deleteDelim(currentIndex) } - } else - NotApplicable //abort? - case (Nil, (lsa @ LabelSeqArg(n,Delimiter(t),_,_)) :: Delimiter(s) :: _) if ! matches(t) && matches(s) => - if (numCurrentTokens > 0) { - //picks the last element of the sequence (possibly the only one) - onApplyI {currentIndex => - PickAllSeq(n,Some(lsa.info)) - delete(1) - deleteDelim(currentIndex) - } - } else if (numCurrentTokens == 0) { - //picks nothing and finds an empty sequence - onApplyI {currentIndex => - SeqDone(n,Some(lsa.info)) - delete(1) - deleteDelim(currentIndex) - } - } else - NotApplicable //abort? - // the notation expects a variable sequence followed by the current token: find the empty sequence + case _ => + if (ns.length == numCurrentTokens) { + // create a single arg for each available token + onApplyI {currentIndex => + PickSingles(ns) + delete(numCurrentTokens) + deleteDelim(currentIndex) + } + } else if (ns.length > numCurrentTokens) { + // if more tokens available than needed, merge all remaining tokens into the last argument + //TODO use matched tokens as delimiters, merge in between them + onApplyI {currentIndex => + PickSingles(ns.init) + PickAll(ns.last) + delete(numCurrentTokens) + deleteDelim(currentIndex) + } + } else { + NotApplicable + } + } + // the notation expects a sequence whose separator is the current token: all previously shifted tokens become the first element, and we start the sequence + case (Nil, (cm: ChildMarker) :: _) if cm.isSequenceVia.exists(s => matches(s.text)) => + onApply { + PickAllSeq(cm) + addPrepickedDelims(cm.isSequenceVia.get,currentToken) + } + // the notation expects a sequence followed by the current token: end the (possibly empty) sequence + case (Nil,(cm:ChildMarker) :: Delimiter(s) :: _) if cm.isSequenceVia.exists(t => !matches(t.text)) && matches(s) => + if (numCurrentTokens > 0) { + //picks the last element of the sequence (possibly the only one) + onApplyI {currentIndex => + PickAllSeq(cm) + delete(1) + deleteDelim(currentIndex) + } + } else if (numCurrentTokens == 0) { + //picks nothing and finds an empty sequence + onApplyI {currentIndex => + SeqDone(cm) + delete(1) + deleteDelim(currentIndex) + } + } else + NotApplicable //abort? +/* DELETE after testing 2020-07-07 + // the notation expects a variable sequence followed by the current token: find the empty sequence case (Nil, (vm @ Var(n, _, Some(Delimiter(t)), _)) :: Delimiter(s) :: _) if ! matches(t) && matches(s) => onApplyI {currentIndex => val fv = new FoundVar(vm) @@ -337,7 +299,9 @@ class ActiveNotation(scanner: Scanner, val rules: List[ParsingRule], val backtra fv.newVar(currentIndex, currentToken) fv.state = FoundVar.AfterName found ::= fv - } /* + } +*/ + /* // removed because // - there is no way to make sure the shifted Token is a variable name // - it's confusing that it does not also check for the type key or the separator @@ -361,19 +325,19 @@ class ActiveNotation(scanner: Scanner, val rules: List[ParsingRule], val backtra deleteDelim(currentIndex) } case _ => Abort - }*/ + } case _ => Abort // should be impossible - } + }*/ case _ => NotApplicable } } /** - * precondition: this.applicable(scanner.currentToken) - * terminate the current argument(s) and match the current token to the next expected delimiter + * precondition: this.applicable(scanner.currentToken) + * terminate the current argument(s) and match the current token to the next expected delimiter * * @param currentIndex the index of currentToken - * @return true iff the notation is fully applied, i.e., no further arguments or delimiters can be matched - */ + * @return true iff the notation is fully applied, i.e., no further arguments or delimiters can be matched + */ //currentIndex must be passed here because it is not known yet when applicable is called (because other notations may be closed in between) def apply(currentIndex: Int): Boolean = { remember(currentIndex) @@ -388,20 +352,24 @@ class ActiveNotation(scanner: Scanner, val rules: List[ParsingRule], val backtra * i.e., the current arguments can be the last arguments of the notation with no further delimiter expected */ def closable : Applicability = { - splitOffArgs(left) match { - case (Nil, SimpSeqArg(n, Delimiter(s), _) :: Nil) => - if (inSeqArg(n) && numCurrentTokens > 0) { + splitOffSimples(left) match { + // the notation expects a sequence + case (Nil, (m: ChildMarker) :: Nil) if m.isSequence => + if (numCurrentTokens > 0) { + // the available tokens are the last (possibly only) element onApply { - PickAllSeq(n,None) + PickAllSeq(m) delete(1) } - } else if (! inSeqArg(n) && numCurrentTokens == 0) { - onApply { - SeqDone(n,None) + } else if (!inSeq(m)) { + // no tokens available and the sequence has not been started yet: empty sequence + onApply { + SeqDone(m) delete(1) - } + } } else NotApplicable + /* case (Nil, (vm: Var) :: Nil) => found match { case (fv: FoundVar) :: _ => //the last thing found was a variable, probably fv.marker == vm @@ -429,19 +397,11 @@ class ActiveNotation(scanner: Scanner, val rules: List[ParsingRule], val backtra case _ => // can't close because we've never started vm, still need to parse vm NotApplicable - } - case (List((n,Some(info))), Nil) if numCurrentTokens > 0 => - // one argument taking all available Token's - onApply { - found ::= PickAll(n,Some(info)) - delete(1) - } - // as many arguments as there are Token's - // should we abort immediately if the numbers do not match up? - case (List((n,None)), Nil) if numCurrentTokens > 0 => + }*/ + case (List(m), Nil) if numCurrentTokens > 0 => // one argument taking all available Token's onApply { - found ::= PickAll(n,None) + found ::= PickAll(m) delete(1) } // as many arguments as there are Token's diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/parser/LexerExtension.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/parser/LexerExtension.scala index a90afc6864..2cf16a1fa2 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/parser/LexerExtension.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/parser/LexerExtension.scala @@ -412,11 +412,8 @@ case class StringInterpolationToken(text: String, firstPosition: SourcePosition, s.ref = eti.outer.source.copy(region = s.reg) case m: MMTPart => val e = m.unparsed - val boundVars = BoundName.getVars(eti.boundNames) - val cont = eti.outer.context ++ Context(boundVars.map(VarDecl(_,None,None,None,None)) :_*) - val ref = eti.outer.source.copy(region = m.reg) - val pu = ParsingUnit(ref, cont, e, eti.outer.iiContext) - m.term = eti.parser(pu)(eti.errorCont).toTerm + val tm = eti.callbackParse(m.reg, e) + m.term = tm } lexer.makeTerm(this, eti) } @@ -540,7 +537,8 @@ class QuotationLexer(quoteType: GlobalName, quoteTerm: GlobalName) extends Strin val fullcontext = eti.outer.context ++ context //TODO not obvious which context should be used for the quoted term val srcref = eti.outer.source.copy(region = token.region) val pu = ParsingUnit(srcref, fullcontext, str, eti.outer.iiContext) - val t = eti.parser(pu)(eti.errorCont).toTerm + val parser = eti.parser + val t = parser(pu)(eti.errorCont).toTerm val freeVars = t.freeVars diff names if (freeVars.nonEmpty) { val e = SourceError("quotation lexer", srcref, "free variables in quoted term: " + names.mkString(", ")) diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/parser/NotationBasedParser.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/parser/NotationBasedParser.scala index 7fe601598a..209b7e57e3 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/parser/NotationBasedParser.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/parser/NotationBasedParser.scala @@ -55,14 +55,12 @@ case class ParsingRuleTable(groups: List[ParsingRuleGroup]) { /** * names are locally meaningful: variables bound in the contexts (to be parsed as OMV), fields of dependent OML sequences (to be parsed as OML) */ -case class BoundName(name: LocalName, isOML: Boolean) - +case class BoundName(name: LocalName, isOML: Boolean) { + def toTerm: Term = if (isOML) OML(name) else OMV(name) +} object BoundName { - /** the names of bound variables */ - def getVars(names: List[BoundName]): List[LocalName] = { - names collect { - case BoundName(n,false) => n - } + def getVarNames(bns: List[BoundName]) = bns collect { + case BoundName(n,false) => n } } @@ -77,20 +75,6 @@ class NotationBasedParser extends ObjectParser { private lazy val prag = controller.pragmatic private lazy val lup = controller.globalLookup - /** - * builds parser's notation context based on content in the controller - * and the scope (base path) of the parsing unit - * returned list is sorted (in increasing order) by priority - */ - protected def tableNotations(nots: List[ParsingRule]): ParsingRuleTable = { - val qnotations = nots.groupBy(x => x.notation.precedence).toList.map { - case (p, ns) => ParsingRuleGroup(p, ns) - } - // log("notations in scope: " + qnotations) - val qnotationsOrdered = qnotations.sortWith((p1, p2) => p1.precedence < p2.precedence) - ParsingRuleTable(qnotationsOrdered) - } - /** constructs a SourceError, all errors go through this method */ private def makeError(msg: String, reg: SourceRegion)(implicit pu: ParsingUnit, errorCont: ErrorHandler) { val ref = SourceRef(pu.source.container, reg) @@ -98,90 +82,9 @@ class NotationBasedParser extends ObjectParser { errorCont(err) } - /* - * declarations of the unknown variables (implicit arguments, missing types, etc. - * - * the variable names are irrelevant as long as they are unique within each call to the parser - * Moreover, we make sure the names are chosen the same way every time to support change management. - */ - private object Variables { - // due to the recursive processing, variables in children are always found first - // so adding to the front yields the right order - /** unknowns */ - private var unknowns: List[VarDecl] = Nil - private var counter = 0 - - /** free variables (whose types may depend on the unknowns) */ - private var freevars: List[LocalName] = Nil - /** @return the free variables detected so far */ - def getFreeVars = freevars - - /** @return returns the unknown and free variables at the end, free vars have unknown types */ - def getVariables(implicit pu: ParsingUnit): (Context,Context) = { - val fvDecls = freevars map {n => - val tp = newUnknown(newType(n), Nil) // types of free variables must be closed; alternative: allow any other free variable - VarDecl(n,tp) - } - (unknowns, fvDecls) - } - - private def next = { - val s = counter.toString - counter += 1 - s - } - - def reset() { - unknowns = Nil - counter = 0 - freevars = Nil - } - - /** name of an omitted implicit argument */ - def newArgument = ParseResult.VariablePrefixes.implicitArg / next - /** name of the omitted type of a variable */ - def newType(name: LocalName) = LocalName("") / name / next - /** name of an explicitly omitted argument */ - def newExplicitUnknown = ParseResult.VariablePrefixes.explicitUnknown / next - - /** generates a new unknown variable, constructed by applying a fresh name to all bound variables */ - def newUnknown(name: LocalName, boundNames: List[BoundName])(implicit pu: ParsingUnit) = { - unknowns ::= VarDecl(name) - val bvars = BoundName.getVars(boundNames) - // handling of shadowing: we assume that an unknown cannot depend on a shadowed variable - // so we remove shadowed (i.e., earlier) occurrences of bound variables - // There are reasonable cases, where the unknown does depend on a shadowed variable, e.g., in [x: type, c: x, x: type] c = c. - // The behavior in still unspecified in these cases. - val bvarsD = bvars.reverse.distinct.reverse - //apply meta-variable to all bound variables in whose scope it occurs - checking.Solver.makeUnknown(name, bvarsD) - } - - /** generates a new unknown variable for the index that chooses from a list of options in an ambiguity - * always an integer, thus independent of bound variables - */ - def newAmbiguity = { - val name = LocalName("") / "A" / next - unknowns ::= VarDecl(name) - OMV(name) - } - - /** generates a new free variable that is meant to be bound on the outside */ - def newFreeVariable(n: String) = { - val name = LocalName(n) - freevars ::= name - OMV(name) - } - - /** removes a free variable if it is later found out that it can be interpreted otherwise, used for OMLs */ - def removeFreeVariable(n: LocalName) { - freevars = freevars diff List(n) - } - } - import Variables._ - /** + * top level parsing function: initializes input state and implicit arguments, calls main parsing method, and builds ParseResult * @param pu the parsing unit */ def apply(pu: ParsingUnit)(implicit errorCont: ErrorHandler): ParseResult = { @@ -189,10 +92,17 @@ class NotationBasedParser extends ObjectParser { return DefaultObjectParser(pu) } implicit val puI = pu + val variables = new Variables + val tm = parse(pu, variables, errorCont) + val (unk,free) = variables.getVariables + ParseResult(unk,free,tm) + } + + /** main parsing method */ + private def parse(implicit pu: ParsingUnit, variables: Variables, errorCont: ErrorHandler): Term = { //gathering notations and lexer extensions in scope - val (parsing, lexing, _) = getRules(pu.context , Some(pu.iiContext)) + val (parsing,lexing,_) = getRules(pu.context,Some(pu.iiContext)) val notations = tableNotations(parsing) - Variables.reset() log("parsing: " + pu.term + " in context " + pu.context) log("rules:") logGroup { @@ -203,33 +113,47 @@ class NotationBasedParser extends ObjectParser { } val escMan = new EscapeManager(lexing) val tl = TokenList(pu.term, escMan, pu.source.region.start) - val result = if (tl.getTokens.isEmpty) { + if (tl.getTokens.isEmpty) { makeError("no tokens found: " + pu.term, pu.source.region) - DefaultObjectParser(pu) + DefaultObjectParser(pu).toTerm } else { - //scanning - val ul = new UnmatchedList(tl) + /* scanning: the scanner initially scans with top rule, then with all notations in increasing order of precedence + but we will actually call ul.scan only in makeTerm so that we can add local notations first + */ // TODO does it make sense to sort by meta-theory-level first, then by precedence? - // the scanner initially scans with top rule, then with all notations in increasing order of precedence - // but we will actually call ul.scanner.scan only in makeTerm so that we can add local notations first - ul.scanner = new Scanner(tl, Some(pu), notations, controller.report) - // turn the syntax tree into a term - val tm = logGroup { - makeTerm(ul, Nil) + val ul = new UnmatchedList(tl, Some(pu), notations, controller.report) + /* turn the syntax tree into a term + to the outside, this term is its own block, so bound names introduced by the term are dropped + to the inside, we are not in a block yet + */ + val (_,tm) = logGroup { + makeTerm(ul, Nil, false) } - SourceRef.update(tm,pu.source) // weirdly necessary,apparently + SourceRef.update(tm,pu.source) // weirdly necessary, apparently log("parse result: " + tm.toString) - val (unk, free) = getVariables - ParseResult(unk, free, tm) + tm } - result + } + + /** + * builds parser's notation context based on content in the controller + * and the scope (base path) of the parsing unit + * returned list is sorted (in increasing order) by priority + */ + protected def tableNotations(nots: List[ParsingRule]): ParsingRuleTable = { + val qnotations = nots.groupBy(x => x.notation.precedence).toList.map { + case (p, ns) => ParsingRuleGroup(p, ns) + } + // log("notations in scope: " + qnotations) + val qnotationsOrdered = qnotations.sortWith((p1, p2) => p1.precedence < p2.precedence) + ParsingRuleTable(qnotationsOrdered) } private type RuleLists = (List[ParsingRule], List[LexerExtension], List[NotationExtension]) private def unappNotation(e: ContentElement, altNames: List[LocalName], args: Int) = { val ms = Mixfix(Delim(e.name.toString) :: Range(0, args).toList.map(SimpArg(_))) - val n = new TextNotation(ms, Precedence.infinite, None) + val n = new TextNotation(ms, Precedence.infinite, None, false) ParsingRule(e.path, altNames, n) } @@ -258,7 +182,7 @@ class NotationBasedParser extends ObjectParser { var names = (c.name :: c.alternativeNames).map(_.toString) //the names that can refer to this declaration if (c.name.last == SimpleStep("_")) names ::= c.name.init.toString //the unapplied notations consisting just of the name - val unapp = names map (n => new TextNotation(Mixfix(List(Delim(n))), Precedence.infinite, None)) + val unapp = names map (n => new TextNotation(Mixfix(List(Delim(n))), Precedence.infinite, None, false)) val app = c.not.toList (unapp:::app).foreach {n => nots ::= ParsingRule(c.path, c.alternativeNames, n) @@ -299,77 +223,184 @@ class NotationBasedParser extends ObjectParser { } } - /** true if n may be the name of a free variable, see [[ParseResult]] + + /* + * declarations of the unknown variables (implicit arguments, missing types, etc. * - * making this always true hides source errors - * making this never true requires awkwardly binding all variables + * the variable names are irrelevant as long as they are unique within each call to the parser + * Moreover, we make sure the names are chosen the same way every time to support change management. */ - private def mayBeFree(n: String) = { - n != "" && n(0).isLetter && - n.forall(c => c.isUpper || c.isDigit) + private class Variables { + // due to the recursive processing, variables in children are always found first + // so adding to the front yields the right order + /** unknowns */ + private var unknowns: List[VarDecl] = Nil + private var counter = 0 + + /** free variables (whose types may depend on the unknowns) */ + private var freevars: List[LocalName] = Nil + /** @return the free variables detected so far */ + def getFreeVars = freevars + + /** @return returns the unknown and free variables at the end, free vars have unknown types */ + def getVariables(implicit pu: ParsingUnit): (Context,Context) = { + val fvDecls = freevars map {n => + val tp = newUnknown(newType(n), Nil) // types of free variables must be closed; alternative: allow any other free variable + VarDecl(n,tp) + } + (unknowns, fvDecls) + } + private def next = { + val s = counter.toString + counter += 1 + s + } + /** name of an omitted implicit argument */ + def newArgument = ParseResult.VariablePrefixes.implicitArg / next + /** name of the omitted type of a variable */ + def newType(name: LocalName) = LocalName("") / name / next + /** name of an explicitly omitted argument */ + def newExplicitUnknown = ParseResult.VariablePrefixes.explicitUnknown / next + + /** generates a new unknown variable, constructed by applying a fresh name to all bound variables */ + def newUnknown(name: LocalName, boundNames: List[BoundName])(implicit pu: ParsingUnit) = { + unknowns ::= VarDecl(name) + // handling of shadowing: we assume that an unknown cannot depend on a shadowed variable + // so we remove shadowed (i.e., earlier) occurrences of bound variables + // There are reasonable cases, where the unknown does depend on a shadowed variable, e.g., in [x: type, c: x, x: type] c = c. + // The behavior in still unspecified in these cases. + val boundNamesD = boundNames.reverse.distinct.reverse + //apply meta-variable to all bound variables in whose scope it occurs + // this line determines whether implicit arguments may depend on OML names + val mayUse = boundNames.map(_.toTerm) + checking.Solver.makeUnknown(name, mayUse) + } + + /** generates a new unknown variable for the index that chooses from a list of options in an ambiguity + * always an integer, thus independent of bound variables + */ + def newAmbiguity = { + val name = LocalName("") / "A" / next + unknowns ::= VarDecl(name) + OMV(name) + } + + /** generates a new free variable that is meant to be bound on the outside */ + def newFreeVariable(n: String) = { + val name = LocalName(n) + freevars ::= name + OMV(name) + } } - /** + /** true if n may be a name introduced inside a term */ + private def mayBeName(n: String) = { + n != "" && TokenList.isLetter(n(0)) && n.forall(c => TokenList.isLetterOrNumber(c) || TokenList.isConnector(c)) + } + + /** true if left-over token n may be interpreted as a free variable, see [[ParseResult]] + * making this always true hides source errors in case of typos + * making this never true precludes using free variables + */ + private def mayBeFree(n: String) = { + mayBeName(n) && n(0).isUpper + } + private type MadeTerm = (List[BoundName],Term) + private type MadeDecl = (List[BoundName],OML) + /** * recursively transforms a TokenListElem (usually obtained from a [[Scanner]]) to an MMT Term * @param te the element to transform * @param boundNames the names bound in this term (excluding the variables of the context of the parsing unit) * @param pu the original ParsingUnit (constant during recursion) - * @param attrib the resulting term should be a variable attribution + * @param attrib the resulting term should be an attributed declaration of a fresh name + * @return the term and the names it binds that can be used in future terms */ - private def makeTerm(te: TokenListElem, boundNames: List[BoundName], attrib: Boolean = false)(implicit pu: ParsingUnit, errorCont: ErrorHandler): Term = { + private def makeTerm(te: TokenListElem, boundNames: List[BoundName], inBlock: Boolean, attrib: Boolean = false) + (implicit pu: ParsingUnit, variables: Variables, errorCont: ErrorHandler): MadeTerm = { // cases may return multiple options in case of ambiguity - val term = te match { + val mt: MadeTerm = te match { case te @ Token(word, _, _, _) => lazy val unparsed = OMSemiFormal(objects.Text("unknown", word)) // fallback option val name = LocalName.parse(word) - val isBound = boundNames.find(_.name == name) - val term = if (isBound.isDefined) { - //single Tokens may be bound names ... + val isBound = boundNames.reverse.find(_.name == name) + val term = if (attrib) { + // we need the name in a declaration + if (isBound.isDefined) { + // TODO can a variable shadow another bound name? for now: generate error but parse it as usual + makeError(s"variable $word shadows a bound name", te.region) + } + if (mayBeName(word)) { + OMV(word) // unbound variable, to be turned into VarDecl by caller + } else { + makeError("fresh variable name expected; found: " + word,te.region) + unparsed + } + } else if (isBound.isDefined) { + // single Tokens may be bound names ... if (isBound.get.isOML) OML(name) else OMV(name) - } else if (getFreeVars.contains(name) || pu.context.exists(_.name == name)) { + } else if (variables.getFreeVars.contains(name) || pu.context.exists(_.name == name)) { // ... or free variables ... OMV(name) } else if (word == "_") { // unbound _ is a fresh unknown variable - newUnknown(newExplicitUnknown, boundNames) + variables.newUnknown(variables.newExplicitUnknown,boundNames) } else if (word.count(c => c == '?' || c == '/') > 0) { // ... or qualified identifiers makeIdentifier(te).map(OMID).getOrElse(unparsed) } else if (mayBeFree(word)) { - newFreeVariable(word) + variables.newFreeVariable(word) } else { //in all other cases, we don't know - makeError("unbound token: " + word, te.region) + makeError("unbound token: " + word,te.region) unparsed } - term + (Nil,term) case e: ExternalToken => - val eti = new ExternalTokenParsingInput(pu, boundNames, this, errorCont) - e.parse(eti) + val eti = new ExternalTokenParsingInput(pu, this, errorCont) { + def callbackParse(reg: SourceRegion, s: String) = { + val boundVars = BoundName.getVarNames(boundNames) + val cont = pu.context ++ Context(boundVars.map(VarDecl(_,None,None,None,None)): _*) + val ref = pu.source.copy(region = reg) + val innerpu = ParsingUnit(ref,cont,s,pu.iiContext) + parse(innerpu, variables, errorCont) + } + } + (Nil,e.parse(eti)) case ml: MatchedList => - makeTermFromMatchedList(ml, boundNames, attrib) + makeTermFromMatchedList(ml, boundNames, inBlock, attrib) case ul: UnmatchedList => - // scanning is delayed until here in order to allow for collecting local notations first - ul.scanner.scan() - val term = if (ul.tl.length == 1) { - // process the single TokenListElement - makeTerm(ul.tl(0), boundNames) + /* scanning is delayed until here in order to allow for modifying the scanning based on what has been parsed already + - makeTermFromMatchedList below may have previously added notations to ul + - we will now check for parsing names + */ + if (attrib && ul.tl.isSingleWord.isDefined) { + /* if we know we need a name, by-pass the usual methods and return it + this slightly hacky case allows handling fresh names or names that cannot be statically resolved + */ + val t = ul.tl.isSingleWord.get + (Nil, OMV(LocalName.parse(t.word))) } else { - /* This case arises if - - the Term is ill-formed - - the matching TextNotation is missing - - a subterm has no delimiters (e.g., as in LF applications) - - a semi-formal subterm consists of multiple text Tokens - Consequently, it is not obvious how to proceed. - By using defaultApplication, the behavior is somewhat configurable. - */ - val terms = ul.tl.getTokens.map(makeTerm(_, boundNames)) - prag.defaultApplication(Some(pu.getLevel), terms.head, terms.tail) + ul.scan() + if (ul.tl.length == 1) { + // process the single TokenListElement + makeTerm(ul.tl(0), boundNames, inBlock, attrib) + } else { + /* This case arises if + - the Term is ill-formed + - the matching TextNotation is missing + - a subterm has no delimiters (e.g., as in LF applications) + - a semi-formal subterm consists of multiple text Tokens + Consequently, it is not obvious how to proceed. + By using defaultApplication, the behavior is somewhat configurable. + */ + val (bns,terms) = ul.tl.getTokens.map(makeTerm(_,boundNames,inBlock)).unzip + val t = prag.defaultApplication(Some(pu.getLevel),terms.head,terms.tail) + (Nil,t) + } } - term } - SourceRef.update(term, pu.source.copy(region = te.region)) - term + SourceRef.update(mt._2, pu.source.copy(region = te.region)) + mt } /** auxiliary method of makeTerm @@ -423,98 +454,186 @@ class NotationBasedParser extends ObjectParser { } - /** auxiliary method of makeTerm */ - private def makeTermFromMatchedList(ml: MatchedList, boundNames: List[BoundName], attrib: Boolean)(implicit pu: ParsingUnit, errorCont: ErrorHandler): Term = { - val notation = ml.an.rules.head.notation // all notations must agree - val arity = notation.arity - val firstVar = arity.firstVarNumberIfAny - //log("constructing term for notation: " + ml.an) - val found = ml.an.getFound - // compute the names of all bound variables, in abstract syntax order - val newBVars: List[(Var, LocalName)] = found.flatMap { - case fv: FoundVar => fv.getVars map { - case SingleFoundVar(_, name, _) => - val ln = LocalName.parse(name.word) - (fv.marker, ln) - } - case _ => Nil - }.sortBy(_._1.number) - val newBVarNames = newBVars.map(x => BoundName(x._2, false)) - /** the bound variables occurring in a variable: the prefix of newBVarNames just before a certain variable identified by its Var marker and - within that FoundVar's sequence of variables by its name */ - def boundNamesInVar(vm: Var, name: LocalName): List[BoundName] = { - val pos = newBVars.indexWhere(_ ==(vm, name)) - boundNames ::: newBVarNames.take(pos) - } - /** the bound variable in argument, distinguishing arguments before and after the variables */ - def boundNamesInArg(n: Int): List[BoundName] = if (n < firstVar) boundNames else boundNames ::: newBVarNames - // 3 lists for the children, in concrete syntax order, together with their position in the abstract syntax - // order does not matter except that sequence elements (which have the same position) must occur in sequence order - // the arguments before the variables - var subs: List[(Int, Term)] = Nil - // the variable declaration list (name + type) - var vars: List[(Var, SourceRegion, LocalName, Option[Term])] = Nil - // the arguments behind the variables - var args: List[(Int, Term)] = Nil - // We walk through found and fill subs, vars, and args by - // recursively processing the respective TokenListElem in ml.tokens - /** adds terms to either subs or args, depending on n, and increments i for each term */ - def addTerms(n: Int, terms: List[Term]) { - val nterms = terms.map((n,_)) - if (n < firstVar) - subs = subs ::: nterms - else - args = args ::: nterms - } - def doFoundContent(fc: FoundContent, toks: List[UnmatchedList]) {fc match { - // argument before the variables - case FoundSimpArg(_, n) => - val r = makeTerm(toks.head, boundNamesInArg(n)) - addTerms(n, List(r)) - // label arguments: as the cases above but with makeOML instead of makeTerm - case FoundOML(_,n,info) => - val r = makeOML(toks.head, boundNamesInArg(n),info) - addTerms(n, List(r)) - // sequence arguments: as the case above, but as many TokenListElement as the sequence has elements - case FoundSimpSeqArg(n, fas) => - val r = toks.map(t => makeTerm(t, boundNamesInArg(n))) - addTerms(n, r) - // label sequence arguments: as above - case FoundSeqOML(n,fas,info) => - // the names encountered in the sequence so far - // if the sequence is dependent, these names are bound in the remainder - var omlNames: List[BoundName] = Nil - val r = toks.map {tok => - val usableOMLNames = if (info.dependent) omlNames else Nil - val term = makeOML(tok,boundNamesInArg(n):::usableOMLNames,info) - if (info.dependent) { - term match { - case o: OML => omlNames ::= BoundName(o.name, true) - case _ => - } - } - term - } - addTerms(n, r) - // variables - case fv: FoundVar => - var toksLeft = toks - fv.getVars foreach {case SingleFoundVar(pos, nameToken, tpOpt) => - val name = LocalName(nameToken.word) - // a variable declaration, so take one TokenListElement for the type - val tp = tpOpt map {_ => - val stp = makeTerm(toksLeft.head, boundNamesInVar(fv.marker, name), attrib = true) - toksLeft = toksLeft.tail - // remove toplevel operator, e.g., : - stp match { - case prag.StrictTyping(stpt) => stpt - case _ => stp // applies to unregistered type attributions - } - } - vars = vars ::: List((fv.marker, nameToken.region, name, tp)) - } - // now toksLeft.empty - }} + /** auxiliary method of makeTerm + * @param boundNames visible variables and OMLs that were declared in the surrounding term + * @param outerBlock if true, the surrounding term is a block, i.e., new OML declarations must be tracked and returned + * @param attrib if true, this is to be become an attributed fresh name + */ + private def makeTermFromMatchedList(ml: MatchedList, boundNames: List[BoundName], outerBlock: Boolean, attrib: Boolean) + (implicit pu: ParsingUnit, variables: Variables, errorCont: ErrorHandler): (List[BoundName],Term) = { + val notation = ml.an.rules.head.notation // all notations must agree + val arity = notation.arity + val innerBlock = notation.block + val isBlock = outerBlock || innerBlock + val firstVar = arity.firstVarNumberIfAny + /* DELETE after testing 2020-07-07 + //log("constructing term for notation: " + ml.an) + val found = ml.an.getFound + // compute the names of all bound variables, in abstract syntax order + val newBVars: List[(Var, LocalName)] = found.flatMap { + case fv: FoundVar => fv.getVars map { + case SingleFoundVar(_, name, _) => + val ln = LocalName.parse(name.word) + (fv.marker, ln) + } + case _ => Nil + }.sortBy(_._1.number) + val newBVarNames = newBVars.map(x => BoundName(x._2, false)) + + /** the bound variables occurring in a variable: the prefix of newBVarNames just before a certain variable identified by its Var marker and + within that FoundVar's sequence of variables by its name */ + def boundNamesInVar(vm: Var, name: LocalName): List[BoundName] = { + val pos = newBVars.indexWhere(_ ==(vm, name)) + boundNames ::: newBVarNames.take(pos) + } + /** the bound variables in argument, distinguishing arguments before and after the variables */ + def boundNamesInArg(n: Int): List[BoundName] = if (n < firstVar) boundNames else boundNames ::: newBVarNames + */ + + // 3 lists for the children, in concrete syntax order, together with their position in the abstract syntax + // in abstract syntax order + // the arguments before the variables + var subs: List[(Int, Term)] = Nil + // the variable declaration list + var vars: List[(Var, OML)] = Nil + // the arguments behind the variables + var args: List[(Int, Term)] = Nil + // the bound names that the arguments processed so far have introduced, to be used in later (in abstract syntax order) arguments + // contains all bound variables, and if isBlock also all OMLs + var newBoundNamesSoFar: List[BoundName] = Nil + def boundNamesSoFar = boundNames ::: newBoundNamesSoFar + + // We walk through found and fill subs, vars, and args by + // recursively processing the respective TokenListElem in ml.tokens + /** adds terms to either subs or args, depending on n */ + def addTerm(n: Int, mt:MadeTerm) { + val (bns,tm) = mt + val ntm = (n,tm) + if (n < firstVar) + subs = subs ::: List(ntm) + else + args = args ::: List(ntm) + if (isBlock) + newBoundNamesSoFar = newBoundNamesSoFar ::: bns + } + def addVar(v: Var, md: MadeDecl) { + val oml = md._2 + val name = oml.name + val newBound = List(BoundName(name, false)) + vars = vars ::: List((v,oml)) + // bound variables are always available in later arguments; the declaration is its own block, so any names introduced in it are dropped + newBoundNamesSoFar = newBoundNamesSoFar ::: newBound + } + // will be called below (in abstract syntax order) on every FoundContent + def doFoundContent(fc: FoundContent, toks: List[UnmatchedList]) {fc match { + // argument before the variables + case FoundSimp(_, m: SimpArg) => + if (attrib) + true + val attribArg = attrib && m.number == 1 // for nesting attributions: the first argument is to be attributed + val r = makeTerm(toks.head,boundNamesSoFar,isBlock,attribArg) + addTerm(m.number,r) + // label arguments: as the cases above but with makeOML instead of makeTerm + case FoundSimp(_, m: LabelArg) => + val r = makeOML(toks.head,boundNamesSoFar,m.info) + addTerm(m.number, r) + // variable binding: similar to OML + case FoundSimp(_, m: Var) => + val r = makeOML(toks.head,boundNamesSoFar,m.info) + addVar(m,r) + // sequence arguments: as the cases above, but as many TokenListElement as the sequence has elements + case FoundSeq(m: SimpSeqArg, _) => + toks.foreach {t => + val r = makeTerm(t,boundNamesSoFar,isBlock) + addTerm(m.number,r) + } + // label sequence arguments: as above + case FoundSeq(m: LabelSeqArg, _) => + // names encountered in the sequence are available for later arguments + toks.foreach {tok => + val r = makeOML(tok,boundNamesSoFar,m.info) + addTerm(m.number, r) + } + // sequence variables: as above + case FoundSeq(m: Var, _) => + toks.foreach {tok => + val r = makeOML(tok,boundNamesSoFar,m.info) + addVar(m,r) + } + case _ => throw ImplementationError("unexpected found object") + /* DELETE after testing 2020-07-07 + case fv: FoundVar => + var toksLeft = toks + fv.getVars foreach {case SingleFoundVar(pos, nameToken, tpOpt) => + val name = LocalName(nameToken.word) + // a variable declaration, so take one TokenListElement for the type + val tp = tpOpt map {_ => + val (_,t) = makeTerm(toksLeft.head, boundNamesSoFar, isBlock) + toksLeft = toksLeft.tail + t + } + val newBound = List(BoundName(name, false)) + vars = vars ::: List((fv.marker, nameToken.region, name, tp)) + // bound variables are always available in later arguments; the declaration is its own block, so any names + introduced in it are dropped + newBoundNamesSoFar = newBoundNamesSoFar ::: newBound + } + // now toksLeft.empty + */ + }} + // we process all found content in abstract syntax order, inserting implicit arguments where needed + var processed: List[FoundContent] = Nil + arity.components.foreach { + case ia: ImplicitArg => + val t = variables.newUnknown(variables.newArgument, boundNamesSoFar) + addTerm(ia.number, (Nil,t)) + case arg => + val tokens = ml.tokens.filter(_._1.number == arg.number) + // TODO optional tokens could be handled here + tokens.foreach {case (fc,uls) => + // this is essentially just a call to doFoundContent + // but first we handle local notations + arg match { + case a: ArgumentMarker => + // local notations + a.properties.localNotations.foreach {case lni => + // find the previous argument that lni references, obtain additional notations from it, add them to uls + (subs ::: args).find(_._1 == lni.argument) foreach {case (_,e) => + val tO = lni.role match { + case LocalNotationInfo.Theory => + Some(e) + case LocalNotationInfo.Domain => + Morph.domain(e)(lup) + case LocalNotationInfo.Codomain => + Morph.codomain(e)(lup) + } + //calling this on a non-type-checked t may or may not find all relevant notations + val localNotations = tO match { + case Some(t) => + tableNotations(getRules(t)._1) + case None => + makeError("cannot determine theory for local notations",ml.region) + ParsingRuleTable(Nil) + } + uls.foreach {ul => + ul.addRules(localNotations,lni.replace) + } + } + } + case _ => + } + // now the main call + processed ::= fc + doFoundContent(fc,uls) + } + } + // sanity check: have we processed every token + if (processed.length != ml.tokens.length) { + throw ImplementationError("unprocessed tokens") + } + + /* DELETE after testing 2020-07-07 + // all tokens of ml enriched with the respective local notation info val tokensWithLocalNotationInfo: List[(FoundContent, Option[LocalNotationInfo], List[UnmatchedList])] = ml.tokens map {case (fc, uls) => val arg = arity.components.find(_.number == fc.number) @@ -561,6 +680,8 @@ class NotationBasedParser extends ObjectParser { doFoundContent(fc, uls) } } + */ + // the list of constants of the used notation // basically, cons = mlCons, but we drop every constant that is defined to be equal to one we already have // such cases can happen with structures, where the generated constants are essentially aliases that do not require ambiguity resolution @@ -580,15 +701,20 @@ class NotationBasedParser extends ObjectParser { if (cons == List(utils.mmt.brackets) || cons == List(utils.mmt.andrewsDot) || cons == List(utils.mmt.andrewsDotRight)) { //TODO add metadata for keeping track of brackets // source ref of the returned term will be overridden with the source ref of the bracketed term - return args.head._2 + return (boundNamesSoFar, args.head._2) } - // process subs, vars, and args, which are needed to build the term + val finalSub = Substitution(subs.map(a => Sub(OMV.anonymous, a._2)): _*) + val finalVars = vars.map {case (vm, oml) => oml.vd} + val finalArgs = args.map(_._2) + + /* delete after testing 2020-09-01 + // process subs, vars, and args, which are needed to build the term // this includes sorting args and vars according to the abstract syntax // add implicit arguments before the variables val finalSubs: List[Term] = arity.subargs.flatMap { case ImplicitArg(_, _) => - List(newUnknown(newArgument, boundNames)) + List(variables.newUnknown(variables.newArgument, boundNamesSoFar)) case LabelArg(n,_,_) => val a = subs.find(_._1 == n).get List(a._2) @@ -604,10 +730,15 @@ class NotationBasedParser extends ObjectParser { } val finalSub = Substitution(finalSubs.map(a => Sub(OMV.anonymous, a)): _*) + // compute the variables + val finalVars = vars.map { + case (vm, oml) => oml.vd + } + // add implicit arguments behind the variables (same as above except for using newBVarNames) val finalArgs: List[Term] = arity.arguments.flatMap { case ImplicitArg(_, _) => - List(newUnknown(newArgument, boundNames ::: newBVarNames)) + List(variables.newUnknown(variables.newArgument, boundNamesSoFar)) case LabelArg(n, _,_) => val a = args.find(_._1 == n).get // must exist if notation matched List(a._2) @@ -621,30 +752,9 @@ class NotationBasedParser extends ObjectParser { val as = args.filter(_._1 == n) as.map(_._2) } + */ - // compute the variables - val finalVars = vars.sortBy(_._1.number).map { - case (vm, reg, vname, tp) => - val (finalTp, unknown) = if (!vm.typed) - (None, false) - else tp match { - case Some(_) => (tp, false) - case None => - //new unknown for the type - //under a binder, apply the meta-variable to all governing bound variables - //these are the boundVars and all preceding vars of the current binder - val governingBVars = boundNamesInVar(vm, vname) - val t = newUnknown(newType(vname), governingBVars) - (Some(t), true) - } - val vd = VarDecl(vname).copy(tp = finalTp) - if (unknown) - metadata.TagInferredType.set(vd) - SourceRef.update(vd, pu.source.copy(region = reg)) - vd - } - - /* construct each possible alternative terms + /* construct each possible alternative term all alternatives must use the same notation; therefore, they will ask for the same unknown variables we cache those in UnknownCacher to make sure we only generate one set of unknown variables */ @@ -669,7 +779,7 @@ class NotationBasedParser extends ObjectParser { /** gets the next unknown variable */ def getNext : Term = { if (firstRun) { - val u = newUnknown(newArgument, boundNames) + val u = variables.newUnknown(variables.newArgument, boundNames) // these unknown occur at the beginning of the current term, so boundNames instead of boundNamesSoFar cachedUnknowns :::= List(u) u } else { @@ -679,9 +789,9 @@ class NotationBasedParser extends ObjectParser { } } } - /** constructs one alternative terms */ + /** constructs one alternative term */ def makeAlternative(con: ContentPath, adaptedFinalArgs: List[Term]): Term = { - if (arity.isConstant && subs.isEmpty && args.isEmpty && vars.isEmpty && !attrib) { + if (arity.isConstant && subs.isEmpty && args.isEmpty && vars.isEmpty) { //no args, vars, scopes --> OMID return OMID(con) } @@ -694,27 +804,25 @@ class NotationBasedParser extends ObjectParser { } con match { case con: MPath => - if (finalSubs.nonEmpty) + if (finalSub.nonEmpty) makeError("no substitution allowed in module application", ml.region) if (finalVars.isEmpty) OMPMOD(con, adaptedFinalArgs) else OMBINDC(OMMOD(con), finalVars, adaptedFinalArgs) case con: GlobalName => - prag.makeStrict(level, con, finalSub, finalVars, adaptedFinalArgs, attrib, notation)( - () => UnknownCacher.getNext - ) + prag.makeStrict(level, con, finalSub, finalVars, adaptedFinalArgs, notation)(() => UnknownCacher.getNext) } } // construct the alternative terms - if (cons.length > 1 && finalSub.isEmpty && finalVars.isEmpty && finalArgs.nonEmpty) { + val altTms = if (cons.length > 1 && finalSub.isEmpty && finalVars.isEmpty && finalArgs.nonEmpty) { // to avoid duplicating the arguments in each alternative (which would in particular cause them to be checked multiple times), // we bind argument a_i as variable /AP/i=a_i outside of the ambiguous term val (argCont,argNames) = finalArgs.zipWithIndex.map {case (a,i) => val n = LocalName("") / "AP" / i.toString (VarDecl(n, df = a), OMV(n)) }.unzip - val av = newAmbiguity + val av = variables.newAmbiguity val alternatives = cons map {con => val a = makeAlternative(con, argNames) UnknownCacher.prepareNextRun @@ -731,36 +839,54 @@ class NotationBasedParser extends ObjectParser { case hd :: Nil => hd case l => - val av = newAmbiguity + val av = variables.newAmbiguity ObjectParser.oneOf(av::l) } } + // OMLs are exported to the surrounding block (if any) unless this terms introduces its own block; variables are always invisible from the outside + val newNamesForSurroundingBlock = if (outerBlock && !innerBlock) newBoundNamesSoFar.filter(_.isOML) else Nil + (newNamesForSurroundingBlock, altTms) } /** like makeTerm but interprets OMA(:,OMA(=,v)) as an OML */ - private def makeOML(te: TokenListElem, boundNames: List[BoundName], info: LabelInfo, attrib: Boolean = false) - (implicit pu: ParsingUnit, errorCont: ErrorHandler): Term = { - // TODO evil hack to allow OML names with slashes, must be removed at next opportunity - val filter : Error => Boolean = { - case SourceError(_,_,msg,_,_) if (msg startsWith "unbound token:") || (msg startsWith "ill-formed constant reference") => false - case _ => true - } - val t = makeTerm(te,boundNames)(pu,new FilteringErrorHandler(errorCont,filter)) - t match { - case OMLTypeDefNot(name, tpOpt, dfOpt, ntOpt) /* if !boundVars.contains(name) && getFreeVars.contains(name) */ => - removeFreeVariable(name) - val tp = tpOpt orElse {if (info.typed) Some(newUnknown(newExplicitUnknown, boundNames)) else None} - val df = dfOpt orElse {if (info.defined) Some(newUnknown(newExplicitUnknown, boundNames)) else None} - val l = OML(name,tp,df,ntOpt) - l + private def makeOML(te: TokenListElem, boundNames: List[BoundName], info: LabelInfo) + (implicit pu: ParsingUnit, variables: Variables, errorCont: ErrorHandler): MadeDecl = { + val mt = makeTerm(te,boundNames, false, attrib = true) // OML is its own block, cannot export names other than itself + mt match { + case (_, OMLTypeDefNot(name, tpOpt, dfOpt, ntOpt) ) => + var unknown = false // true if no unknown generated + val tp = tpOpt orElse { + if (info.typed) { + unknown = true + Some(variables.newUnknown(variables.newType(name), boundNames)) + } else + None + } + val df = dfOpt orElse { + if (info.defined) { + unknown = true + Some(variables.newUnknown(variables.newExplicitUnknown, boundNames)) + } else + None + } // ever being able to infer a definiens is unlikely + val l = OML(name,tp,df,ntOpt).from(mt._2) + if (unknown) { + metadata.TagInferredType.set(l) + } + /* an OML is its own block - type and definiens do not export names into the environment + so any names introduced inside are dropped, and the single name of the OML is exported + */ + val newBound = List(BoundName(name, true)) + (newBound, l) case _ => - makeError("expected label, found other term: " + t, te.region) - t + makeError("expected declaration, found other term: " + mt._2, te.region) + (mt._1, OML(OMV.anonymous, None, Some(mt._2))) } } /** matches v[:T][=D][#N] */ private object OMLTypeDefNot { + /* DELETE after testing 2020-07-07 private object Name { def unapply(t : Term) : Option[LocalName] = t match { case OMV(n) => Some(n) @@ -777,7 +903,7 @@ class NotationBasedParser extends ObjectParser { case _ => None } - } + }*/ def unapply(t : Term) : Option[(LocalName,Option[Term],Option[Term],Option[TextNotation])] = { var tp: Option[Term] = None var df: Option[Term] = None @@ -805,7 +931,7 @@ class NotationBasedParser extends ObjectParser { nt = Some(not) matchTDN(k) } - case Name(n) => Some(n) + case OMV(n) => Some(n) case _ => None } matchTDN(t).map {n => (n, tp, df, nt)} @@ -822,7 +948,7 @@ class NotationBasedParser extends ObjectParser { case _ => None } } - private object OMLtype extends OMLAnnotation("OMLType") - private object OMLdef extends OMLAnnotation("OMLDef") - private object OMLnotation extends OMLAnnotation("OMLNotation") + private object OMLtype extends OMLAnnotation("Type") + private object OMLdef extends OMLAnnotation("Def") + private object OMLnotation extends OMLAnnotation("Notation") } \ No newline at end of file diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/parser/Parser.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/parser/Parser.scala index a7d27221ac..1d75a73117 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/parser/Parser.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/parser/Parser.scala @@ -30,8 +30,8 @@ case class ParsingUnit(source: SourceRef, context: Context, term: String, iiCont /** encapsulates the output of an [[ObjectParser]] * @param unknown the unknown variables that must be solved - * @param the free variables that must be bound at the outside (may use unknowns) - * @param the parsed term (may use unknowns and free variables) + * @param free the free variables that must be bound at the outside (may use unknowns) + * @param term the parsed term (may use unknowns and free variables) */ case class ParseResult(unknown: Context, free: Context, term: Term) { def toTerm = { @@ -91,7 +91,7 @@ sealed abstract class HasParentInfo extends ParentInfo { def docParent: DPath } /** the content is located inside a document - * @param parent the parent document + * @param docParent the parent document */ case class IsDoc(docParent: DPath) extends HasParentInfo /** the content is located inside a ModuleOrLink diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/parser/Scanner.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/parser/Scanner.scala index f2a2061a9d..fa50bdeae0 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/parser/Scanner.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/parser/Scanner.scala @@ -16,8 +16,7 @@ import info.kwarc.mmt.api.parser.ActiveNotation._ * @param tl the TokenList to scan * matched notations are applied to tl, i.e., tl always holds the current TokenList */ -class Scanner(val tl: TokenList, parsingUnitOpt: Option[ParsingUnit], ruleTableInit: ParsingRuleTable, - val report: frontend.Report) extends frontend.Logger { +class Scanner(val tl: TokenList, parsingUnitOpt: Option[ParsingUnit], ruleTableInit: ParsingRuleTable, val report: frontend.Report) extends frontend.Logger { val logPrefix = "scanner" private def logState() { @@ -332,7 +331,7 @@ class Scanner(val tl: TokenList, parsingUnitOpt: Option[ParsingUnit], ruleTableI } /** scans for some notations and applies matches to tl - * @param ns the notations to scan for + * @param group the rules to scan with */ private def scan(group: ParsingRuleGroup) { log("scanning " + tl + " with notations " + group.rules.mkString(",")) @@ -370,42 +369,27 @@ case class FoundDelim(pos: Int, delim: Delimiter) extends Found { /** anything but a delimiter */ sealed abstract class FoundContent extends Found { + /** the corresponding [[Marker]] */ + def marker: ChildMarker /** the number of the corresponding [[Marker]] */ - def number: Int + def number = marker.number } -/** represents a [[notations.Arg]] that was found - * @param slice the TokenSlice where it was found - * (as TokenList's are mutable, slice is not necessarily valid in the future) - * @param n the number of the Arg +/** represents a single child that was found + * @param slice the TokenSlice where it was found (as TokenList's are mutable, slice is not necessarily valid in the future) */ -sealed abstract class FoundArg extends FoundContent { - val slice: TokenSlice +case class FoundSimp(slice: TokenSlice, marker: ChildMarker) extends FoundContent { override def toString: String = slice.toString - def fromTo: Some[(Int, Int)] = Some((slice.start, slice.next)) - } -case class FoundSimpArg(slice : TokenSlice, number : Int) extends FoundArg - -case class FoundOML(slice : TokenSlice, number: Int, info: LabelInfo) extends FoundArg - -/** represents an [[notations.SeqArg]] that was found - * @param n the number of the SeqArg - * @param args the arguments that were found - */ -sealed abstract class FoundSeqArg extends FoundContent { - val args: List[FoundArg] +/** represents a child sequence (SeqArg or Var) that was found */ +case class FoundSeq(marker: ChildMarker, args: List[FoundSimp]) extends FoundContent { override def toString: String = number.toString + args.map(_.toString).mkString(":(", " ", ")") - def fromTo: Option[(Int, Int)] = if (args.isEmpty) None else Some((args.head.slice.start, args.last.slice.next)) - } -case class FoundSimpSeqArg(number : Int, args : List[FoundSimpArg]) extends FoundSeqArg - -case class FoundSeqOML(number: Int, args: List[FoundOML], info: LabelInfo) extends FoundSeqArg +/* /** helper class for representing a single found variable * @param pos first Token @@ -473,3 +457,5 @@ object FoundVar { /** final state in FoundVar */ val Done = -1 } + +*/ \ No newline at end of file diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/parser/StructureParser.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/parser/StructureParser.scala index 25596eb7f3..0b947b09ee 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/parser/StructureParser.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/parser/StructureParser.scala @@ -348,7 +348,11 @@ class KeywordBasedParser(objectParser: ObjectParser) extends Parser(objectParser if (nc(c).isDefined) errorCont(makeError(treg, "notation of this constant already given, ignored")) else { - val notation = TextNotation.parse(notString, state.namespaces) + val notation = try { + TextNotation.parse(notString, state.namespaces) + } catch {case p: ParseError => + throw makeError(treg, "error in notation", Some(p)) + } SourceRef.update(notation,state.makeSourceRef(SourceRegion(treg.start,state.reader.getLastReadSourcePosition))) nc(c) = notation } @@ -804,11 +808,11 @@ class KeywordBasedParser(objectParser: ObjectParser) extends Parser(objectParser readDelimiter("abbrev", "=") match { case "abbrev" => val (_, _, df) = readParsedObject(context) - val v = View(ns, name, from, to, Some(df.toTerm), isImplicit) + val v = View(ns, name, from, to, TermContainer.asParsed(df.toTerm), isImplicit) moduleCont(v, parent) end(v) case "=" => - val v = View(ns, name, from, to, None, isImplicit) + val v = View(ns, name, from, to, TermContainer.empty(), isImplicit) moduleCont(v, parent) logGroup { readInModule(v, context ++ v.getInnerContext, noFeatures)(state.copy()) diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/parser/TokenList.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/parser/TokenList.scala index 5f2f9bb30c..d506a56344 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/parser/TokenList.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/parser/TokenList.scala @@ -153,6 +153,15 @@ class TokenList(private var tokens: List[TokenListElem]) { /** returns all tokens */ def getTokens: List[TokenListElem] = tokens + /** if this list consists of a single word, return it */ + def isSingleWord: Option[Token] = { + if (tokens.length != 1) None else { + tokens.head match { + case t: Token => Some(t) + case _ => None + } + } + } /** * applies a notation and transforms this token list accordingly (this is the only place where [[MatchedList]]s are created) * @param an the notation to reduce @@ -161,31 +170,30 @@ class TokenList(private var tokens: List[TokenListElem]) { */ def reduce(an: ActiveNotation, rt: ParsingRuleTable, rep: frontend.Report): (Int, Int) = { val found = an.getFound - def doFoundArg(fa: FoundArg): UnmatchedList = { + def doFoundSimp(fa: FoundSimp): UnmatchedList = { fa match { - case fa: FoundArg => + case fa: FoundSimp => // fa.slice might be a single token, but that is harmless - val ul = new UnmatchedList(new TokenList(fa.slice.toList)) - ul.scanner = new Scanner(ul.tl, None, rt, rep) - ul + new UnmatchedList(new TokenList(fa.slice.toList), None, rt, rep) } } var newTokens: List[(FoundContent, List[UnmatchedList])] = Nil found foreach { case _: FoundDelim => - case fa: FoundArg => - newTokens ::= (fa, List(doFoundArg(fa))) - case fsa : FoundSeqArg => - newTokens ::= (fsa, fsa.args map doFoundArg) - case fv: FoundVar => + case fa: FoundSimp => + newTokens ::= (fa, List(doFoundSimp(fa))) + case fsa : FoundSeq => + newTokens ::= (fsa, fsa.args map doFoundSimp) +/* case fv: FoundVar => DELETE after testing 2020-07-07 val toks = fv.getVars flatMap { case SingleFoundVar(_, _, tpOpt) => tpOpt match { - case Some(fa) => List(doFoundArg(fa)) + case Some(fa) => List(doFoundSimp(fa)) case None => Nil } } newTokens ::= (fv, toks) + */ } val (from, to) = an.fromTo checkIndex(from, "active notation is " + an.toString) @@ -252,17 +260,19 @@ case class Token(word: String, firstPosition: SourcePosition, whitespaceBefore: * @param text the characters making up the token */ abstract class ExternalToken(text: String) extends PrimitiveTokenListElem(text) { - /** a continuation function called by the parser when parsing this Token - * - * @param outer the ParsingUnit during which this ExternalToken was encountered - * @param BoundName the context - * @param parser the parser calling this function - */ + /** a continuation function called by the parser when parsing this Token */ def parse(input: ExternalTokenParsingInput): Term } -/** bundles arguments passed into [[ExternalToken]] */ -case class ExternalTokenParsingInput(outer: ParsingUnit, boundNames: List[BoundName], parser: ObjectParser, errorCont: ErrorHandler) +/** bundles arguments passed into [[ExternalToken]] + * @param outer the ParsingUnit during which this ExternalToken was encountered + * @param parser the object parser + * @param errorCont error handler +*/ +abstract class ExternalTokenParsingInput(val outer: ParsingUnit, val parser: ObjectParser, val errorCont: ErrorHandler) { + /** callback function to parse terms within the current parser */ + def callbackParse(reg: SourceRegion, term: String): Term +} /** A convenience class for an ExternalToken whose parsing is context-free so that it can be parsed immediately * @param term the result of parsing @@ -301,13 +311,12 @@ class MatchedList(val tokens: List[(FoundContent,List[UnmatchedList])], val an: * not known (yet) which notation should be used. * * @param tl the TokenList that is to be reduced - * @param localNotations notations that should additionally be used for this subterm */ -class UnmatchedList(val tl: TokenList) extends TokenListElem { +// TODO: merge this with the Scanner class +class UnmatchedList(val tl: TokenList, parsingUnitOpt: Option[ParsingUnit], rt: ParsingRuleTable, rep: frontend.Report) extends TokenListElem { val firstPosition = tl(0).firstPosition val lastPosition = tl(tl.length - 1).lastPosition - private[parser] var scanner: Scanner = null - private[parser] var localNotations: Option[ParsingRuleTable] = None + private val scanner: Scanner = new Scanner(tl, parsingUnitOpt, rt, rep) def addRules(rules : ParsingRuleTable, replace: Boolean) { scanner.addRules(rules, replace) tl.getTokens.foreach { @@ -316,6 +325,7 @@ class UnmatchedList(val tl: TokenList) extends TokenListElem { case _ => } } + def scan() {scanner.scan()} override def toString: String = if (tl.length == 1) tl(0).toString else "{unmatched " + tl.toString + " unmatched}" } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/presentation/MMTSyntaxPresenter.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/presentation/MMTSyntaxPresenter.scala index cc5201e64c..8efe01d769 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/presentation/MMTSyntaxPresenter.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/presentation/MMTSyntaxPresenter.scala @@ -3,7 +3,7 @@ package info.kwarc.mmt.api.presentation import info.kwarc.mmt.api._ import info.kwarc.mmt.api.documents._ import info.kwarc.mmt.api.modules._ -import info.kwarc.mmt.api.objects.{OMMOD, OMPMOD, Term} +import info.kwarc.mmt.api.objects.{OMID, OMMOD, OMPMOD, Term} import info.kwarc.mmt.api.opaque.{OpaqueElement, OpaqueText, OpaqueTextPresenter} import info.kwarc.mmt.api.symbols._ import info.kwarc.mmt.api.utils.URI @@ -11,9 +11,22 @@ import info.kwarc.mmt.api.utils.URI /** * Presenter writing out parsable MMT surface syntax. * - * This class supersedes the now deleted class MMTStructurePresenter. - * Previously was just a minimally modified copy of MMTStructurePresenter - * now been deleted. + * Usage + * {{{ + * import info.kwarc.mmt.api.presentation + * import info.kwarc.mmt.api.presentation.MMTSyntaxPresenter + * + * val presenter = state.ctrl.extman.getOrAddExtension(classOf[MMTSyntaxPresenter], "present-text-notations").getOrElse( + * throw new Exception // do something + * ) + * + * val stringRenderer = new presentation.StringBuilder + * val yourTheory : Theory = ??? + * presenter(yourTheory) + * + * println(stringRenderer.get) + * }}} + * */ class MMTSyntaxPresenter(objectPresenter: ObjectPresenter = new NotationBasedPresenter) extends Presenter(objectPresenter) { @@ -24,6 +37,9 @@ class MMTSyntaxPresenter(objectPresenter: ObjectPresenter = new NotationBasedPre **/ protected val presentGenerated = false + /** + * The format of [[MMTSyntaxPresenter]] as an extension + */ def key: String = "present-text-notations" + (if (presentGenerated) "-flat" else "") override def outExt = "mmt" @@ -36,7 +52,7 @@ class MMTSyntaxPresenter(objectPresenter: ObjectPresenter = new NotationBasedPre /** * Present an element such as a [[Theory]], a [[View]] or a [[Declaration]]. * - * @param element The element to present. It must already be added to the [[Controller]] instance this extension is linked to. + * @param element The element to present. It must already be added to the [[info.kwarc.mmt.api.frontend.Controller]] instance this extension is linked to. * @param standalone if true, include appropriate header and footer * @param rh output stream */ @@ -45,6 +61,18 @@ class MMTSyntaxPresenter(objectPresenter: ObjectPresenter = new NotationBasedPre present(element, rh)(new PersistentNamespaceMap) } + /** + * Present an element such as a [[Theory]], a [[View]] or a [[Declaration]] to a string. + * + * Behavior equals [[apply()]] with [[presentation.StringBuilder]] as the [[RenderingHandler]]. + */ + def presentToString(element: StructuralElement, standalone: Boolean = false): String = { + val stringRenderer = new presentation.StringBuilder + apply(element, standalone)(stringRenderer) + + stringRenderer.get + } + /** * Get a wrapping rendering handler indenting every line it is passed to. * @@ -206,23 +234,9 @@ class MMTSyntaxPresenter(objectPresenter: ObjectPresenter = new NotationBasedPre rh(" =") rh(" \n") - // View assignment are actually [[Constant]]s, but in their presented syntax they do not - // feature a type component. Hence, we need this function instead of just delegating - // to presentation of, say a theory's constant. - def presentViewAssignment(assignment: Constant): Unit = { - assignment.df match { - case Some(definiens) => - indented(rh)(assignment.name.last.toString) - rh(" = ") - apply(definiens, Some(assignment.path $ DefComponent))(rh) - rh(DECLARATION_DELIMITER + "\n") - case _ => ??? // TODO Can view constants have no definiens? If not, rewrite to throw exception here - } - } - val declarations = if (presentGenerated) view.getDeclarations else view.getPrimitiveDeclarations declarations.foreach { - case c: Constant => presentViewAssignment(c) + case c: Constant => doConstant(c, rh, presentType = false) // In all other cases, present as usual, this might present invalid syntax, though case d => present(d, indented(rh)) } @@ -237,7 +251,8 @@ class MMTSyntaxPresenter(objectPresenter: ObjectPresenter = new NotationBasedPre if (args.nonEmpty) { rh("(") args.foreach(o => { - apply(o, None)(rh); rh(" ") + apply(o, None)(rh); + rh(" ") }) rh(")") } @@ -245,75 +260,117 @@ class MMTSyntaxPresenter(objectPresenter: ObjectPresenter = new NotationBasedPre objectLevel.apply(tm, None)(rh) } - private def doConstant(c: Constant, rh: RenderingHandler)(implicit nsm: PersistentNamespaceMap): Unit = { - rh(c.name.last.toString) - - val hadAlias = c.alias.nonEmpty - c.alias foreach { a => - rh(" @ ") - rh(a.toPath) + /** + * Present a constant + * + * @param presentType If true, the type component is presented if one exists. If false, the type component is not presented even if it exists. + */ + private def doConstant(c: Constant, rh: RenderingHandler, presentType: Boolean = true)(implicit nsm: PersistentNamespaceMap): Unit = { + /* + A constant is built of multiple "elements" delimited by [[OBJECT_DELIMITER]]. + For example, here is a [[Constant]] with many elements: + + {{{ + judgement + : {V:vocabulary}{Γ:Ctx V}Expr Γ⟶(Expr Γ⟶prop) + ❘ = [V:vocabulary][Γ:Ctx V][e:Expr Γ][E:Expr Γ]foo bar + ❘ role simplify + ❘ @ jud + ❘ @ judgment + ❘ # 1 |- 2 ∶ 3 prec 100 + ❘ meta metakey1 ?DummyTheory2 + ❘ meta metakey2 (f x) + ❙ + }}} + + Note that "elements" is a term I made-up for the sake of this comment. + It differs from the word "component" (established in MMT circles) insofar that for instance + above we have multiple "meta" elements, but MMT treats the set of all metadatums of a constant + as a single [[MetaDataComponent]] of that constant. + + In the following we aggregate lists of such elements, which we later join by appropriate + newlines and [[OBJECT_DELIMITER object delimiters]]. + Since instead of naive string concatenation, we use the concept of [[RenderingHandler rendering handlers]] + overall, these lists of elements are actually lists of callback functions each accepting a rendering handler. + */ + + // usual type and definiens, each lists of at most one element + val typeElements = if (presentType) { + c.tp.toList.map(typeTerm => (rh: RenderingHandler) => { + rh(": ") + apply(typeTerm, Some(c.path $ TypeComponent))(rh) + }) + } else { + Nil } - val hadTypeComponent = c.tp.isDefined - c.tp foreach { typeTerm => - if (hadAlias) { - rh(OBJECT_DELIMITER) - } + val definiensElements = c.df.toList.map(definiensTerm => (rh: RenderingHandler) => { + rh("= ") + apply(definiensTerm, Some(c.path $ DefComponent))(rh) + }) - rh("\n") - indented(rh)(" : ") - apply(typeTerm, Some(c.path $ TypeComponent))(rh) - } + // miscellaneous elements + val aliasElements = c.alias.map(a => (rh: RenderingHandler) => { + rh(s"@ ${a.toPath}") + }) - val hadDefiniensComponent = c.df.isDefined - c.df foreach { definiensTerm => - if (hadTypeComponent || hadAlias) { - rh(OBJECT_DELIMITER) - } + val roleElements = c.rl.toList.map(roleStr => (rh: RenderingHandler) => { + rh(s"role ${roleStr}") + }) - rh("\n") - indented(rh)(" = ") - apply(definiensTerm, Some(c.path $ DefComponent))(rh) - } + val notationElements = c.notC.parsing.toList.map(textNotation => (rh: RenderingHandler) => { + rh(s"# ${textNotation.toText}") + }) - c.notC.parsing foreach { textNotation => - if (hadDefiniensComponent || hadTypeComponent || hadAlias) { - rh(OBJECT_DELIMITER) - } + // TODO: (1) generalize this metadata output to theories, views (i.e. modules), and documents + // (2) Do not print special metadata like source references + val metadataElements = c.metadata.getAll.map(datum => (rh: RenderingHandler) => { + rh("meta ") + doURI(OMID(datum.key), rh) + rh(" ") + objectPresenter(datum.value, Some(c.path $ MetaDataComponent))(rh) + }) + + // aggregate all elements in visually pleasing order + val elements: List[RenderingHandler => Unit] = + typeElements ::: definiensElements ::: roleElements ::: aliasElements ::: notationElements ::: metadataElements - rh("\n") - indented(rh)(" # ") - rh(textNotation.toText) + // present all elements + rh(c.name.last.toString) + val indentedRh = indented(rh) + elements.zipWithIndex.foreach { case (renderFunction, index) => + if (index == 0) { + indentedRh("\n" + " ") + } else { + indentedRh("\n" + OBJECT_DELIMITER + " ") + } + renderFunction(indentedRh) } + rh("\n") } - private def doStructure(s: Structure, rh: RenderingHandler)(implicit nsm: PersistentNamespaceMap): Unit = { - val decs = s.getPrimitiveDeclarations - if (decs.isEmpty) { + private def doStructure(s: Structure, rh: RenderingHandler)(implicit nsm: PersistentNamespaceMap): Unit = s match { + // special case of structures: trivial include + case Include(IncludeData(home, from, args, df, total)) => rh("include ") - - // In views "include" declarations carry a definiens component. - // As an example, consider the view v from a theory T1 to a theory T2. - // - // (1) Let both theories include a common base theory S, then the view can - // encompass "include ?S" which will be turned into a structure whose - // type component is "?T1" and whose definiens component is `OMIDENT(OMID - // (T2.path))` - // (2) Let T1 include S1 and T2 include S2 and phi: S1 -> S2 a morphism. - // Then the view v can encompass "include ?phi", which will be turned - // into a structure whose type component is "?S1" and whose definiens - // component is "?phi". - // TODO(Florian|Dennis) Have a look at the description above and confirm/decline. - val incl = if (s.df.isDefined) s.df.get else s.from - doURI(incl, rh) - } else { - rh("structure " + s.name + " : " + s.from.toMPath.^^.last + "?" + s.from.toMPath.last) - //this.present(s.from, Some(s.path $ TypeComponent)) + doURI(OMMOD(from), rh) + // TODO args ignored + df.foreach(definiensTerm => { + rh(" " + OBJECT_DELIMITER + " ") + objectPresenter(definiensTerm, Some(s.path $ DefComponent))(rh) + }) + + // actual structure, not just trivial include + case _ => + rh("structure " + s.name + " : ") + doURI(s.from, rh) doDefComponent(s, rh) - // TODO Why not reuse "presentViewAssignment" from [[doView]]? - decs.foreach { d => present(d, indented(rh)) } - } + val indentedRh = indented(rh) + s.getDeclarations.foreach { + case c: Constant => doConstant(c, indentedRh, presentType = false) + case x => present(x, indentedRh) + } } /** `= df` if df is present, returns true if there was one */ diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/presentation/NotationBasedPresenter.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/presentation/NotationBasedPresenter.scala index cd111748e5..9905589318 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/presentation/NotationBasedPresenter.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/presentation/NotationBasedPresenter.scala @@ -594,8 +594,7 @@ class NotationBasedPresenter extends ObjectPresenter { case None => return default case Some(objP) => - val PragmaticTerm(op, subargs, context, args, attrib, not, pos) = objP - if (attrib) return default // TODO specify and implement handling of HOAS type attributions + val PragmaticTerm(op, subargs, context, args, not, pos) = objP val firstVarNumber = subargs.length+1 val firstArgNumber = subargs.length+context.length+1 /* @@ -665,9 +664,6 @@ class NotationBasedPresenter extends ObjectPresenter { case c @ Var(n, typed, _,_) => //sequence variables impossible due to flattening doChild(c, context(n-firstVarNumber), currentPosition) if (compFollows) doSpace(1) - case AttributedObject => - // we know attributee.isDefined due to flattening - //TODO case d: Delimiter => val dE = d.expand(op, getAlias(op)) val unpImps = if (unplacedImplicitsDone) Nil else unplacedImplicits map { @@ -733,7 +729,7 @@ class NotationBasedPresenter extends ObjectPresenter { } } val br = bracket(not) - val flatMarkers = not.arity.flatten(not.presentationMarkers, subargs.length, context.length, args.length, attrib) + val flatMarkers = not.arity.flatten(not.presentationMarkers, subargs.length, context.length, args.length) br match { case n if n > 0 => doBracketedGroup { doMarkers(flatMarkers) } case 0 => doOptionallyBracketedGroup { doMarkers(flatMarkers) } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/presentation/Presenter.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/presentation/Presenter.scala index d2573180fb..e6d2198bfd 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/presentation/Presenter.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/presentation/Presenter.scala @@ -65,7 +65,7 @@ abstract class Presenter(val objectLevel: ObjectPresenter) /** relegates to objectPresenter */ def apply(o: Obj, origin: Option[CPath])(implicit rh : RenderingHandler): Unit = objectLevel(o, origin) - override def outDim = Dim("export", "presentation", key) + override def outDim: Dim = Dim("export", "presentation", key) } /** helper object */ diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/proving/imperative/Language.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/proving/imperative/Language.scala deleted file mode 100644 index 983d79f0e7..0000000000 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/proving/imperative/Language.scala +++ /dev/null @@ -1,20 +0,0 @@ -package info.kwarc.mmt.api.proving.imperative - -import info.kwarc.mmt.api._ -import objects._ - -case class ImperativeProof(steps: List[ProofStep]) - -abstract class ProofStep { - -} - -case class Assume(name: LocalName, tp: Term) extends ProofStep -case class Let(name: List[LocalName], tp: Option[Term], df: Term) extends ProofStep -case class Hence(tp: Term, by: ImperativeProof, name: Option[LocalName]) extends ProofStep - -case class Suffices(newGoal: Term, by: ImperativeProof) extends ProofStep -case class Cases(splitOn: Term, cases: List[(Term,ImperativeProof)]) extends ProofStep -case class LocalRef(name: LocalName) extends ProofStep -case class ProofTerm(pf: Term) extends ProofStep - diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/proving/imperative/Prover.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/proving/imperative/Prover.scala deleted file mode 100644 index e7466778d4..0000000000 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/proving/imperative/Prover.scala +++ /dev/null @@ -1,44 +0,0 @@ -package info.kwarc.mmt.api.proving.imperative - -import info.kwarc.mmt.api._ -import objects._ - -class Prover { - // for now: manually put all rules; eventually list generated from current MMT context - private val rules: List[ProofStepRule] = Nil - - def prove(context: Context, goal: Term, proof: ImperativeProof) = { - var state = new ProofState(context, goal) - proof.steps foreach {step => - val ruleO = rules.find(_.applicable(step)) - ruleO match { - case Some(r) => r(this)(state, step) - case None => // report error - } - } - } -} - -class ProofState(initContext: Context, initGoal: Term) { - var context = initContext - var goal = initGoal -} - -abstract class ProofStepRule { - def applicable(step: ProofStep): Boolean - def apply(prover: Prover)(state: ProofState, step: ProofStep): Unit -} - -object CasesRule extends ProofStepRule { - def applicable(step: ProofStep) = step match {case _:Cases => true case _ => false} - - def apply(prover: Prover)(state: ProofState, step: ProofStep) { - val Cases(split, cases) = step - // TODO proof split - cases foreach {case (ass,pf) => - val assName = LocalName("a") - val newCon = state.context ++ VarDecl(assName, ass) - prover.prove(newCon, state.goal, pf) - } - } -} diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/symbols/Constant.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/symbols/Constant.scala index 6de89f1543..138cdbd7ed 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/symbols/Constant.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/symbols/Constant.scala @@ -77,8 +77,8 @@ object Visibility { * @param home the parent theory * @param name the name of the constant * @param alias an alternative (usually shorter) name - * @param tp the optional type - * @param df the optional definiens + * @param tpC the type container optionally containing a type + * @param dfC the definiens container optionally containing a definiens * @param rl the role of the constant */ class FinalConstant(val home : Term, val name : LocalName, val alias: List[LocalName], @@ -93,7 +93,7 @@ object Constant { * TermContainer factory */ def apply(home : Term, name : LocalName, alias: List[LocalName], tp: Option[Term], df: Option[Term], - rl : Option[String], not: NotationContainer = NotationContainer()) = + rl : Option[String], not: NotationContainer = NotationContainer.empty()) = new FinalConstant(home, name, alias, TermContainer(tp), TermContainer(df), rl, not, Visibility.public) def apply(home : Term, name : LocalName, alias: List[LocalName], tpC : TermContainer, dfC : TermContainer, rl : Option[String], notC: NotationContainer) = diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/symbols/DerivedDeclaration.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/symbols/DerivedDeclaration.scala index a078d7e448..5b7888b13c 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/symbols/DerivedDeclaration.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/symbols/DerivedDeclaration.scala @@ -117,7 +117,7 @@ abstract class GeneralStructuralFeature[Level <: DerivedContentElement](val feat def getHeaderNotation: List[Marker] /** the parse rule for the header */ - def getHeaderRule = parser.ParsingRule(mpath, Nil, TextNotation(Mixfix(getHeaderNotation), Precedence.integer(0), None)) + def getHeaderRule = parser.ParsingRule(mpath, Nil, TextNotation(Mixfix(getHeaderNotation), Precedence.integer(0), None, false)) /** parses the header term of a derived declaration into its name and type * by default it is interpreted as OMA(mpath, name :: args) where OMA(mpath, args) is the type @@ -407,11 +407,11 @@ trait TypedParametricTheoryLike extends StructuralFeature with ParametricTheoryL case _ => throw InvalidObject(t, "ill-formed header") } - def parseTypedDerivedDeclaration(dd: DerivedDeclaration, expectedFeature: Option[String]=None) : (Context, List[Term], DerivedDeclaration, Context) = { + def parseTypedDerivedDeclaration(dd: DerivedDeclaration, expectedFeature: Option[List[String]]=None) : (Context, List[Term], DerivedDeclaration, Context) = { val (indDefPath, context, indParams) = ParamType.getParams(dd) val indD = controller.library.get(indDefPath) match { - case indD: DerivedDeclaration if (expectedFeature.isEmpty || expectedFeature == Some(indD.feature)) => indD - case d: DerivedDeclaration => throw LocalError("the referenced derived declaration is not of the feature "+expectedFeature.get+" but of the feature "+d.feature+".") + case indD: DerivedDeclaration if (expectedFeature.isEmpty || expectedFeature.get.contains(indD.feature)) => indD + case d: DerivedDeclaration => throw LocalError("the referenced derived declaration is not among the features "+expectedFeature.get+" but of the feature "+d.feature+".") case _ => throw LocalError("Expected definition of corresponding inductively-defined types at "+indDefPath.toString() +" but no derived declaration found at that location.") } @@ -424,12 +424,12 @@ trait TypedParametricTheoryLike extends StructuralFeature with ParametricTheoryL } def checkParams(indCtx: Context, indParams: List[Term], context: Context, env: ExtendedCheckingEnvironment) : Unit = { - //A first attempt to check the indParams match the indCtx //check the indParams match the indCtx at least in length if (indCtx .length != indParams.length) { throw LocalError("Incorrect length of parameters for the referenced derived declaration .\n"+ "Expected "+indCtx.length+" parameters but found "+indParams.length+".")} - + + //check whether their types also match indCtx zip indParams map {case (vd, tm) => vd.tp map {expectedType => val tpJudgement = Typing(Stack.empty, tm, expectedType) @@ -457,7 +457,7 @@ object TypedParametricTheoryLike { /** retrieves the parameters and arguments */ def getParams(dd: DerivedDeclaration): (GlobalName, Context, List[Term]) = { dd.tpC.get.get match { - case OMBINDC(OMMOD(mpath), pars, OMS(p)::args) => (p, pars, args) + case OMBINDC(OMMOD(_), pars, OMS(p)::args) => (p, pars, args) } } /** retrieves the parameters */ @@ -465,6 +465,39 @@ object TypedParametricTheoryLike { } } +trait ReferenceLikeTypedParametricTheoryLike extends StructuralFeature with TypedParametricTheoryLike { + // override val ParamType = TypedParametricTheoryLike.ParamType(getClass) + // override val Type = ParametricTheoryLike.Type(getClass) + + /** + * parse the derived declaration into its components + * @param dd the derived declaration + * @return returns a pair containing the mpath of the derived declaration, the declarations defined in the referenced theory, + * the argument context of this derived declaration, the arguments provided to the referenced theory and the outer context + */ + def getDecls(dd: DerivedDeclaration): (GlobalName, List[Declaration], Context, List[Term], Context) = { + val (indDefPathGN, context, indParams) = ParamType.getParams(dd) + val (indCtx, decs) : (Context, List[StructuralElement])= controller.getO(indDefPathGN) match { + case Some(str) => str match { + case t: Theory => (t.parameters, t.getDeclarations) + case t: StructuralElement if (t.feature =="theory") => + val decs = t.getDeclarations + //We shouldn't have to do something this ugly, for this match to work out + //TODO: There should be a better method provided by the api + val params = t.headerInfo match { + case HeaderInfo("theory", _, List(_, params: Context)) => params + case _ => Context.empty + } + (params, decs) + case m : ModuleOrLink => (Context.empty, m.getDeclarations) + case t => throw GeneralError("reflection over unsupported target (expected a theory)"+t.path+" of feature "+t.feature + ": "+t) + } + case None => throw GeneralError("target of reflection not found at "+indDefPathGN) + } + (indDefPathGN, decs.map({d => d match {case d: Declaration => d case _ => throw LocalError("unsupported structural element at "+d.path)}}), indCtx, indParams, context) + } +} + /** * Generative, definitional functors/pushouts with free instantiation * called structures in original MMT diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/symbols/MultiDimTerm.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/symbols/MultiDimTerm.scala index dd77f4b534..8ce807d088 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/symbols/MultiDimTerm.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/symbols/MultiDimTerm.scala @@ -238,6 +238,8 @@ object TermContainer { } def asParsed(term: Term): TermContainer = asParsed(Some(term)) + + def empty(): TermContainer = new TermContainer } /** container for mutable contexts */ diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/uom/EnumerateTest.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/uom/EnumerateTest.scala new file mode 100644 index 0000000000..89828cb20a --- /dev/null +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/uom/EnumerateTest.scala @@ -0,0 +1,36 @@ +import info.kwarc.mmt.api.uom._ + +object EnumerateTest { + def enumerateSemanticType(st: SemanticType, m: Int) { + + val it = st.enumerate(m).getOrElse {return} + + while (it.hasNext) { + println(st.toString(it.next)) + } + + } + + def main(args: Array[String]) { + + /** SemanticTypes: + * StandardInt + * StandardNat + * StandardPositive + * StandardRat + * ComplexRat + * StandardBool + * ListType(SemanticType) + * TupleType(SemanticType, dimension) + * IntModulo (class) + * Product (class) + * StandardString + * Not Working: IntModulo*/ + + //Any randomization and quick count should use a mode selection (numbers only) + //StandardBool only has 2 values, mode selection should have no influence + //modes for numbers: 0 = random, else the number determines increment + enumerateSemanticType(StandardString, 5) + + } +} \ No newline at end of file diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/uom/GenericScalaExporter.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/uom/GenericScalaExporter.scala index b2600677fd..19cdcbb426 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/uom/GenericScalaExporter.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/uom/GenericScalaExporter.scala @@ -86,12 +86,20 @@ object GenericScalaExporter { } case class ArgumentList(args: List[Argument]) { def +(that: ArgumentList) = ArgumentList(this.args ::: that.args) + // if at most the last argument is a sequence, this appends ::Nil if needed + def finalNil = if (args.lastOption.exists(a => !a.sequence)) " :: Nil" else "" + // x1, ..., xn def names = args.map(_.name) + // T1, ..., Tn def types = args.map(_.tp) // x1: T1, ..., xn: Tn val decls = args.map(_.decl) + // x1: T1, ..., xn: Tn + val declsConsed = decls.map(d => "(" + d + ")").mkString(" :: ") + finalNil // List(x1,...,xn) - def nameList = args.map {a => if (a.sequence) a else "List(" + a + ")"}.mkString(":::") + def nameList = args.map {a => if (a.sequence) a.name else "List(" + a.name + ")"}.mkString(":::") + // x1::x2:: ... :: xn [:: Nil] + def namesConsed = names.mkString(" :: ") + finalNil // (x1, ..., xn) or x1 def tuple = if (args.length == 1) names.head else names.mkString("(", ", ", ")") def tupleType = if (args.length == 0) "Unit" else if (args.length == 1) types.head else types.mkString("(", ", ", ")") @@ -101,20 +109,17 @@ object GenericScalaExporter { * @param second the arguments */ abstract class Operator(first: ArgumentList, second: ArgumentList) { - /** maps a list of variables and a list of arguments to a term */ - def mmtTerm(vs: List[String], as: List[String]): String - def combined = first+second - def term = mmtTerm(first.names, second.names) - def pattern = mmtTerm(first.decls, second.decls) def tuple = combined.tuple def tupleType = combined.tupleType def context = combined.decls.mkString(",") + /** maps a list of variables and a list of arguments to a term */ + def makeTerm(pattern: Boolean): String // def apply(x1,...,xn) : Term = term - def applyMethod = List(s"def apply($context): Term = $term") + def applyMethod = List(s"def apply($context): Term = ${makeTerm(false)}") def unapplyMethod = List( s"def unapply(t: Term): Option[$tupleType] = t match {", - s" case $pattern => Some($tuple)", + s" case ${makeTerm(true)} => Some($tuple)", s" case _ => None", s"}" ) @@ -124,14 +129,14 @@ object GenericScalaExporter { /** for MMT terms using OMS, OMA, and OMBIND */ class MMTOperator(p: ContentPath, f: ArgumentList, s: ArgumentList) extends Operator(f,s) { private def omid = "OMID(this.path)" - def f(as: List[String]) = as.mkString(",") - def mmtTerm(a1: List[String], a2: List[String]): String = { - if (a1.isEmpty && a2.isEmpty) + def makeTerm(pattern: Boolean): String = { + val sS = if (pattern) s.declsConsed else s.nameList + if (f.args.isEmpty && s.args.isEmpty) omid - else if (a1.isEmpty) - s"OMA($omid, List(${f(a2)}))" + else if (f.args.isEmpty) + s"OMA($omid, $sS)" else - s"OMBINDC($omid, ${f(a1)}, ${f(a2)})" + s"OMBINDC($omid, ${f.nameList}, $sS)" } } } @@ -254,7 +259,7 @@ class GenericScalaExporter extends Exporter { } /** 1%w, */ - private val defaultNotation = TextNotation(Mixfix(List(SimpSeqArg(1, Delim(" "), CommonMarkerProperties.noProps))), Precedence.integer(0), None) + private val defaultNotation = TextNotation(Mixfix(List(SimpSeqArg(1, Delim(" "), CommonMarkerProperties.noProps))), Precedence.integer(0), None, false) /** additional lines (without indentation) to be added to the companion object, most importantly the apply/unapply methods */ protected def companionObjectFields(c: Constant): List[String] = { val nt = c.not.getOrElse(defaultNotation) diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/uom/RealizedType.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/uom/RealizedType.scala index 24ff61518e..87b1ee59c5 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/uom/RealizedType.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/uom/RealizedType.scala @@ -19,7 +19,7 @@ case class RealizedType(synType: Term, semType: SemanticType) extends uom.Simpli */ def of(u: Any) = { if (!semType.valid(u)) { - semType.valid(u) + // semType.valid(u) // only needed for debugging, useful to have a breakpoint here to debug the validity check throw ParseError("invalid literal value for type " + synType + " realized as " + semType.asString + ": " + u) } val vN = semType.normalform(u) diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/uom/SemanticType.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/uom/SemanticType.scala index 4b6a3c592b..3694aa6ff5 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/uom/SemanticType.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/uom/SemanticType.scala @@ -42,7 +42,7 @@ abstract class SemanticType extends SemanticObject { /** @return a LexerExtension that is to be used when this type is in scope */ def lex: Option[parser.LexFunction] = None /** @return a fresh iterator over values of this type */ - def enumerate: Option[Iterator[Any]] = None + def enumerate(mode: Int): Option[Iterator[Any]] = None /** returns a canonical embedding from this type into some other type * only the identity of this type by default, override as needed @@ -67,12 +67,16 @@ trait RSemanticType[V] extends SemanticType { /** this must be the class object of V (which cannot be implemented generically here in Scala) */ val cls: Class[V] - /** does nothing but triggers Scala type checking */ - def apply(v: V): Any = v + /** overridden to sharpen return type */ + override def enumerate(mode: Int): Option[Iterator[V]] = None - /** does nothing but refines the Scala type if possible */ - def unapply(u: Any): Option[V] = u match { + /** does nothing but triggers Scala type checking */ + def apply(v: V): Any = v + + /** does nothing but refines the Scala type if possible */ + def unapply(u: Any): Option[V] = u match { //TODO not typesafe for complex types, cls == u.getClass works for complex types but does not consider subtyping + // maybe use cls.isAssignableFrom(u.getClass) to handle subtyping case v: V@unchecked if cls.isInstance(v) => Some(v) case v: V@unchecked if cls.isPrimitive && cls == u.getClass => diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/uom/Simplifier.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/uom/Simplifier.scala index 39109d019b..6f0ce85b7d 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/uom/Simplifier.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/uom/Simplifier.scala @@ -62,7 +62,26 @@ trait StructureSimplifier extends Extension { * the designated super class of all simplifiers */ abstract class Simplifier(val objectLevel: ObjectSimplifier) extends StructureSimplifier with LeveledExtension { + /** + * Simplify an [[Obj object]] using a set of rules. + * + * @param obj The object to simplify + * @param su Settings of the simplification + * @param rules The rules to use, which can possibly be more or less or totally different than the ones + * visible in `su.context`. + * @return The simplified object + * + * @see [[apply(obj: Obj, su: SimplificationUnit)]] + */ def apply(obj: Obj, su: SimplificationUnit, rules: RuleSet): obj.ThisType = objectLevel(obj, su, rules) + + /** + * Simplify an [[Obj object]] using all rules visible in `su.context`. + * + * @param obj The object to simplify + * @param su Settings of the simplification + * @return The simplified object + */ def apply(obj: Obj, su: SimplificationUnit): obj.ThisType = { val rules = RuleSet.collectRules(controller, su.context) apply(obj, su, rules) diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/uom/StandardLiterals.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/uom/StandardLiterals.scala index 020e844d54..5b2418d023 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/uom/StandardLiterals.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/uom/StandardLiterals.scala @@ -7,84 +7,84 @@ import parser._ import SemanticOperator._ object Product { - val matcher = new utils.StringMatcher2("(",",",")") + val matcher = new utils.StringMatcher2("(",",",")") - def tensor(left: Unary, right: Unary) = { - val f = new Product(left.from, right.from) - val t = new Product(left.to, right.to) - t.pairOps(f.toLeft compose left, f.toRight compose right) - } + def tensor(left: Unary, right: Unary) = { + val f = new Product(left.from, right.from) + val t = new Product(left.to, right.to) + t.pairOps(f.toLeft compose left, f.toRight compose right) + } } case class Product(val left: SemanticType, val right: SemanticType) extends SemanticType { - def asString = "(" + left.asString + "*" + right.asString + ")" - override def valid(u: Any) = u match { - case (l,r) => left.valid(l) && right.valid(r) - case _ => false - } - override def normalform(u: Any) = u match { - case (l,r) => (left.normalform(l), right.normalform(r)) - } - def fromString(s: String) = { - val (s1,s2) = Product.matcher.unapply(s).get - (left.fromString(s1), right.fromString(s2)) - } - def toString(u: Any) = u match { - case (l,r) => Product.matcher(left.toString(l), right.toString(r)) - } - override def enumerate: Option[Iterator[Any]] = { - val lE = left.enumerate.getOrElse {return None} - val rE = right.enumerate.getOrElse {return None} - val i = new Iterator[Any] { - private var lSeen: List[Any] = Nil - private var rSeen: List[Any] = Nil - /** a buffer of precomputed values */ - private var nextFromLeft = true - private var precomputed: List[Any] = Nil - def hasNext = precomputed.nonEmpty || lE.hasNext || rE.hasNext - def next = { - if (precomputed.nonEmpty) { - val h = precomputed.head - precomputed = precomputed.tail - h - } else { - if (nextFromLeft && lE.hasNext) { - val l = lE.next - lSeen ::= l - precomputed = rSeen map {r => (l,r)} - } else { - val r = rE.next - rSeen ::= r - precomputed = lSeen map {l => (l,r)} - } - nextFromLeft = ! nextFromLeft - next - } - } - } - Some(i) - } - - val toLeft = Unary(this, left){case (x,y) => x} - val toRight = Unary(this, right){case (x,y) => y} - - def pairOps(f1: Unary, f2: Unary): Unary = { - if (f1.from == f2.from && f1.to == left && f2.to == right) { - Unary(f1.from, this) {x => (f1.map(x), f2.map(x))} - } else - throw ImplementationError("ill-typed") - } - - override def embed(into: SemanticType) = into match { - case p: Product => (left.embed(p.left), right.embed(p.right)) match { - case (Some(eL), Some(eR)) => Some(Product.tensor(eL,eR)) - case _ => None - } - } - override def subtype(that: SemanticType) = that match { - case p: Product => (p.left subtype left) && (p.right subtype right) - case _ => false - } + def asString = "(" + left.asString + "*" + right.asString + ")" + override def valid(u: Any) = u match { + case (l,r) => left.valid(l) && right.valid(r) + case _ => false + } + override def normalform(u: Any) = u match { + case (l,r) => (left.normalform(l), right.normalform(r)) + } + def fromString(s: String) = { + val (s1,s2) = Product.matcher.unapply(s).get + (left.fromString(s1), right.fromString(s2)) + } + def toString(u: Any) = u match { + case (l,r) => Product.matcher(left.toString(l), right.toString(r)) + } + override def enumerate(m: Int): Option[Iterator[Any]] = { + val lE = left.enumerate(m).getOrElse {return None} + val rE = right.enumerate(m).getOrElse {return None} + val i = new Iterator[Any] { + private var lSeen: List[Any] = Nil + private var rSeen: List[Any] = Nil + /** a buffer of precomputed values */ + private var nextFromLeft = true + private var precomputed: List[Any] = Nil + def hasNext = precomputed.nonEmpty || lE.hasNext || rE.hasNext + def next = { + if (precomputed.nonEmpty) { + val h = precomputed.head + precomputed = precomputed.tail + h + } else { + if (nextFromLeft && lE.hasNext) { + val l = lE.next + lSeen ::= l + precomputed = rSeen map {r => (l,r)} + } else { + val r = rE.next + rSeen ::= r + precomputed = lSeen map {l => (l,r)} + } + nextFromLeft = ! nextFromLeft + next + } + } + } + Some(i) + } + + val toLeft = Unary(this, left){case (x,y) => x} + val toRight = Unary(this, right){case (x,y) => y} + + def pairOps(f1: Unary, f2: Unary): Unary = { + if (f1.from == f2.from && f1.to == left && f2.to == right) { + Unary(f1.from, this) {x => (f1.map(x), f2.map(x))} + } else + throw ImplementationError("ill-typed") + } + + override def embed(into: SemanticType) = into match { + case p: Product => (left.embed(p.left), right.embed(p.right)) match { + case (Some(eL), Some(eR)) => Some(Product.tensor(eL,eR)) + case _ => None + } + } + override def subtype(that: SemanticType) = that match { + case p: Product => (p.left subtype left) && (p.right subtype right) + case _ => false + } } class RProduct[U,V](l: RSemanticType[U], r: RSemanticType[V]) extends Product(l, r) with RSemanticType[(U,V)] { @@ -92,96 +92,167 @@ class RProduct[U,V](l: RSemanticType[U], r: RSemanticType[V]) extends Product(l, } object ListType { - val matcher = new utils.StringMatcher2Sep("[",",","]") + val matcher = new utils.StringMatcher2Sep("[",",","]") } case class ListType(val over: SemanticType) extends SemanticType { - def asString = "List[" + over.asString + "]" - override def valid(u: Any) = u match { - case us: List[_] => us.forall(over.valid) - case _ => false - } - override def normalform(u: Any) = u match { - case us: List[_] => us map over.normalform - } - def fromString(s: String) = { - val us = ListType.matcher.unapply(s).get - us map over.fromString - } - def toString(u: Any) = u match { - case us: List[_] => ListType.matcher(us map over.toString) - } + def asString = "List[" + over.asString + "]" + + override def valid(u: Any) = u match { + case us: List[_] => us.forall(over.valid) + case _ => false + } + + override def normalform(u: Any) = u match { + case us: List[_] => us map over.normalform + } + + def fromString(s: String) = { + val us = ListType.matcher.unapply(s).get + us map over.fromString + } + + def toString(u: Any) = u match { + case us: List[_] => ListType.matcher(us map over.toString) + } + + override def enumerate(m: Int): Option[Iterator[Any]] = { + /** + * value m determines the maximum size of the generated list + */ + val el = over.enumerate(1).getOrElse {return None} + val i = new Iterator[Any] { + private var curlen = 0 + val rand = scala.util.Random + private var c = 0 + private var precomputed: List[Any] = Nil + + def hasNext = precomputed.nonEmpty || el.hasNext + + def next = { + if (precomputed.nonEmpty) { + val h = precomputed.head + precomputed = precomputed.tail + h + } else { + var nli: List[Any] = Nil + while (c <= curlen) { + if(el.hasNext){ + nli ::= el.next() + c += 1 + } + } + c = 0 + if(m == 0){ + curlen = rand.nextInt % 100 + } else{ + curlen = (curlen + 1) % m + } + nli + } + } + } + Some(i) + } } class RList[U](o: RSemanticType[U]) extends ListType(o) with RSemanticType[List[U]] { val cls = classOf[List[U]] } object TupleType { - val matcher = new utils.StringMatcher2Sep("(",",",")") + val matcher = new utils.StringMatcher2Sep("(",",",")") } case class TupleType(val over: SemanticType, val dim: Int) extends SemanticType { - def asString = "(" + over.asString + "^" + dim + ")" - override def valid(u: Any) = u match { - case us: List[_] if us.length == dim => us.forall(over.valid) - case _ => false - } - override def normalform(u: Any) = u match { - case us: List[_] => us map over.normalform - } - def fromString(s: String) = { - val us = TupleType.matcher.unapply(s).get - us map over.fromString - } - def toString(u: Any) = u match { - case us: List[_] => TupleType.matcher(us map over.toString) - } + def asString = "(" + over.asString + "^" + dim + ")" + override def valid(u: Any) = u match { + case us: List[_] if us.length == dim => us.forall(over.valid) + case _ => false + } + override def normalform(u: Any) = u match { + case us: List[_] => us map over.normalform + } + def fromString(s: String) = { + val us = TupleType.matcher.unapply(s).get + us map over.fromString + } + def toString(u: Any) = u match { + case us: List[_] => TupleType.matcher(us map over.toString) + } + override def enumerate(m: Int): Option[Iterator[Any]] = { + val el = over.enumerate(m).getOrElse {return None} + val i = new Iterator[Any] { + private var c = 0 + private var precomputed: List[Any] = Nil + + def hasNext = precomputed.nonEmpty || el.hasNext + + def next = { + if (precomputed.nonEmpty) { + val h = precomputed.head + precomputed = precomputed.tail + h + } else { + var nt: List[Any] = Nil + while (c < dim) { + if(el.hasNext){ + nt ::= el.next() + c += 1 + } + } + c = 0 + nt + } + } + } + Some(i) + } } class RTuple[U](o: RSemanticType[U], d: Int) extends TupleType(o,d) with RSemanticType[List[U]] { val cls = classOf[List[U]] } abstract class Subtype(val of: SemanticType) extends SemanticType { - def asString = "(a subtype of " + of.asString + ")" - def by(u: Any): Boolean - override def valid(u: Any) = of.valid(u) && by(u) - override def normalform(u: Any) = of.normalform(u) - def fromString(s: String) = of.fromString(s) - def toString(u: Any) = of.toString(u) - override def lex = of.lex - /** for a finite subtype of an infinite type, hasNext will eventually run forever */ - override def enumerate = of.enumerate.map(i => i filter by) - - /** inclusion into the supertype */ - def incl = Unary(this, of){x => x} - - override def embed(into: SemanticType) = super.embed(into) orElse {of.embed(into) map {e => incl compose e}} - override def subtype(that: SemanticType) = - (of subtype that) || super.subtype(that) + def asString = "(a subtype of " + of.asString + ")" + def by(u: Any): Boolean + override def valid(u: Any) = of.valid(u) && by(u) + override def normalform(u: Any) = of.normalform(u) + def fromString(s: String) = of.fromString(s) + def toString(u: Any) = of.toString(u) + override def lex = of.lex + /** for a finite subtype of an infinite type, hasNext will eventually run forever */ + override def enumerate(m: Int) = of.enumerate(m).map(i => i filter by) + + /** inclusion into the supertype */ + def incl = Unary(this, of){x => x} + + override def embed(into: SemanticType) = super.embed(into) orElse {of.embed(into) map {e => incl compose e}} + override def subtype(that: SemanticType) = + (of subtype that) || super.subtype(that) } abstract class RSubtype[U](of: RSemanticType[U]) extends Subtype(of) with RSemanticType[U] { val cls = of.cls } abstract class Quotient(val of: SemanticType) extends SemanticType { - def asString = "(a quotient of " + of.asString + ")" - def by(u: Any): Any - override def valid(u: Any) = of.valid(u) - override def normalform(u: Any) = by(of.normalform(u)) - def fromString(s: String) = by(of.fromString(s)) - def toString(u: Any) = of.toString(by(u)) - override def lex = of.lex - /** for a finite quotient of an infinite type, hasNext will eventually run forever */ - override def enumerate = of.enumerate.map {i => - var seen: List[Any] = Nil - i filter {u => - val uN = normalform(u) - val take = ! (seen contains uN) - if (take) seen ::= uN - take - } - } - - /** the projection of an element to its representative */ - def repr = Unary(of, this) {x => normalform(x)} + def asString = "(a quotient of " + of.asString + ")" + def by(u: Any): Any + override def valid(u: Any) = of.valid(u) + override def normalform(u: Any) = by(of.normalform(u)) + def fromString(s: String) = by(of.fromString(s)) + def toString(u: Any) = of.toString(by(u)) + override def lex = of.lex + /** for a finite quotient of an infinite type, hasNext will eventually run forever */ + override def enumerate(m: Int) = of.enumerate(m).map {i => + var seen: List[Any] = Nil + i filter {u => + val uN = normalform(u) + val take = ! (seen contains uN) + if (take) seen ::= uN + take + } + } + + /** the projection of an element to its representative */ + def repr = Unary(of, this) {x => normalform(x)} } abstract class RQuotient[V](of: RSemanticType[V]) extends Quotient(of) with RSemanticType[V] { val cls = of.cls @@ -195,21 +266,27 @@ trait IntegerRepresented extends RSemanticType[BigInt] { /** bundles functions that are typically used when defining literals based on integers */ abstract class IntegerLiteral extends Atomic[BigInt] with IntegerRepresented { - def fromString(s: String) = BigInt(s) - override def lex = Some(new parser.NumberLiteralLexer(false,false)) - override def enumerate = { - val it = new Iterator[BigInt] { - //TODO for testing, it would help to reach high numbers faster - private var precomputed = 0 - def hasNext = true - def next = { - val i = precomputed - precomputed = if (i > 0) -i else -i+1 - i - } - } - Some(it) - } + def fromString(s: String) = BigInt(s) + override def lex = Some(new parser.NumberLiteralLexer(false,false)) + override def enumerate(m: Int) = { + val it = new Iterator[BigInt] { + //TODO for testing, it would help to reach high numbers faster + private val rand = scala.util.Random + private var precomputed = 0 + def hasNext = true + def next = { + val i = precomputed + if(m == 0){ + precomputed = rand.nextInt() + } + else{ + precomputed = if (i > 0) -i else -i+m + } + i + } + } + Some(it) + } } /** standard integer numbers */ @@ -226,41 +303,41 @@ object StandardInt extends IntegerLiteral { /** standard natural numbers */ object StandardNat extends RSubtype(StandardInt) { - override def asString = "nat" - def by(u: Any) = StandardInt.unapply(u).get >= 0 + override def asString = "nat" + def by(u: Any) = StandardInt.unapply(u).get >= 0 } /** standard positive natural numbers */ object StandardPositive extends RSubtype(StandardNat) { - def by(u: Any) = StandardInt.unapply(u).get != 0 + def by(u: Any) = StandardInt.unapply(u).get != 0 } /** standard integers modulo, i.e., a finite type of size modulus */ class IntModulo(modulus: Int) extends RQuotient(StandardInt) { - def by(u: Any) = StandardInt.unapply(u).get mod modulus - /** overridden for efficiency and to ensure termination */ - override def enumerate = Some((0 until modulus).iterator) + def by(u: Any) = StandardInt.unapply(u).get mod modulus + /** overridden for efficiency and to ensure termination */ + override def enumerate(m: Int) = Some((0 until modulus).iterator.map(BigInt(_))) } /** standard rational numbers */ object StandardRat extends RQuotient(new RProduct(StandardInt,StandardPositive)) { - override def asString = "rat" - def by(u: Any): (BigInt,BigInt) = { - val (e:BigInt,d:BigInt) = u - val gcd = e gcd d - (e / gcd, d / gcd) - } - override def toString(u: Any) = { - val (e:BigInt,d:BigInt) = u - if (d == 1) e.toString - else (e.toString + "/" + d.toString) - } - private val matcher = utils.StringMatcher("","/","") - override def fromString(s: String) = s match { - case this.matcher(e,d) => (StandardInt.fromString(e), StandardNat.fromString(d)) - case s => (StandardInt.fromString(s.trim),BigInt(1)) - } - override def lex = Some(new parser.NumberLiteralLexer(false,true)) + override def asString = "rat" + def by(u: Any): (BigInt,BigInt) = { + val (e:BigInt,d:BigInt) = u + val gcd = e gcd d + (e / gcd, d / gcd) + } + override def toString(u: Any) = { + val (e:BigInt,d:BigInt) = u + if (d == 1) e.toString + else (e.toString + "/" + d.toString) + } + private val matcher = utils.StringMatcher("","/","") + override def fromString(s: String) = s match { + case this.matcher(e,d) => (StandardInt.fromString(e), StandardNat.fromString(d)) + case s => (StandardInt.fromString(s.trim),BigInt(1)) + } + override def lex = Some(new parser.NumberLiteralLexer(false,true)) /** embedding into the complex numbers */ override def embed(into: SemanticType) = super.embed(into) orElse { @@ -280,18 +357,44 @@ object ComplexRat extends RProduct(StandardRat, StandardRat) { // switched to java.lang.Double, because that's what .toDouble returns and // java.lang.Double =/= scala.Double (problem in RepresentationType.unapply) object StandardDouble extends Atomic[java.lang.Double] { - def asString = "double" - val cls = classOf[java.lang.Double] - val key = "OMF" - def fromString(s: String) = s.toDouble //s.toDouble - override def lex = Some(new parser.NumberLiteralLexer(true, false, true)) + def asString = "double" + val cls = classOf[java.lang.Double] + val key = "OMF" + def fromString(s: String) = s.toDouble //s.toDouble + override def lex = Some(new parser.NumberLiteralLexer(true, false, true)) } object StandardString extends Atomic[String] { - def asString = "string" - val cls = classOf[String] - def fromString(s: String) = s - override def lex = Some(new SymmetricEscapeLexer('\"', '\\')) + def asString = "string" + val cls = classOf[String] + def fromString(s: String) = s + override def lex = Some(new SymmetricEscapeLexer('\"', '\\')) + override def enumerate(m: Int) = { + val st = new Iterator[String] { + var c = scala.util.Random.alphanumeric + private val rand = scala.util.Random + var len = 0 + var curlen = 0 + def hasNext = true + def next = { + var s1 = "" + while(len < curlen){ + c = c.tail + val s2 = c.head.toString + s1 = s1 + s2 + len += 1 + } + if(m == 0){ + curlen = rand.nextInt() % 20 + } else { + curlen = (curlen + 1)%m + } + len = 0 + s1 + } + } + Some(st) + } } object StringOperations { @@ -310,24 +413,32 @@ object StringOperations { } } -object StandardBool extends Atomic[java.lang.Boolean] { - def asString = "bool" - val cls = classOf[java.lang.Boolean] - def fromString(s: String) = s match { - case "true" => true - case "false" => false - } - override def lex = Some(FiniteKeywordsLexer(List("true","false"))) - override def enumerate = Some(List(true,false).iterator) +object StandardBool extends Atomic[scala.Boolean] { + def asString = "bool" + val cls = classOf[scala.Boolean] + def fromString(s: String) = s match { + case "true" => true + case "false" => false + } + override def lex = Some(FiniteKeywordsLexer(List("true","false"))) + override def enumerate(m: Int) = Some(List(true,false).iterator) + + // Annoyingly, this seems to be necessary, since at key points, the implicit conversions + // java.lang.Boolean <=> scala.Boolean are not applied + override def unapply(u: Any): Option[Boolean] = u match { + case b : Boolean => Some(b) + case b : java.lang.Boolean => Some(b) + case _ => None + } } import utils.URI /** URI literals, concrete syntax is uri"..." */ object URILiteral extends Atomic[URI] { - def asString = "uri" - val cls = classOf[URI] - def fromString(s: String) = URI(s) - override def lex = quotedLiteral("uri") + def asString = "uri" + val cls = classOf[URI] + def fromString(s: String) = URI(s) + override def lex = quotedLiteral("uri") } /** UUIDs */ @@ -344,7 +455,7 @@ object TermLiteral extends Atomic[Term] { val cls = classOf[Term] /** MMT parser is not called here as it depends on context */ def fromString(s: String) = OMSemiFormal(Text("unparsed", s)) - override def atomicToString(t: Term): String = t.toStr(true) + override def atomicToString(t: Term): String = t.toStr(true) } /** defines [[SemanticOperator]]s for the standard arithmetic operations */ @@ -378,17 +489,17 @@ object Arithmetic { } /** OpenMath's literals - * These should be moved to an OpenMath plugin, but they are used by the API, e.g., for metadata - */ + * These should be moved to an OpenMath plugin, but they are used by the API, e.g., for metadata + */ object OMLiteral { - def apply[V <: Any](key: String, semType: Atomic[V]) = - new RepresentedRealizedType(objects.OMS(OpenMath._path ? key), semType) - /** OpenMath OMI - unlimited precision integers */ - val OMI = apply("OMI", StandardInt) - /** OpenMath OMF - IEEE double precision floats */ - val OMF = apply("OMF", StandardDouble) - /** OpenMath OMSTR - strings */ - val OMSTR = apply("OMSTR", StandardString) - /** URIs (not actually part of OpenMath) */ - val URI = apply("URI", URILiteral) + def apply[V <: Any](key: String, semType: Atomic[V]) = + new RepresentedRealizedType(objects.OMS(OpenMath._path ? key), semType) + /** OpenMath OMI - unlimited precision integers */ + val OMI = apply("OMI", StandardInt) + /** OpenMath OMF - IEEE double precision floats */ + val OMF = apply("OMF", StandardDouble) + /** OpenMath OMSTR - strings */ + val OMSTR = apply("OMSTR", StandardString) + /** URIs (not actually part of OpenMath) */ + val URI = apply("URI", URILiteral) } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/utils/BreadthFirstSearch.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/utils/BreadthFirstSearch.scala new file mode 100644 index 0000000000..a740ca7cbb --- /dev/null +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/utils/BreadthFirstSearch.scala @@ -0,0 +1,75 @@ +package info.kwarc.mmt.api.utils + +import scala.collection.mutable + +/** + * Utils for breadth-first-search (BFS) + */ +object BreadthFirstSearch { + /** + * Collect elements in a BFS manner from a bounded set. + * + * @param all The set of all elements. + * @param initial The initial set to start collecting with. + * @param explorer A function exploring all possible elements from one element. + * It is called like `explorer(elem, collectionSoFar, remainingElems)`. + * + * You can use `remainingElems` to optimize your algorithm. E.g. if you needed to + * search through `all` previously, you can just search through `remainingElems`, + * since all other elements are already part of the collection. Thus readding them + * is a no-op. + * + * Invariant: `remainingElems == all - collectionSoFar && remainingElems.contains(elem)`. + * @tparam T The type of elements. + * @return All collected elements. + */ + def collectBounded[T](all: Set[T], initial: Seq[T], explorer: (T, Set[T], Set[T]) => Set[T]): Set[T] = { + val queue = mutable.Queue[T](initial: _*) + val collection = mutable.HashSet[T](initial : _*) + val remaining = mutable.HashSet[T](all.toSeq: _*) + + while (queue.nonEmpty) { + val newNodes = explorer(queue.dequeue(), collection.toSet, remaining.toSet).diff(collection.toSet) + + queue.enqueue(newNodes.toSeq: _*) + collection ++= newNodes + remaining --= newNodes + } + + collection.toSet + } + + /** + * Collect elements in a BFS manner. + * + * @param initial The initial set to start collecting with. + * @param explorer A function exploring all possible elements from one element. + * It is called like `explorer(elem, collectionSoFar)`. + * @tparam T The type of elements. + * @return All collected elements. + */ + def collect[T](initial: Seq[T], explorer: (T, Set[T]) => Set[T]): Set[T] = { + val queue = mutable.Queue[T](initial: _*) + val collection = mutable.HashSet[T](initial : _*) + while (queue.nonEmpty) { + val newNodes = explorer(queue.dequeue(), collection.toSet).diff(collection.toSet) + + queue.enqueue(newNodes.toSeq: _*) + collection ++= newNodes + } + + collection.toSet + } + + /** + * Collect elements in a BFS manner. + * + * @param initial The initial set to start collecting with. + * @param explorer A function exploring all possible elements from one element. + * @tparam T The type of elements. + * @return All collected elements. + */ + def collect[T](initial: Seq[T], explorer: T => Set[T]): Set[T] = { + collect(initial, (t: T, _: Set[T]) => explorer(t)) + } +} \ No newline at end of file diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/utils/MyList.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/utils/MyList.scala index 59b21639c0..e373badedc 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/utils/MyList.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/utils/MyList.scala @@ -16,6 +16,18 @@ case class MyList[A](l: List[A]) { /** like map but with a partial function; removes all results that are None */ def mapPartial[B](f: A => Option[B]): List[B] = l.map(f).filter(_.isDefined).map(_.get) + /** like map but with a partial function; returns a result only if no results are None */ + def mapPartialStrict[B](f: A => Option[B]): Option[List[B]] = { + val bs = l map {a => + f(a) match { + case Some(b) => b + case None => return None + } + } + Some(bs) + } + + /** a map function in which SkipThis() can be called to skip an element */ def mapOrSkip[B](f: A => B): List[B] = l flatMap {a => try {List(f(a))} diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/utils/RunJavaClass.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/utils/RunJavaClass.scala index f10a5b89b0..d04181fcd2 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/utils/RunJavaClass.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/utils/RunJavaClass.scala @@ -1,26 +1,47 @@ package info.kwarc.mmt.api.utils -import info.kwarc.mmt.api.utils.MMTSystem.IsFat - object RunJavaClass { + /** Same as apply, but without arguments */ + def apply(clz: String): ProcessBuilder = apply(clz, Nil) + /** - * Runs a java class (i.e. one with a main method) inside a new java process + * Run a java class (i.e. one with a main method) inside a new java process * Inherits the classpath from the current J */ def apply(clz: String, cargs: List[String]): ProcessBuilder = { // get the classpath, which is either the jar that is provided val cp = System.getProperty("java.class.path") - val args = List( - System.getProperty("java.home") + java.io.File.separator + "bin" + java.io.File.separator + "java", - ) ::: sys.env.get("JAVA_OPTS").map(_.split(" ").toList).getOrElse(Nil) ::: List( - "-cp", - cp, - clz, - ) ::: cargs + val args = + sys.env.get("JAVA_OPTS").map(_.split(" ").toList).getOrElse(Nil) ::: List( + "-cp", + cp, + clz, + ) ::: cargs - new ProcessBuilder(args:_*) + runLongJavaCommand( + javaBinary = System.getProperty("java.home") + java.io.File.separator + "bin" + java.io.File.separator + "java", + args + ) + } + + /** + * Invoke the `java` command with possibly large arguments. + * + * To support large arguments, we use the @argumentFile syntax: + * https://docs.oracle.com/javase/9/tools/java.htm#JSWOR-GUID-4856361B-8BFD-4964-AE84-121F5F6CF111 + * + * That is, we create a file, say `file.txt`, holding the arguments and then invoke `java @file.txt`. + * + * @param javaBinary Path to Java binary + * @param args Arguments + */ + private def runLongJavaCommand(javaBinary: String, args: List[String]): ProcessBuilder = { + val argsFile = java.io.File.createTempFile("MMT-RunJavaClassJavaArgs", ".txt") + File.write(argsFile, args.mkString(" ")) + + // TODO: Temp file produced by RunJavaClass is never deleted + + new ProcessBuilder(javaBinary, s"@${argsFile.getAbsolutePath}") } - /** Same as apply, but without arguments */ - def apply(clz: String): ProcessBuilder = apply(clz, Nil) } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/utils/StringMatcher.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/utils/StringMatcher.scala index 8ef1082c4f..fa947a08d7 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/utils/StringMatcher.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/utils/StringMatcher.scala @@ -8,6 +8,17 @@ object StringMatcher { (before+"(.*)"+middle1+"(.*)"+middle2+"(.*)"+after)r } +class StringSplit(at: Int) { + def unapply(s: String) = { + if (s.length >= at) Some((s.substring(0,2), s.substring(2))) + else None + } +} +object StringSplit { + val At1 = new StringSplit(1) + val At2 = new StringSplit(2) +} + /** * matches S1 in "before S1 after" */ diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/web/ServerPlugin.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/web/ServerPlugin.scala index 1451bb0df6..5a839a0b35 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/web/ServerPlugin.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/web/ServerPlugin.scala @@ -19,7 +19,7 @@ import scala.util.Try * * It will be called on URIs of the form http://server:port/:CONTEXT/PATH?QUERY * - * @param context the CONTEXT + * @param pathPrefix the CONTEXT */ abstract class ServerExtension(val pathPrefix: String) extends FormatBasedExtension { /** @@ -148,7 +148,7 @@ class SVGServer extends ServerExtension("svg") with ContextMenuProvider { } /** @return (d,f) such that d/key/f is the path to the svg file for path exported by key */ - private def svgPath(path: Path): Option[(File, List[String])] = { + private def svgPath(path: Path): Option[(File, FilePath)] = { val (inNarr, newPath) = path.dropComp match { // narrative case dp: DPath => (true, dp) @@ -171,7 +171,8 @@ class SVGServer extends ServerExtension("svg") with ContextMenuProvider { val inPathFile = Archive.MMTPathToContentPath(mp) (arch, "content" :: inPathFile) } - Some((arch.root / "export", relPath)) + val relPathSVG = FilePath(relPath).setExtension("svg") + Some((arch.root / "export", relPathSVG)) } } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/api/web/ServerRequest.scala b/src/mmt-api/src/main/info/kwarc/mmt/api/web/ServerRequest.scala index 4e002645ee..1e11c7730d 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/api/web/ServerRequest.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/api/web/ServerRequest.scala @@ -12,8 +12,8 @@ import scala.xml.Node * Represents a request made to the MMT Server. * * @param method Method used to make request - * @param headerData Request headers that were sent along with the request - * @param sessionID sessionID that was + * @param headers Request headers that were sent along with the request + * @param session sessionID that was * @param path the full path of the request (use pathForExtension when accessing the path in a [[ServerExtension]]) * @param query the query portion of the URL * @param body diff --git a/src/mmt-api/src/main/info/kwarc/mmt/test/MMTUnitTest.scala b/src/mmt-api/src/main/info/kwarc/mmt/test/MMTUnitTest.scala index cfa163aa02..656c962707 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/test/MMTUnitTest.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/test/MMTUnitTest.scala @@ -2,8 +2,8 @@ package info.kwarc.mmt.test import info.kwarc.mmt.api.archives.BuildQueue import info.kwarc.mmt.api.frontend._ +import info.kwarc.mmt.api.objects.Term import info.kwarc.mmt.api.utils - import info.kwarc.mmt.test.testers.BaseTester /** @@ -43,4 +43,14 @@ abstract class MMTUnitTest extends BaseTester { controller.extman.get(classOf[BuildQueue]).foreach(controller.extman.removeExtension) } + def assertEqual[T](expected: T, actual: T, msg: String = ""): Unit = { + if (expected != actual) { + testError("Equality failed" + (if (msg != "") " (" + msg + ")" else "")) + testError("Expected value: " + expected) + testError("Actual value: " + actual) + } + } + + def assertTermEqual(expected: Term, actual: Term, msg: String = ""): Unit = assertEqual _ + def assertSetEqual[T](expected: Set[T], actual: Set[T], msg: String = ""): Unit = assertEqual _ } diff --git a/src/mmt-api/src/main/info/kwarc/mmt/test/testers/ArchiveTester.scala b/src/mmt-api/src/main/info/kwarc/mmt/test/testers/ArchiveTester.scala index d926f7d594..6a83e38e70 100644 --- a/src/mmt-api/src/main/info/kwarc/mmt/test/testers/ArchiveTester.scala +++ b/src/mmt-api/src/main/info/kwarc/mmt/test/testers/ArchiveTester.scala @@ -8,15 +8,14 @@ import scala.util.Try /** trait implementing testing for archives */ trait ArchiveTester extends BaseTester with ActionTester { lazy protected val testBranch: Option[String] = { - // look into the environment and check if the TEST_USE_BRANCH // environment variable is set - val envBranch = Try(sys.env("TEST_USE_BRANCH")).toOption - if(envBranch.nonEmpty) { - envBranch - // else use the current git branch - } else { - MMTSystem.gitVersion + // Then use the appropriate version for the tests + Try(sys.env("TEST_USE_BRANCH")).toOption match { + case Some(b) if b.startsWith("devel-") => Some("devel") + case Some(b) => Some(b) + // fallback to using the system branch + case _ => MMTSystem.gitVersion } } diff --git a/src/mmt-glf/src/info/kwarc/mmt/glf/ElpiGenerationServer.scala b/src/mmt-glf/src/info/kwarc/mmt/glf/ElpiGenerationServer.scala new file mode 100644 index 0000000000..e8fcacc64b --- /dev/null +++ b/src/mmt-glf/src/info/kwarc/mmt/glf/ElpiGenerationServer.scala @@ -0,0 +1,96 @@ +package info.kwarc.mmt.glf + +import info.kwarc.mmt.api.modules.Theory +import info.kwarc.mmt.api.symbols.{Constant, Declaration, PlainInclude, RuleConstant, Structure} +import info.kwarc.mmt.api.{DPath, MPath} +import info.kwarc.mmt.api.utils.{JSONArray, JSONBoolean, JSONObject, JSONString, URI} +import info.kwarc.mmt.api.web.{ServerError, ServerExtension, ServerRequest, ServerResponse} +import info.kwarc.mmt.lf.RuleMatcher +import info.kwarc.mmt.lf.elpi._ + +class ElpiGenerationServer extends ServerExtension("glif-elpigen") { + override def start(args: List[String]): Unit = { + super.start(args) + } + + private def errorResponse(message: String): ServerResponse = { + ServerResponse.JsonResponse( + JSONArray(JSONString(message)) + ) + } + + def apply(request: ServerRequest): ServerResponse = { + var theoryPath : Option[MPath] = None + var mode : String = "types" + var followMeta : Boolean = false + var followIncludes : Boolean = true + request.body.asJSON match { + case JSONObject(map) => + for (entry <- map) { + entry match { + case (JSONString("theory"), JSONString(value)) => theoryPath = Some(DPath(URI(value)).toMPath) + case (JSONString("mode"), JSONString(value)) => mode = value + case (JSONString("follow-meta"), JSONBoolean(value)) => followMeta = value + case (JSONString("follow-includes"), JSONBoolean(value)) => followIncludes = value + case (key, _) => throw ServerError("Invalid JSON: can't handle entry '" + key.toFormattedString("") + "'") + } + } + case _ => throw ServerError("Invalid JSON: Expected object") + } + + val theory = theoryPath.map(controller.getO(_) match { + case Some(t: Theory) => controller.simplifier.apply(t); t + case None => return errorResponse("Could not find theory " + theoryPath) + case _ => return errorResponse(theoryPath + " does not appear to be a theory") + }) + + if (theory.isEmpty) return errorResponse("No theory specified") + + lazy val lup = controller.globalLookup + lazy val ruleMatcher = new RuleMatcher(lup, List("Judgment", "TabMarker", "TabClosed")) + + val ctx = mode match { + case "types" => new ElpiGenCtx(new ConstantHandlerSequence(List(new TypeHandler)), followMeta, followIncludes) + case "simpleprover" => + new ElpiGenCtx(new ConstantHandlerSequence(List( + new GeneratedFromHandler(controller), + new MainJudgmentHandler(ruleMatcher), + new ProductRuleHandler(ruleMatcher), + new IterativeDeepeningHandler(ruleMatcher), + new ProofTermHandler(ruleMatcher), + new RuleUseHandler(ruleMatcher), + new HandDownHandler(ruleMatcher, ""), + )), followMeta, followIncludes) + case _ => throw ServerError("Unkown mode '" + mode + "'") + } + val p = translateTheory(theory.get, ctx) + ServerResponse.JsonResponse(JSONString(ELPI.Program(p:_*).toELPI)) + } + + private def translateTheory(thy: Theory, ctx:ElpiGenCtx): List[ELPI.Decl] = { + val meta = if (ctx.followMeta && thy.meta.get.parent.toString != "http://cds.omdoc.org/urtheories") { + translateTheory(controller.getO(thy.meta.get).get.asInstanceOf[Theory], ctx) + } else Nil + val decls = thy.getDeclarations flatMap (translateDeclaration(_, ctx)) + meta ::: decls + } + + private def translateDeclaration(d: Declaration, ctx:ElpiGenCtx): List[ELPI.Decl] = { + d match { + case c: Constant => ctx.ch.handle(c) + case PlainInclude(from,_) => + if (!ctx.followInclude) Nil + else if (from.parent.toString == "http://cds.omdoc.org/urtheories") Nil + else translateTheory(controller.getO(from).get.asInstanceOf[Theory], ctx) + case _: RuleConstant => + Nil // ignored + case _: Structure => + Nil // redundant after flattening + case _ => + throw ELPIError("unknown declaration: " + d.path) + } + } +} + +class ElpiGenCtx(val ch : ConstantHandler, val followMeta : Boolean, val followInclude : Boolean) + diff --git a/src/mmt-glf/src/info/kwarc/mmt/glf/GfAST.scala b/src/mmt-glf/src/info/kwarc/mmt/glf/GfAST.scala index 7689de21c4..8b2f8cc2cf 100644 --- a/src/mmt-glf/src/info/kwarc/mmt/glf/GfAST.scala +++ b/src/mmt-glf/src/info/kwarc/mmt/glf/GfAST.scala @@ -1,8 +1,10 @@ package info.kwarc.mmt.glf -import info.kwarc.mmt.api.objects.Term +import info.kwarc.mmt.api.objects.{OMLIT, Term} import info.kwarc.mmt.api.symbols.Constant +import info.kwarc.mmt.api.uom.StandardNat import info.kwarc.mmt.lf.ApplySpine +import info.kwarc.mmt.sequences.NatRules.NatLit import scala.collection.mutable.ListBuffer @@ -30,15 +32,27 @@ case class GfFun(fun : String, args : List[GfAST]) extends GfAST { } } +case class GfInt(value: String) extends GfAST { + override def toString: String = value + override def toOMDocRec(theorymap : Map[String, Constant]): Term = { + OMLIT(StandardNat(value.toInt), NatLit) + } +} + object GfAST { def parseAST(str : String) : GfAST = { var head_end = 0 + var isint = true while (head_end < str.length && str.charAt(head_end) != ' ') { + if (str.charAt(head_end) < '0' || str.charAt(head_end) > '9') isint = false head_end += 1 } val head = str.take(head_end) + if (isint) { + return GfInt(head) + } val args = ListBuffer[GfAST]() diff --git a/src/mmt-glf/src/info/kwarc/mmt/glf/GfImporter.scala b/src/mmt-glf/src/info/kwarc/mmt/glf/GfImporter.scala index 8985183d35..da6a0b9117 100644 --- a/src/mmt-glf/src/info/kwarc/mmt/glf/GfImporter.scala +++ b/src/mmt-glf/src/info/kwarc/mmt/glf/GfImporter.scala @@ -90,8 +90,21 @@ class GfImporter extends Importer { langTheory.add(c) } + var foundint = false + def symbname2OMS(s : String) : OMID = { - constMap.get(s) match { + if (s == "Int") { + if (!foundint) { + // MPath(DPath(URI("http://mathhub.info/COMMA/GLF")), LocalName("GLF_Int")) + // MPath(DPath(URI("http://mathhub.info/MitM/Foundation")), LocalName("IntLiterals")) + controller.add(PlainInclude(MPath(DPath(URI("http://cds.omdoc.org/urtheories")), LocalName("NatLiteralsOnly")), + langTheory.toTerm.toMPath)) + } + foundint = true + // OMS(GlobalName(MPath(DPath(URI("http://mathhub.info/COMMA/GLF")), LocalName("GLF_Int")), LocalName("Int"))) + // OMS(GlobalName(MPath(DPath(URI("http://mathhub.info/MitM/Foundation")), LocalName("IntLiterals")), LocalName("int_lit"))) + OMS(GlobalName(MPath(DPath(URI("http://cds.omdoc.org/urtheories")), LocalName("NatSymbols")), LocalName("NAT"))) + } else constMap.get(s) match { case Some(c : Constant) => OMS(GlobalName(c.home.toMPath, c.name)) case None => throw new Exception("Failed to find symbol " + s) } @@ -109,7 +122,6 @@ class GfImporter extends Importer { FINISHING UP */ index(toplevelDoc) - BuildSuccess(directlyincluded.toList, LogicalDependency(langTheory.toTerm.toMPath)::Nil) } } diff --git a/src/mmt-glf/src/info/kwarc/mmt/glf/GfParser.scala b/src/mmt-glf/src/info/kwarc/mmt/glf/GfParser.scala index 6c828a9e23..1f40e4e936 100644 --- a/src/mmt-glf/src/info/kwarc/mmt/glf/GfParser.scala +++ b/src/mmt-glf/src/info/kwarc/mmt/glf/GfParser.scala @@ -53,11 +53,11 @@ class GfLexer extends RegexParsers { // def other_operator : Parser[OTHER_OPERATOR] = "=" ^^ (str => OTHER_OPERATOR(str)) // keywords - def abstract_ : Parser[ABSTRACT.type] = "abstract" ^^ (_ => ABSTRACT) - def of_ : Parser[OF.type] = "of" ^^ (_ => OF) - def cat_ : Parser[CAT.type] = "cat" ^^ (_ => CAT) - def fun_ : Parser[FUN.type] = "fun" ^^ (_ => FUN) - def other_segment : Parser[OTHER_SEGMENT] = "flags" ^^ {str => OTHER_SEGMENT(str)} + def abstract_ : Parser[ABSTRACT.type] = "abstract[ \t\r\f\n]".r ^^ (_ => ABSTRACT) + def of_ : Parser[OF.type] = "of[ \t\r\f\n]".r ^^ (_ => OF) + def cat_ : Parser[CAT.type] = "cat[ \t\r\f\n]".r ^^ (_ => CAT) + def fun_ : Parser[FUN.type] = "fun[ \t\r\f\n]".r ^^ (_ => FUN) + def other_segment : Parser[OTHER_SEGMENT] = "flags[ \t\r\f\n]".r ^^ {str => OTHER_SEGMENT(str)} def tokens: Parser[List[GfToken]] = { phrase(rep1(abstract_ | of_ | cat_ | fun_ | other_segment | @@ -158,7 +158,12 @@ class GfParser { def parseBody(ctx : GfParserContext): Unit = { expectToken(LEFT_BRACE, ctx) var ignoremode = false + var possiblydone = false // if we are in ignoremode and we've found a '}', we could be done while (true) { + if (possiblydone) { + if (ctx.reachedEOF()) return + possiblydone = false + } ctx.popToken() match { case CAT => parseCat(ctx) @@ -170,6 +175,7 @@ class GfParser { ignoremode = true case RIGHT_BRACE => if (! ignoremode) return + possiblydone = true case token => if (!ignoremode) throw GfUnexpectedTokenException("Expected segment type (like cat or fun), found: '" + token.toString + "'") } diff --git a/src/mmt-glf/src/info/kwarc/mmt/glf/GlfBuildServer.scala b/src/mmt-glf/src/info/kwarc/mmt/glf/GlfBuildServer.scala index b1ac5ba610..c3af226b2b 100644 --- a/src/mmt-glf/src/info/kwarc/mmt/glf/GlfBuildServer.scala +++ b/src/mmt-glf/src/info/kwarc/mmt/glf/GlfBuildServer.scala @@ -14,8 +14,8 @@ class GlfBuildServer extends ServerExtension("glf-build"){ override def start(args: List[String]): Unit = { super.start(args) controller.extman.addExtension(gfImporter) - } + def apply(request: ServerRequest): ServerResponse = { val query : GlfBuildQuery = GlfBuildQuery.fromJSON(request.body.asJSON) diff --git a/src/mmt-glf/src/info/kwarc/mmt/glf/GlfConstructServer.scala b/src/mmt-glf/src/info/kwarc/mmt/glf/GlfConstructServer.scala index 0dca05b3f0..928d3ef1af 100644 --- a/src/mmt-glf/src/info/kwarc/mmt/glf/GlfConstructServer.scala +++ b/src/mmt-glf/src/info/kwarc/mmt/glf/GlfConstructServer.scala @@ -1,10 +1,13 @@ package info.kwarc.mmt.glf import info.kwarc.mmt.api.modules.{Theory, View} +import info.kwarc.mmt.api.objects.{OMBIND, OMLIT, OMS, OMV, Term} import info.kwarc.mmt.api.symbols.Constant import info.kwarc.mmt.api.uom.SimplificationUnit -import info.kwarc.mmt.api.{DPath, MPath} +import info.kwarc.mmt.api.{DPath, LocalName, MPath} import info.kwarc.mmt.api.utils.{JSON, JSONArray, JSONBoolean, JSONObject, JSONString, URI} import info.kwarc.mmt.api.web.{ServerError, ServerExtension, ServerRequest, ServerResponse} +import info.kwarc.mmt.lf.elpi.{BaseConstantHandler, ELPIExporter} +import info.kwarc.mmt.lf.{ApplySpine, Arrow, Lambda, Pi} import scala.collection.mutable import scala.collection.mutable.ArrayBuffer @@ -14,7 +17,7 @@ import scala.collection.mutable.ArrayBuffer This is used to apply the semantics construction (a view) to a GF parse tree (which can be seen as an LF term). */ -class GlfConstructServer extends ServerExtension("glf-construct"){ +class GlfConstructServer extends ServerExtension("glf-construct") { def apply(request: ServerRequest): ServerResponse = { val query: GlfConstructQuery = GlfConstructQuery.fromJSON(request.body.asJSON) @@ -56,42 +59,72 @@ class GlfConstructServer extends ServerExtension("glf-construct"){ case ex: Exception => return errorResponse(ex.getMessage) } - /* val theorymap : Map[String, Constant] = { - controller.simplifier(theory) - val consts = theory.getConstants ::: theory.getIncludes.map(controller.get).collect { - case t : Theory => - controller.simplifier(t) - t.getConstants - }.flatten - HashMap(consts.map(c => (c.name.toString,c)):_*) - } - */ - val trees = query.asts .map(GfAST.parseAST) .map(_.toOMDocRec(theoryMap.toMap)) .map(t => view match { case Some(v) => controller.library.ApplyMorphs(t, v.toTerm) - case None => t }) + case None => t + }) .map(t => if (query.simplify) controller.simplifier(t, - SimplificationUnit(theory.getInnerContext,expandDefinitions=query.deltaExpansion,fullRecursion=true)) else t) + SimplificationUnit(theory.getInnerContext, expandDefinitions = query.deltaExpansion, fullRecursion = true)) else t) + .map(t => removeFakeLambdas(t, Set())) .distinct - ServerResponse.JsonResponse(JSONArray(trees.map(t => JSONString(controller.presenter.asString(t))) : _*)) + if (query.toElpi) { + ServerResponse.JsonResponse(JSONArray(trees.map(t => JSONString(ELPIExporter.translateTerm(t).toELPI())): _*)) + } else { + ServerResponse.JsonResponse(JSONArray(trees.map(t => JSONString(controller.presenter.asString(t))): _*)) + } } - private def errorResponse(message : String) : ServerResponse = { + private def errorResponse(message: String): ServerResponse = { ServerResponse.JsonResponse( JSONArray(JSONString(message)) ) } + + private def removeFakeLambdas(t: Term, bound : Set[String]): Term = { + t match { + case OMS(p) => + if (bound.contains(p.toMPath.toGlobalName.toString)) { + OMV(p.name.toStr(true)) + } + else { + OMS(p) + } + case OMV(n) => + OMV(n) + case OMBIND(b, c, s) => + OMBIND(b, c, removeFakeLambdas(s, bound)) + case ApplySpine(OMS(p), List(tp1, _, OMS(v), b)) => + if (p.toMPath.toGlobalName.toString.equals("http://mathhub.info/COMMA/GLF?fakeLambda?fake_lambda")) { // apparently, it's "[logic]/fake_lambda" + val name = v.name.toStr(true) + Lambda(LocalName(name), tp1, removeFakeLambdas(b, bound + v.toMPath.toGlobalName.toString)) + } else { + t match { + case ApplySpine(f, args) => + ApplySpine(removeFakeLambdas(f, bound), args.map(a => removeFakeLambdas(a, bound)): _*) + } + } + case ApplySpine(f, args) => + ApplySpine(removeFakeLambdas(f, bound), args.map(a => removeFakeLambdas(a, bound)): _*) + case Arrow(_, _) => + throw new GlfException("Didn't expect arrow in result of semantics construction") + case Pi(_, _, _) => + throw new GlfException("Didn't expect pi in result of semantics construction") + case OMLIT(v, rt) => OMLIT(v, rt) + case _ => throw new GlfException("unknown term: " + t) + } + } } class GlfConstructQuery(val asts : List[String], val languageTheory : Option[MPath], val semanticsView : Option[MPath], val simplify : Boolean, - val deltaExpansion : Boolean) + val deltaExpansion : Boolean, + val toElpi : Boolean) object GlfConstructQuery { def fromJSON(json : JSON) : GlfConstructQuery = { @@ -100,6 +133,7 @@ object GlfConstructQuery { var semView : Option[String] = None var simplify = true var deltaExpansion = false + var toElpi = false json match { case JSONObject(map) => @@ -116,6 +150,7 @@ object GlfConstructQuery { case (JSONString("semanticsView"), JSONString(value)) => semView = Some(value) case (JSONString("simplify"), JSONBoolean(value)) => simplify = value case (JSONString("deltaExpansion"), JSONBoolean(value)) => deltaExpansion = value + case (JSONString("toElpi"), JSONBoolean(value)) => toElpi = value case (key, _) => throw ServerError("Invalid JSON: can't handle entry '" + key.toFormattedString("") + "'" ) } } @@ -123,6 +158,6 @@ object GlfConstructQuery { } val semanticsView : Option[MPath] = semView.map(s => DPath(URI(s)).toMPath) val languageTheory = langTheo.map(p => DPath(URI(p)).toMPath) - new GlfConstructQuery(asts.toList, languageTheory, semanticsView, simplify, deltaExpansion) + new GlfConstructQuery(asts.toList, languageTheory, semanticsView, simplify, deltaExpansion, toElpi) } } diff --git a/src/mmt-isabelle/README.md b/src/mmt-isabelle/README.md index c93781674f..92b9d10547 100644 --- a/src/mmt-isabelle/README.md +++ b/src/mmt-isabelle/README.md @@ -3,17 +3,17 @@ Isabelle/MMT ## Requirements -Isabelle/MMT requires a version of Isabelle that fits precisely to it. The -latest stable release is for Isabelle2019 (June 2019), but intermediate -development versions repository -- see also file `README_REPOSITORY` section +Isabelle/MMT requires a version of Isabelle that fits precisely to +it. The latest stable release is for Isabelle2020 (April 2020). For +intermediate development versions see file `README_REPOSITORY` section **Quick start in 30min** in https://isabelle.sketis.net/repos/isabelle The following versions should fit together: - * Isabelle/b1f3e86a4745 from https://isabelle.sketis.net/repos/isabelle - * AFP/429a712d7c4d from https://isabelle.sketis.net/repos/afp-devel - * MMT/32ceb598c5a3 from https://github.com/UniFormal/MMT/commits/devel - * MathHub/MMT/urtheories/0636094dce6f from + * Isabelle/abf3e80bd815 from https://isabelle.sketis.net/repos/isabelle + * AFP/9449ae539c8a from https://isabelle.sketis.net/repos/afp-devel + * MMT/42718dc13dcc from https://github.com/UniFormal/MMT/commits/devel + * MathHub/MMT/urtheories/5c80b26b6cae from https://gl.mathhub.info/MMT/urtheories/commits/devel The corresponding OMDoc content is available here (commit messages refer to the diff --git a/src/mmt-isabelle/ROOT b/src/mmt-isabelle/ROOT index 737cc1d4a7..085469fde0 100644 --- a/src/mmt-isabelle/ROOT +++ b/src/mmt-isabelle/ROOT @@ -1,5 +1,6 @@ chapter MMT -session MMT = Pure + +session MMT = HOL + description "Setup for Isabelle/MMT query command" - theories Query + theories [condition = ISABELLE_MMT_CONDITION] + Query diff --git a/src/mmt-isabelle/src/info/kwarc/mmt/isabelle/Importer.scala b/src/mmt-isabelle/src/info/kwarc/mmt/isabelle/Importer.scala index 2e0a30b5f6..6dc50b45fe 100644 --- a/src/mmt-isabelle/src/info/kwarc/mmt/isabelle/Importer.scala +++ b/src/mmt-isabelle/src/info/kwarc/mmt/isabelle/Importer.scala @@ -321,7 +321,7 @@ object Importer val notation = NotationContainer() def prefix_notation(delim: String, impl: Int): TextNotation = - new TextNotation(Prefix(Delim(isabelle.Symbol.decode(delim)), impl, 0), Precedence.infinite, None) + new TextNotation(Prefix(Delim(isabelle.Symbol.decode(delim)), impl, 0), Precedence.infinite, None, false) def xname_notation: List[TextNotation] = xname.toList.map(prefix_notation(_, 0)) @@ -341,7 +341,7 @@ object Importer } val delim = Delim(isabelle.Symbol.decode(infix.delim)) val fixity = Infix(delim, implicit_args, 2, assoc) - new TextNotation(fixity, Precedence.integer(infix.pri), None) + new TextNotation(fixity, Precedence.integer(infix.pri), None, false) } xname_notation ::: List(infix_notation) } @@ -606,6 +606,18 @@ object Importer catch { case isabelle.ERROR(msg) => isabelle.error(msg + "\nin type " + ty) } } + object OFCLASS // FIXME workaround for Isabelle2020 on case-insensitive file-system + { + import isabelle.Term._ + + def unapply(t: Term): Option[(Typ, String)] = + t match { + case App(Const(Class_Const(c), List(ty)), Const(isabelle.Pure_Thy.TYPE, List(ty1))) + if ty == ty1 => Some((ty, c)) + case _ => None + } + } + def import_term(tm: isabelle.Term.Term, env: Env = Env.empty, bounds: List[String] = Nil): Term = { def typ(t: isabelle.Term.Typ): Term = import_type(t, env) @@ -622,7 +634,7 @@ object Importer catch { case _: IndexOutOfBoundsException => isabelle.error("Loose bound variable " + i) } case isabelle.Term.Abs(x, ty, b) => lf.Lambda(LocalName(x), typ(ty), term(x :: bs, b)) - case isabelle.Term.OFCLASS(ty, c) => + case OFCLASS(ty, c) => lf.Apply(import_class(c), typ(ty)) case isabelle.Term.App(a, b) => lf.Apply(term(bs, a), term(bs, b)) diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/ImperativeProofs.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/ImperativeProofs.scala deleted file mode 100644 index f04ac607b3..0000000000 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/ImperativeProofs.scala +++ /dev/null @@ -1,21 +0,0 @@ -package info.kwarc.mmt.lf - -import info.kwarc.mmt.api._ -import objects._ -import proving.imperative._ - -case class Fix(name: List[LocalName]) extends ProofStep - -object FixRule extends ProofStepRule { - def applicable(step: ProofStep) = step match {case _:Fix => true case _ => false} - - def apply(prover: Prover)(state: ProofState, step: ProofStep) { - val Fix(ns) = step - // TODO split state.goal as Arrow(from,to), Pi(newCond, newGoal), or maybe even Ded(Forall(newCon,newGoal)) - val newCon: Context = ??? - val newGoal = ??? - state.context = state.context ++ newCon - state.goal = newGoal - } -} - diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/LF.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/LF.scala index 10f115f4c1..6fa44d405a 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/LF.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/LF.scala @@ -30,7 +30,7 @@ object LF { def constant(name: String) = OMS(_path ? name) - lazy val hoas = notations.HOAS(Apply.path, Lambda.path, OfType.path) + lazy val hoas = notations.HOAS(Apply.path, Lambda.path) } class LFSym(name: String) { diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/NotationGenerator.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/NotationGenerator.scala index 1b37fe89d2..86589296db 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/NotationGenerator.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/NotationGenerator.scala @@ -37,7 +37,7 @@ class NotationGenerator extends ChangeListener { val parseMarkers = SymbolName() :: Range(0,numImplicitArgs).map {i => ImplicitArg(i+1)}.toList ::: Range(numImplicitArgs,numTotalArgs).map {i => SimpArg(i+1)}.toList - val nt = new TextNotation(Mixfix(parseMarkers), Precedence.integer(0), Some(LF.theoryPath)) + val nt = new TextNotation(Mixfix(parseMarkers), Precedence.integer(0), Some(LF.theoryPath), false) metadata.Generated.set(nt) c.notC.parsingDim.set(nt) } @@ -48,7 +48,7 @@ class NotationGenerator extends ChangeListener { } val presentationMarkers : List[Marker] = tree ::: SymbolName() :: Range(0,numImplicitArgs).map {i => ImplicitArg(i+1)}.toList - val nt = new TextNotation(Mixfix(presentationMarkers), Precedence.integer(0), Some(LF.theoryPath)) + val nt = new TextNotation(Mixfix(presentationMarkers), Precedence.integer(0), Some(LF.theoryPath), false) metadata.Generated.set(nt) c.notC.presentationDim.set(nt) } diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/RuleMatcher.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/RuleMatcher.scala index b806d77640..60f60acfe4 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/RuleMatcher.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/RuleMatcher.scala @@ -41,10 +41,9 @@ case class RuleParameter(name: LocalName, tp: Term) extends RuleArgument case class RuleAssumption(judgment: ComplexJudgement) extends RuleArgument /** - * defines pattern matchers on terms for [[InferenceRule]]s + * defines pattern matchers on LF types for [[DeclarativeRule]]s * @param lup needed to lookup the roles of constants * @param roles the roles that form atomic judgements - * @param dedTag role of truth judgements */ class RuleMatcher(lup: Lookup, roles: List[String]) { private def checkRole(p: GlobalName, allowedRoles: List[String]): Option[String] = { @@ -83,7 +82,7 @@ class RuleMatcher(lup: Lookup, roles: List[String]) { } } - /** matches an InferenceRule */ + /** matches a DeclarativeRule */ object Rule { def unapply(t: Term): Option[DeclarativeRule] = t match { case FunType(args, Atomic(conc)) => diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/Rules.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/Rules.scala index 4dadf7aab6..8bdc14447b 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/Rules.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/Rules.scala @@ -469,9 +469,9 @@ class Injectivity(val head: GlobalName) extends TermBasedEqualityRule { } /** |- t:A ---> |- (t:A) : A */ -object TypeAttributionTerm extends InferenceRule(TypeAttribution.path, OfType.path) { +object TypeAttributionTerm extends InferenceRule(OfType.path, OfType.path) { def apply(solver: Solver)(tm: Term, covered: Boolean)(implicit stack: Stack, history: History) : Option[Term] = { - val TypeAttribution(t,a) = tm + val OfType(t,a) = tm if (!covered) { solver.inferTypeAndThen(a)(stack, history + "checking the attributed type") {aI => // nothing to do, we just have to make sure that the type is well-formed before checking the term against it @@ -484,9 +484,9 @@ object TypeAttributionTerm extends InferenceRule(TypeAttribution.path, OfType.pa } /** |- (t:A) = t */ -object DropTypeAttribution extends ComputationRule(TypeAttribution.path) { +object DropTypeAttribution extends ComputationRule(OfType.path) { def apply(solver: CheckingCallback)(tm: Term, covered: Boolean)(implicit stack: Stack, history: History) = { - val TypeAttribution(t,a) = tm + val OfType(t,a) = tm if (!covered) { // this will call TypeAttributionTerm once, which recursively triggers the necessary checks of the type before we throw it away solver.inferType(tm, covered)(stack, history + "checking the attributed type") diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/ScalaExporter.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/ScalaExporter.scala index 3b4f355184..5ca9039d3b 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/ScalaExporter.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/ScalaExporter.scala @@ -13,12 +13,14 @@ import GenericScalaExporter._ /** for LF terms using Apply and Lambda */ class LFOperator(p: ContentPath, vars: ArgumentList, args: ArgumentList) extends Operator(vars,args) { private def omid = "OMID(this.path)" - def mkL(as: List[String]): String = as.mkString("List(", ",", ")") - def mmtTerm(a1: List[String], a2: List[String]): String = { - if (a1.isEmpty) - s"ApplyGeneral($omid, ${mkL(a2)})" + def makeTerm(pattern: Boolean) = { + val argsS = if (pattern) args.namesConsed else args.nameList + if (vars.args.isEmpty && args.args.isEmpty) + omid + else if (vars.args.isEmpty) + s"ApplyGeneral($omid, $argsS)" else - s"Apply($omid, Lambda(Context(${mkL(a1)}), a2.head))" + s"Apply($omid, Lambda(Context(${vars.nameList}), ${args.args.head.name}))" } } @@ -54,7 +56,7 @@ class ScalaExporter extends GenericScalaExporter { } override def outputTrait(t: Theory) { - return + return // switched off for now - causes more trouble than it's worth val includes = t.getIncludesWithoutMeta.filter {i => controller.globalLookup.getO(i) match { case Some(r: RealizationInScala) => diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/Strings.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/Strings.scala new file mode 100644 index 0000000000..7eec9b348d --- /dev/null +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/Strings.scala @@ -0,0 +1,25 @@ +package info.kwarc.mmt.lf + +import info.kwarc.mmt.api.{DPath, LocalName} +import info.kwarc.mmt.api.objects.{OMLIT, Term} +import info.kwarc.mmt.api.uom.{RealizedType, StandardString, TheoryScala} +import info.kwarc.mmt.api.utils.URI + +object Strings extends TheoryScala { + override val _base: DPath = DPath(URI("http://cds.omdoc.org/urtheories")) + override val _name: LocalName = LocalName("Strings") + + object string extends NullaryLFConstantScala(this._path, "string") + + def apply(str: String): Term = OMLIT(str, RealizedType(string.term, StandardString)) + + /** + * Unapply method for MMT/urtheories string literals as [[Term]]s. + * + * @todo Ask Florian where this should go. Ideally either in mmt-lf or into the archive MMT/urtheories + */ + def unapply(term: Term): Option[String] = term match { + case OMLIT(str: String, RealizedType(string.term, StandardString)) => Some(str) + case _ => None + } +} \ No newline at end of file diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/TwelfParser.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/TwelfParser.scala index 65c48b6dc0..0b1fa71a0d 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/TwelfParser.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/TwelfParser.scala @@ -1295,7 +1295,7 @@ class TwelfParser extends Parser(new NotationBasedParser) { // It's a DefinedView val (morphism, positionAfter) = crawlTerm(i, Nil, Nil, vpath $ DefComponent, Context()) i = positionAfter - view = View(vpath.parent, vpath.name, domain, codomain, Some(morphism), isImplicit) + view = View(vpath.parent, vpath.name, domain, codomain, TermContainer.asParsed(morphism), isImplicit) add(view) } diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/Typed.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/Typed.scala index 1bb1a93fb8..c6adfea30c 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/Typed.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/Typed.scala @@ -11,16 +11,7 @@ object Typed { val kind = _base ? "Kinded" ? "kind" } -object TypeAttribution extends BinaryConstantScala(Typed.path, "typeAttribution") - -object OfType { - val path = Typed.path ? "oftype" - def apply(t : Term) = OMA(OMID(path),List(t)) - def unapply(t : Term) : Option[Term] = t match { - case OMA(OMID(this.path), List(a)) => Some(a) - case _ => None - } -} +object OfType extends BinaryConstantScala(Typed.path, "oftype") /** provides apply/unapply methods for the LF equality symbol */ object LFEquality { diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/elpi/Exporter.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/elpi/Exporter.scala index 73f12062bf..e3c6ddb555 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/elpi/Exporter.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/elpi/Exporter.scala @@ -2,82 +2,86 @@ package info.kwarc.mmt.lf.elpi import info.kwarc.mmt.api._ import archives._ -import libraries._ import symbols._ import modules._ import objects._ import documents._ - +import info.kwarc.mmt.api.frontend.Controller import info.kwarc.mmt.lf._ - -case class ELPIError(msg: String) extends Error(msg) +import info.kwarc.mmt.sequences.NatRules.NatLit +import ELPIExporter.translateTerm object HelpCons { def apply(path: GlobalName) = ELPI.Variable(LocalName("help") / path.name) def apply(path: GlobalName, suffix: String) = ELPI.Variable(LocalName("help") / path.name / suffix) } -object PairCons extends ELPI.Constant("pair") +case class ELPIError(msg: String) extends Error(msg) + +object ELPIExporter { + /** straightforward translation of an LF terms to a lambda-Prolog term */ + def translateTerm(t: Term): ELPI.Expr = { + t match { + case OMS(p) => + if (p.toString == "http://cds.omdoc.org/urtheories?NatSymbols?NAT") + ELPI.Variable(LocalName("int")) + else + ELPI.Variable(p.name) + case OMV(n) => + ELPI.Variable(n) + case Lambda(x, _, t) => + ELPI.Lambda(x, translateTerm(t)) + case ApplySpine(f, args) => + val fE = translateTerm(f) + val argsE = args map translateTerm + fE(argsE: _*) + case Arrow(a, b) => + val aE = translateTerm(a) + val bE = translateTerm(b) + ELPI.Arrow(aE, bE) + case Pi(x, _, b) => + ELPI.Forall(x, translateTerm(b)) + case OMLIT(v, NatLit) => + ELPI.Integer(v.toString.toInt) + case _ => throw ELPIError("unknown term: " + t) + } + } +} class ELPIExporter extends Exporter { val key = "lf-elpi" override def outExt = "elpi" - + private lazy val lup = controller.globalLookup - private lazy val ruleMatcher = new RuleMatcher(lup, List("Judgment","Judgement")) - + private lazy val ruleMatcher = new RuleMatcher(lup, List("Judgment", "TabMarker", "TabClosed")) + + private lazy val constantHandler = new ConstantHandlerSequence(List( + new GeneratedFromHandler(controller), + // new TypeHandler(), // not good enough (what do we do with dependent types?) + new MainJudgmentHandler(ruleMatcher), + new ProductRuleHandler(ruleMatcher), + new IterativeDeepeningHandler(ruleMatcher), + new ProofTermHandler(ruleMatcher), + new RuleUseHandler(ruleMatcher), + new HandDownHandler(ruleMatcher, ""), + new HandDownHandler(ruleMatcher, "2"), + new BackChainingHandler(ruleMatcher) + )) + + private def translateTheory(thy: Theory): ELPI.Program = { val cons = thy.getDeclarations val consE = cons flatMap translateDeclaration ELPI.Program(consE:_*) } - + private def translateDeclaration(d: Declaration): List[ELPI.Decl] = { - def fail(msg: String) = { - println(msg) - List(ELPI.Comment("skipping due to error: " + d.path + "\n" + msg)) - } d match { - case c: Constant => - val comment = ELPI.Comment(s"generated from ${c.name} : ${controller.presenter.asString(c.tp.get)}") - if (c.rl contains "Judgment") { - c.tp match { - case Some(FunType(args,_)) => - implicit val varCounter = new VarCounter - val argNames = (1 to args.length).toList.map(_ => ELPI.Variable(varCounter.next(true))) - val certName = ELPI.Variable(varCounter.next(true)) - val hypName = ELPI.Variable(varCounter.next(true)) - val right = ELPI.Variable(c.name)(certName :: argNames :_*) - val left1 = ELPI.Variable(c.name / hypSuffix)(hypName :: argNames :_*) - val left2 = HelpCons(c.path)(argNames ::: List(hypName, certName) :_*) - val ruleE = ELPI.Impl(List(left1,left2),right) - val rule = ELPI.Rule(ruleE) - List(comment, rule) - } - } else { - c.tp match { - case Some(t) => - t match { - case ruleMatcher.Rule(dr) => - try { - val mainRule = translateRule(c, dr)(new VarCounter) - val pr = productRule(c, dr)(new VarCounter) // boilerplate for taking the cartesian product of two helper judgment definitions - List(comment, mainRule, pr) - } catch {case ELPIError(msg) => - fail(msg) - } - case _ => - val msg = "not a rule: " + controller.presenter.asString(t) - fail(msg) - } - case None => - throw ELPIError("cannot translate untyped constant: " + c.path) - } - } + case c: Constant => constantHandler.handle(c) case PlainInclude(from,_) => // we generate one elpi file per MMT theory; MMT includes become lambda-Prolog file includes getOutFileForModule(from) match { - case Some(f) => List(ELPI.Accumulate(f)) + case Some(f) => List(ELPI.Accumulate(f.stripExtension)) case None => throw ELPIError("no ELPI file known for theory " + from) } case _: RuleConstant => @@ -88,36 +92,122 @@ class ELPIExporter extends Exporter { throw ELPIError("unknown declaration: " + d.path) } } - - /** translates an LF rule into the corresponding lambda-Prolog rule with an additional helper predicate as a side condition - * the helper predicate takes all inputs and the output of the rule as arguments and can be used to - * - store the proot term (if query variable), supply a proof term to check (if argument given) - * - guide the proof search by controlling when a rule is applicable - * - control the proof search, e.g., by providing the search depth - */ - private def translateRule(c: Constant, dr: DeclarativeRule)(implicit vc: VarCounter) : ELPI.Rule = { - // for parameters: just the given name, ignoring the type; for assumptions: a generated name and the judgment - val (argNames,assOs) = dr.arguments.map { - case RuleParameter(n,_) => (n,None) // TODO make sure all parNames start with upper case letter (because printer drops outermost pi's) - case RuleAssumption(cj) => - val (n, e) = translateComplex(cj) - (n, Some(e)) - }.unzip - val assEs = assOs.flatMap(_.toList) - // the conclusion and a generated name for it - val (concName, concE) = translateAtomic(dr.conclusion, Nil, false) - val names = argNames ::: List(concName) - // helper judgment: c/help applied to all names; providing rules for this judgment allows guiding the prover - val help = HelpCons(c.path)(names) - // quantify over all names, assumptions imply conclusion, with helper judgment as side condition - val r = ELPI.Forall(names, ELPI.Impl(help::assEs, concE)) - ELPI.Rule(r) + + def exportTheory(thy: Theory, bf: BuildTask) { + val thyE = translateTheory(thy) + rh << thyE.toELPI + } + + def exportView(view: View, bf: BuildTask) {} + def exportDocument(doc: Document, bf: BuildTask) {} + def exportNamespace(dpath: DPath, bd: BuildTask, namespaces: List[BuildTask], modules: List[BuildTask]) {} +} + + +private class VarCounter { + private var i = 0 + def next(upper: Boolean) = { + i += 1 + val base = if (upper) "X" else "x" + LocalName(base + i.toString) + } +} + +trait ConstantHandler { + /** called on every constant. */ + def handle(c : Constant) : List[ELPI.Decl] + /** may be used to generate boilerplate code before processing. + * depending on the exporter, this may not get called. */ + def setup() : List[ELPI.Decl] = List() + /** may be used to generate boilerplate code after processing. + * depending on the exporter, this may not get called. */ + def finish() : List[ELPI.Decl] = List() +} + +class ConstantHandlerSequence(handlers : List[ConstantHandler]) extends ConstantHandler { + def handle(c : Constant) : List[ELPI.Decl] = handlers.flatMap(_.handle(c)) + override def setup() : List[ELPI.Decl] = handlers.flatMap(_.setup()) + override def finish() : List[ELPI.Decl] = handlers.flatMap(_.finish()) +} + +class GeneratedFromHandler(controller : Controller) extends ConstantHandler { + def handle(c : Constant) : List[ELPI.Decl] = + List(ELPI.Comment(s"generated from ${c.name} : ${controller.presenter.asString(c.tp.get)}")) +} + +abstract class BaseConstantHandler(handlerName : String) extends ConstantHandler { + def fail(c: Constant, msg: String): ELPI.Decl = { + ELPI.Comment(c.path + ": " + handlerName + ": skipping due to error: " + msg) + } +} + + +class IfElseHandler(a : ConstantHandler, b : ConstantHandler, useA : Constant => Boolean) extends ConstantHandler() { + override def setup(): List[ELPI.Decl] = a.setup() ++ b.setup() + override def finish(): List[ELPI.Decl] = a.setup() ++ b.setup() + def handle(c : Constant) : List[ELPI.Decl] = + if (useA(c)) a.handle(c) else b.handle(c) +} + +class TypeHandler() extends BaseConstantHandler("th") { + def handle(c : Constant) : List[ELPI.Decl] = { + c.tp match { + case Some(t) => { + val tE = translateTerm(t) + val isKind = t match { + case FunType(_, OMS(Typed.ktype)) => true + case _ => false + } + val decl = ELPI.Data(c.name, tE, isKind) + List(decl) // TODO check how to write dependent types in ELPI + } + case _ => List() + } + } +} + +abstract class JudgmentHandler(handlerName : String, ruleMatcher : RuleMatcher) extends BaseConstantHandler(handlerName) { + /** called when a new judgment is introduced. + * e.g. in "ded : type -> prop | role Judgment ||" */ + def onIntro(c : Constant, vc : VarCounter) : List[ELPI.Decl] = List() + + /** called on rules. + * e.g. on "andI : {A,B} ded A -> ded B -> ded (and A B)" */ + def onRule(c : Constant, dr : DeclarativeRule, vc : VarCounter) : List[ELPI.Decl] + + def handle(c: Constant) = { + val vc = new VarCounter + if (c.rl contains "Judgment") { + onIntro(c, vc) + } else { + c.tp match { + case Some(t) => + t match { + case ruleMatcher.Rule(dr) => onRule(c, dr, vc) + case _ => List() + } + case _ => List() + } + } + } + + /* HELPER METHODS */ + def V(n: LocalName) = ELPI.Variable(n) + val hypSuffix = "hyp" + + def getArgVars(c : Constant, vc : VarCounter) : List[ELPI.Variable] = { + c.tp match { + case Some(FunType(args, _)) => + (1 to args.length).toList.map(_ => ELPI.Variable(vc.next(true))) + } } - /** translates a complex judgment to the corresponding lambda-Prolog predicate */ - private def translateComplex(cj: ComplexJudgement)(implicit vc: VarCounter) : (LocalName, ELPI.Expr) = { - // for parameters: get the name, ignoring the type; for assumptions: translate the judgment and generate a name - val parNames = cj.parameters.map {vd => vd.name} + /** translates a complex judgment to the corresponding lambda-Prolog predicate + * Example: translateComplex(ded A -> ded B) should become pi x1 \ ded/hyp x1 A => ded (X2 x1) B + */ + def translateComplex(cj: ComplexJudgement)(implicit vc: VarCounter) : (LocalName, ELPI.Expr) = { + // for parameters: get the name, ignoring the type; for assumptions: translate the judgment and generate a name + val parNames = cj.parameters.map {vd => vd.name} val (hypNames, hypEs) = cj.hypotheses.map {a => translateAtomic(a, Nil, true)}.unzip val names = parNames ::: hypNames // translate the conclusion, return the generated name as the name for the entire complex judgment @@ -128,99 +218,323 @@ class ELPIExporter extends Exporter { } /** translates an atomic judgment to the corresponding lambda-Prolog predicate - * @param aj the judgment - * @param hypothetical this is a hypothesis of a complex judgment - * @param hypNames if conclusion of hypothetical judgment: the names of the hypotheses - */ - private def translateAtomic(aj: AtomicJudgement, hypNames: List[LocalName], hypothesis: Boolean)(implicit vc: VarCounter) : (LocalName, ELPI.Expr) = { + * @param aj the judgment + * @param hypNames if conclusion of hypothetical judgment: the names of the hypotheses + * @param hypothesis this is a hypothesis of a complex judgment + * + * Example: translateAtomic(ded A, [x], false) should become (X1, "ded (X1 x) A") + * Example: translateAtomic(ded A, [], true) should become (x1, "ded/hyp x1 A") + */ + def translateAtomic(aj: AtomicJudgement, hypNames: List[LocalName], hypothesis: Boolean)(implicit vc: VarCounter) : (LocalName, ELPI.Expr) = { val name = vc.next(!hypothesis) val argsE = aj.arguments map translateTerm val nameExpr = V(name)(hypNames) val opName = aj.operator.name // for theses/conclusions: judgment symbol name applied to hypothesis names // technical modification for a hypothetis: apply the -hyp predicate of the judgment symbol instead - // rules for -hyp predicate are generated when the judgment symbol is exported - val opNameH = if (hypothesis) opName / hypSuffix else opName + // rules for -hyp predicate are generated when the judgment symbol is exported + val opNameH = if (hypothesis) opName / hypSuffix else opName val e = V(opNameH)(nameExpr :: argsE :_*) (name, e) } - - /** boilerplate rule for taking the Cartesian product of two helper predicates for a rule */ - private def productRule(c: Constant, dr: DeclarativeRule)(implicit vc: VarCounter) : ELPI.Rule = { - // for parameters: just the name; for assumptions: the lambda-Prolog judgment and two generated names + + /* Like translateAtomic without hypotheses etc., but replaces Variable applications. + * example: For forall-elimination we want to have + * `ded X (forall P) :- ..., P = F T, ...` instead of `ded X (forall (F T)) :- ... + */ + def translateConclusion(aj : AtomicJudgement)(implicit vc: VarCounter) : (List[LocalName], ELPI.Expr, List[ELPI.Expr]) = { + val cert = vc.next(true) + var names : List[LocalName] = List() + val (argsE, extra) : (List[ELPI.Expr], List[List[ELPI.Expr]]) = aj.arguments.map { + case ApplySpine(OMV(f), a) => { + val v = vc.next(true) + names = v :: names + (V(v), List(ELPI.Equal(V(v), translateTerm(ApplySpine(OMV(f), a :_*))))) + } + case x => (translateTerm(x), List()) + }.unzip + + val e = V(aj.operator.name)(V(cert) :: argsE :_*) + (cert :: names, e, extra.flatten) + } + + def getParNames(dr: DeclarativeRule)(implicit vc: VarCounter): (List[LocalName], List[ELPI.Expr]) = { val parNames = dr.arguments.collect { case RuleParameter(n,_) => n } + // have to add extra ones (for case covered by translateConclusion) + val (names, _, extras) = translateConclusion(dr.conclusion) + (names.drop(1) ::: parNames, extras) + } + +} + +class MainJudgmentHandler(ruleMatcher : RuleMatcher) extends JudgmentHandler("mj", ruleMatcher) { + override def onIntro(c : Constant, vc : VarCounter) : List[ELPI.Decl] = { + val argNames = getArgVars(c, vc) + val certName = vc.next(true) + val cert = ELPI.Variable(certName) + val hypName = ELPI.Variable(vc.next(true)) + val right = ELPI.Variable(c.name)(cert :: argNames :_*) + val left1 = ELPI.Variable(c.name / hypSuffix)(hypName :: argNames :_*) + val left2 = HelpCons(c.path)(argNames ::: List(hypName, cert) :_*) + val ruleE = ELPI.Impl(List(left1,left2),right) + List(ELPI.Rule(ruleE)) + } + + /** translates an LF rule into the corresponding lambda-Prolog rule with an additional helper predicate as a side condition + * the helper predicate takes all inputs and the output of the rule as arguments and can be used to + * - store the proot term (if query variable), supply a proof term to check (if argument given) + * - guide the proof search by controlling when a rule is applicable + * - control the proof search, e.g., by providing the search depth + */ + def onRule(c: Constant, dr: DeclarativeRule, vc: VarCounter) : List[ELPI.Decl] = { + // for parameters: just the given name, ignoring the type; for assumptions: a generated name and the judgment + val (argNames,assOs) = dr.arguments.map { + case RuleParameter(n,_) => (n,None) // TODO make sure all parNames start with upper case letter (because printer drops outermost pi's) + case RuleAssumption(cj) => + val (n, e) = translateComplex(cj)(vc) + (n, Some(e)) + }.unzip + val assEs = assOs.flatMap(_.toList) + // the conclusion and a generated name for it + val (concNames, concE, concExtra) = translateConclusion(dr.conclusion)(vc) + val names = concNames ::: argNames + // helper judgment: c/help applied to all names; providing rules for this judgment allows guiding the prover + val help = HelpCons(c.path)(names) + // quantify over all names, assumptions imply conclusion, with helper judgment as side condition + val r = ELPI.Forall(names, ELPI.Impl(help::(concExtra:::assEs), concE)) + List(ELPI.Rule(r)) + } +} + + +class ProductRuleHandler(ruleMatcher : RuleMatcher) extends JudgmentHandler("prod", ruleMatcher) { + private object ProdCertCons extends ELPI.Constant("prodcert") + override def onIntro(c : Constant, vc : VarCounter) : List[ELPI.Decl] = { + val argNames = getArgVars(c, vc) + val certName = vc.next(true) + val hypName = ELPI.Variable(vc.next(true)) + val cert1 = ELPI.Variable(certName / "1") + val cert2 = ELPI.Variable(certName / "2") + val rightProd = HelpCons(c.path)(argNames ::: List(hypName, ProdCertCons(cert1, cert2)): _*) + val leftProd1 = HelpCons(c.path)(argNames ::: List(hypName, cert1): _*) + val leftProd2 = HelpCons(c.path)(argNames ::: List(hypName, cert2): _*) + List(ELPI.Rule(ELPI.Impl(List(leftProd1, leftProd2), rightProd))) + } + + override def onRule(c: Constant, dr: DeclarativeRule, vc: VarCounter): List[ELPI.Decl] = { + // for parameters: just the name; for assumptions: the lambda-Prolog judgment and two generated names + val (parNames, _) = getParNames(dr)(vc) + val (assNamePairs, assExprs) = dr.arguments.collect { case RuleAssumption(cj) => - val (namePair, e) = productRuleConc(cj) + val (namePair, e) = productRuleConc(cj, vc) (namePair, e) }.unzip + val (assNames1,assNames2) = assNamePairs.unzip val certName = vc.next(true) // e1: first helper predicate applies to a list of inputs with output certName1 val certName1 = certName / "1" - val e1 = HelpCons(c.path, "1")(parNames ::: assNames1 ::: List(certName1)) + // val e1 = HelpCons(c.path, "1")(parNames ::: assNames1 ::: List(certName1)) + val e1 = HelpCons(c.path)(certName1 :: parNames ::: assNames1) // e2: second helper predicate applies to another list of inputs with output certName2 val certName2 = certName / "2" - val e2 = HelpCons(c.path, "2")(parNames ::: assNames2 ::: List(certName2)) - // e: product helper predicate applies to the pair of certName1 and certName2 - val e = HelpCons(c.path)(parNames.map(V) ::: assExprs ::: List(PairCons(List(certName1,certName2))) :_*) + // val e2 = HelpCons(c.path, "2")(parNames ::: assNames2 ::: List(certName2)) + val e2 = HelpCons(c.path)(certName2 :: parNames ::: assNames2) + // e: product helper predicate applies to the pair of certName1 and certName2 + val e = HelpCons(c.path)(ProdCertCons(List(certName1,certName2)) :: parNames.map(V) ::: assExprs :_*) val r = ELPI.Forall(parNames ::: assNames1 ::: assNames2 ::: List(certName1,certName2), ELPI.Impl(List(e1,e2),e)) - ELPI.Rule(r) + List(ELPI.Rule(r)) } /** auxiliary function of productRule: translates the conclusions and generates names for them */ - private def productRuleConc(cj: ComplexJudgement)(implicit vc: VarCounter) : ((LocalName,LocalName), ELPI.Expr) = { + private def productRuleConc(cj: ComplexJudgement, vc: VarCounter) : ((LocalName,LocalName), ELPI.Expr) = { val parNames = cj.parameters.map {vd => vd.name} val hypNames = cj.hypotheses.map {a => vc.next(false)} val certName = vc.next(true) val certName1 = certName / "1" val certName2 = certName / "2" - val names = parNames:::hypNames - val res = ELPI.Lambda(names,PairCons(V(certName1)(names),V(certName2)(names))) + val names = parNames:::hypNames + val res = ELPI.Lambda(names,ProdCertCons(V(certName1)(names),V(certName2)(names))) ((certName1,certName2), res) - } - - private class VarCounter { - private var i = 0 - def next(upper: Boolean) = { - i += 1 - val base = if (upper) "X" else "x" - LocalName(base + i.toString) + } +} + +class IterativeDeepeningHandler(ruleMatcher : RuleMatcher) extends JudgmentHandler("id", ruleMatcher) { + object IdCertCons extends ELPI.Constant("idcert") + override def onIntro(c : Constant, vc : VarCounter) : List[ELPI.Decl] = { + val argNames = getArgVars(c, vc) + val hypName = ELPI.Variable(vc.next(true)) + val rightID = HelpCons(c.path)(argNames:::List(hypName,IdCertCons(V(vc.next(true)))) :_*) + List(ELPI.Rule(ELPI.Impl(List(), rightID))) + } + + override def onRule(c: Constant, dr: DeclarativeRule, vc: VarCounter): List[ELPI.Decl] = { + val (parNames, _) = getParNames(dr)(vc) + + val assCertName = vc.next(true) + val (assNames, assExprs) = dr.arguments.collect { + case RuleAssumption(cj) => + val parNames = cj.parameters.map { vd => vd.name } + val hypNames = cj.hypotheses.map { a => vc.next(false) } + val names = parNames ::: hypNames + // val res = ELPI.Lambda(names, IdCertCons(V(assCertName)(names))) + val res = ELPI.Lambda(names, IdCertCons(V(assCertName))) + (assCertName, res) + }.unzip + val certName = vc.next(true) + val res = HelpCons(c.path)(IdCertCons(List(certName)) :: parNames.map(V) ::: assExprs :_*) + val cond1 = ELPI.GreaterThan(ELPI.Variable(certName), ELPI.Integer(0)) + val cond2 = ELPI.Is(ELPI.Variable(assCertName), ELPI.Minus(ELPI.Variable(certName), ELPI.Integer(1))) + val r = ELPI.Forall(parNames ::: assNames ::: List(certName), ELPI.Impl(List(cond1, cond2),res)) + List(ELPI.Rule(r)) + } +} + +class BackChainingHandler(ruleMatcher : RuleMatcher) extends JudgmentHandler("bc", ruleMatcher) { + + object BcCertCons extends ELPI.Constant("bccert") + + override def onIntro(c: Constant, vc: VarCounter): List[ELPI.Decl] = { + val argNames = getArgVars(c, vc) + val hypName = ELPI.Variable(vc.next(true)) + val cert = ELPI.Variable(vc.next(true)) + val rightBC = HelpCons(c.path)(argNames:::List(hypName,BcCertCons(cert)) :_*) + List(ELPI.Rule(ELPI.Impl(List(), rightBC))) + } + + override def onRule(c: Constant, dr: DeclarativeRule, vc: VarCounter): List[ELPI.Decl] = { + + val isForward = c.rl.contains("ForwardRule") + val (parNames, extras) = getParNames(dr)(vc) + + // val conclVars = getVars(dr.conclusion.arguments.head) + val needBC = isForward || c.rl.contains("EliminationRule") // parNames.exists(n => !conclVars.contains(n)) + + val assCertName = vc.next(true) + var isFirstArg = true + var firstExpr : Option[ELPI.Expr] = None + val (assNames, assExprs) = dr.arguments.collect { + case RuleAssumption(cj) => + val parNames = cj.parameters.map { vd => vd.name } + val hypNames = cj.hypotheses.map { a => vc.next(false) } + val names = parNames ::: hypNames + val res = if (isFirstArg && needBC) { + firstExpr = Some(translateTerm(cj.thesis.arguments.head)) + ELPI.Lambda(names, BcCertCons(V(LocalName("bc/fwdLocked"))(V(assCertName)))) + } else { + ELPI.Lambda(names, BcCertCons(V(assCertName))) + } + isFirstArg = false + (assCertName, res) + }.unzip + val certName = vc.next(true) + val res = HelpCons(c.path)(BcCertCons(List(certName)) :: parNames.map(V) ::: assExprs :_*) + val certval = ELPI.Variable(vc.next(true)) + val conds = if (needBC) { + val cond1 = V(LocalName("bc/val"))(V(certName), certval) // TODO: bc/pos suffices if not ForwardRule + val cond2 = ELPI.GreaterThan(certval, ELPI.Integer(0)) + val cond3 = ELPI.Is(ELPI.Variable(assCertName), ELPI.Minus(certval, ELPI.Integer(1))) + val cond4 = V(LocalName("bc/fwdable"))(firstExpr.get) + List(cond1, cond2, cond3, cond4) + } else { + // val cond1 = ELPI.GreaterThan(V(certName), ELPI.Integer(0)) + val cond1 = V(LocalName("bc/pos"))(V(certName)) + val cond2 = ELPI.Is(ELPI.Variable(assCertName), ELPI.Minus(V(certName), ELPI.Integer(1))) + List(cond1, cond2) + } + val r = ELPI.Forall(parNames ::: assNames ::: List(certName), ELPI.Impl(conds ::: extras,res)) + if (isForward) { + val auxres = ELPI.Variable(vc.next(true)) + val concE = translateTerm(dr.conclusion.arguments.head) + val aux = ELPI.Forall(parNames, ELPI.Impl(V(LocalName("bc/aux"))(concE, auxres), + V(LocalName("bc/aux"))(firstExpr.get, auxres))) + List(ELPI.Rule(r), ELPI.Rule(aux)) + } else { + List(ELPI.Rule(r)) } } - - - private val hypSuffix = "hyp" - - private def V(n: LocalName) = ELPI.Variable(n) - - /** straightforward translation of an LF terms to a lambda-Prolog term */ - private def translateTerm(t: Term): ELPI.Expr = { - t match { - case OMS(p) => - ELPI.Variable(p.name) - case OMV(n) => - ELPI.Variable(n) - case Lambda(x,_,t) => - ELPI.Lambda(x, translateTerm(t)) - case Pi(x,_,b) => - ELPI.Forall(x, translateTerm(b)) - case ApplySpine(f,args) => - val fE = translateTerm(f) - val argsE = args map translateTerm - fE(argsE :_*) - case _ => throw ELPIError("unknown term: " + t) +} + + +class ProofTermHandler(ruleMatcher : RuleMatcher) extends JudgmentHandler("pt", ruleMatcher) { + object PtCertCons extends ELPI.Constant("ptcert") + + override def onIntro(c : Constant, vc : VarCounter) : List[ELPI.Decl] = { + val argNames = getArgVars(c, vc) + val hypName = ELPI.Variable(vc.next(true)) + val rightPT = HelpCons(c.path)(argNames:::List(hypName,PtCertCons(V(LocalName("i"))(argNames :_*))) :_*) + List(ELPI.Rule(ELPI.Impl(List(), rightPT))) + } + + override def onRule(c: Constant, dr: DeclarativeRule, vc: VarCounter): List[ELPI.Decl] = { + val (parNames, _) = getParNames(dr)(vc) + + val (assNames, assExprs) = dr.arguments.collect { + case RuleAssumption(cj) => + val parNames = cj.parameters.map { vd => vd.name } + val hypNames = cj.hypotheses.map { a => vc.next(false) } + val names = parNames ::: hypNames + val assCertName = vc.next(true) + val res = ELPI.Lambda(names, PtCertCons(V(assCertName))) + (assCertName, res) + }.unzip + val newCert = PtCertCons(V(c.path.name)(parNames.map(V) ::: assNames.map(V): _*)) + val res = HelpCons(c.path)(newCert :: parNames.map(V) ::: assExprs: _*) + val r = ELPI.Forall(parNames ::: assNames, ELPI.Impl(List(), res)) + List(ELPI.Rule(r)) + } +} + +/** + * Counts how often a rule has been applied + */ +class RuleUseHandler(ruleMatcher: RuleMatcher) extends JudgmentHandler("u", ruleMatcher) { + override def onRule(c: Constant, dr: DeclarativeRule, vc: VarCounter): List[ELPI.Decl] = { + val (parNames, _) = getParNames(dr)(vc) + val certName = vc.next(true) + var uExpr : Option[ELPI.Expr] = None + val assExprs = dr.arguments.collect { + case RuleAssumption(cj) => + val parNames = cj.parameters.map { vd => vd.name } + val hypNames = cj.hypotheses.map { a => vc.next(false) } + val names = parNames ::: hypNames + if (uExpr.isEmpty) { + uExpr = Some(translateComplex(cj)(vc)._2) + } + val ucert = V(LocalName("ucert"))(V(LocalName("prep"))(uExpr.get, V(certName))) + ELPI.Lambda(names, ucert) } - } - - def exportTheory(thy: Theory, bf: BuildTask) { - val thyE = translateTheory(thy) - rh << thyE.toELPI + if (uExpr.isEmpty) { + // return ELPI.Comment("Can't create helper for ucert: no hypotheses found") + return List(fail(c, "can't create helper: no hypotheses found")) + } + val res = HelpCons(c.path)(V(LocalName("ucert"))(V(certName)) :: parNames.map(V) ::: assExprs :_*) + val cond = V(LocalName("occatmost"))(uExpr.get, ELPI.Integer(if (c.rl.contains("ApplyRepeatedly")) 4 else 1), V(certName)) + val r = ELPI.Forall(parNames, ELPI.Impl(List(uExpr.get, cond),res)) + List(ELPI.Rule(r)) } +} - def exportView(view: View, bf: BuildTask) {} - def exportDocument(doc: Document, bf: BuildTask) {} - def exportNamespace(dpath: DPath, bd: BuildTask, namespaces: List[BuildTask], modules: List[BuildTask]) {} -} \ No newline at end of file +/** + * simply hands something down. Can e.g. be used to return errors. + */ +class HandDownHandler(ruleMatcher: RuleMatcher, name : String) extends JudgmentHandler("hd" + name, ruleMatcher) { + override def onRule(c: Constant, dr: DeclarativeRule, vc: VarCounter): List[ELPI.Decl] = { + val (parNames, _) = getParNames(dr)(vc) + + val hdcert = V(LocalName("hd" + name + "cert"))(V(vc.next(true))) + val assExprs = dr.arguments.collect { + case RuleAssumption(cj) => + val parNames = cj.parameters.map { vd => vd.name } + val hypNames = cj.hypotheses.map { a => vc.next(false) } + val names = parNames ::: hypNames + ELPI.Lambda(names, hdcert) + } + val res = HelpCons(c.path)(hdcert :: parNames.map(V) ::: assExprs :_*) + val r = ELPI.Forall(parNames, ELPI.Impl(List(),res)) + List(ELPI.Rule(r)) + } +} diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/elpi/Syntax.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/elpi/Syntax.scala index 5961e2407b..f409fe1844 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/elpi/Syntax.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/elpi/Syntax.scala @@ -21,7 +21,12 @@ object ELPI { abstract class Decl { def toELPI: String } - + + case class Data(name: LocalName, tp: Expr, isKind: Boolean) extends Decl { + def keyword = if (isKind) "kind" else "type" + def toELPI = keyword + " " + name + " " + tp.toELPI(false) + "." + } + case class Rule(rule: Expr) extends Decl { def toELPI = { val ruleS = rule match { @@ -72,7 +77,11 @@ object ELPI { case class Variable(name: LocalName) extends Expr { def toELPI(bracket: Boolean = true) = name.toString } - + + case class Integer(value: Int) extends Expr { + def toELPI(bracket: Boolean = true) = value.toString + } + case class Lambda(name: LocalName, scope: Expr) extends Expr { def toELPI(bracket: Boolean = true) = { Bracket(bracket)(s"$name \\ ${scope.toELPI(false)}") @@ -95,7 +104,8 @@ object ELPI { def toELPI(bracket: Boolean = true) = { val argsI = args.toList.init.map(_.toELPI(true)) val last = args.last - val bracketLast = !args.last.isInstanceOf[Lambda] + // val bracketLast = !args.last.isInstanceOf[Lambda] + val bracketLast = true // actually, the above seems to cause problems if penultimate arg is a variable val argsL = last.toELPI(bracketLast) val argsE = (argsI ::: List(argsL)).mkString(" ") @@ -122,13 +132,15 @@ object ELPI { } /** built-in unary operators */ - case class UnOp(name: String, prefix: Boolean) - object Not extends BinOp("not") + case class UnOp(name: String, prefix: Boolean) { + def apply(e : Expr): UnaryApply = UnaryApply(this, e) + } + object Not extends UnOp("not", true) /** special case for the application of binary operators */ case class BinaryApply(operator: BinOp, left: Expr, right: Expr) extends Application(Variable(LocalName(operator.name)), left, right) { override def toELPI(bracket: Boolean = true) = { - val leftS = left.toELPI(! left.isAtomic) + val leftS = left.toELPI((! left.isAtomic) || (operator.name == "=>" && left.isInstanceOf[BinaryApply])) // TODO: properly capture case "a, b => c" val rightS = right.toELPI(! right.isAtomic) Bracket(bracket)(s"$leftS ${operator.name} $rightS") } @@ -151,9 +163,13 @@ object ELPI { } object Or extends BinOp(";") object Is extends BinOp("is") + object Equal extends BinOp("=") + object GreaterThan extends BinOp(">") + object Minus extends BinOp("-") object Impl extends BinOp("=>") { def apply(left: List[Expr], right: Expr): Expr = if (left.isEmpty) right else Impl(And(left), right) } + object Arrow extends BinOp("->") /** built-in binders */ abstract class Binder(nameS: String) { diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/hollight/HOLLight.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/hollight/HOLLight.scala index 9b59fb8d9d..e74420bf1d 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/hollight/HOLLight.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/hollight/HOLLight.scala @@ -13,7 +13,7 @@ object HOLLight { val apply = logic ? "Comb" val lambda = logic ? "Abs" val oftype = logic ? "term" - val hoas = notations.HOAS(apply, lambda, oftype) + val hoas = notations.HOAS(apply, lambda) val tp = logic ? "holtype" val tm = logic ? "term" diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveDefinitions.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveDefinitions.scala index 52209d2298..8b4ef2a04b 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveDefinitions.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveDefinitions.scala @@ -24,39 +24,39 @@ class InductiveDefinitions extends StructuralFeature("inductive_definition") wit * @param dd the derived declaration from which the inductive type(s) are to be constructed */ override def check(dd: DerivedDeclaration)(implicit env: ExtendedCheckingEnvironment) { - val (context, indParams, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.feature)) + val (context, indParams, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.compatibleFeatures)) checkParams(indCtx, indParams, Context(dd.parent)++context, env) } /** * Compute the expected type for an inductive definition case + * @param dd the derived declaration containing the definiens + * @param intDecls the internal declaration to give definiens for + * @param indDPath the path to the module containing the internal declarations to give definiens for + * @param c the constant to give a definien for */ - def expectedType(dd: DerivedDeclaration, intDecls: List[InternalDeclaration], indDPath: GlobalName, c: Constant) : (Term, (Boolean, Option[GlobalName])) = { + def expectedType(dd: DerivedDeclaration, intDecls: List[InternalDeclaration], indDPath: GlobalName, c: Constant) : Term = { val tpdecls = tpls(intDecls) val tplPathMap = tpdecls map {tpl => (tpl.externalPath(indDPath), correspondingDecl(dd, tpl.name).get.df.get) } - val intC = intDecls.find(_.name == c.name).get + val intC = intDecls.find(_.name == c.name).getOrElse( + throw GeneralError("Definien for declaration "+c.name+" is missing. ")) val tr = TraversingTranslator(OMSReplacer(p => utils.listmap(tplPathMap, p))) - val cons = intC match { - case const : Constructor => - val intTpl = const.getTpl - val tplC = dd.getDeclarations.find(_.name == intTpl.name).get.path - (true, Some(tplC)) - case _ => (false, None) - } - (tr(Context.empty, intC.externalTp(indDPath)), cons) + tr(Context.empty, intC.externalTp(indDPath)) } /** * Check that each definien matches the expected type + * @param dd the derived declaration whoose declarations provide the required definiens + * @param c the constant for which we should compute the expected type */ override def expectedType(dd: DerivedDeclaration, c: Constant): Option[Term] = { - val (context, indParams, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.feature)) + val (context, indParams, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.compatibleFeatures)) val intDecls = parseInternalDeclarations(indD, controller, Some(indCtx)) - Some(expectedType(dd, intDecls, indD.path, c)._1) + Some(expectedType(dd, intDecls, indD.path, c)) } /** @@ -67,7 +67,7 @@ class InductiveDefinitions extends StructuralFeature("inductive_definition") wit * @param dd the derived declaration to be elaborated */ def elaborate(parent: ModuleOrLink, dd: DerivedDeclaration)(implicit env: Option[uom.ExtendedSimplificationEnvironment] = None) = { - val (context, indParams, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.feature)) + val (context, indParams, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.compatibleFeatures)) //The internal definitions we need to give definiens for val intDecls = parseInternalDeclarations(indD, controller, None) filterNot (_.isOutgoing) @@ -75,9 +75,8 @@ class InductiveDefinitions extends StructuralFeature("inductive_definition") wit val (tplNames, tmlNames, intDeclNames) = (tpls map (_.name), tmls map (_.name), intDecls map (_.name)) val tplExtNames = tplNames map (externalName(indD.path, _)) - -// val expectations = {c:Constant => expectedType(dd, controller, intDecls, indD.path, c)} - val defDecls = parseInternalDeclarationsWithDefiniens(dd, controller, Some(context), Some(expectedType(dd, intDecls, indD.path, _)._2)) + + val defDecls = parseInternalDeclarationsWithDefiniens(dd, controller, Some(context), Some(intDecls)) val (defTpls, defConstrs) = (InternalDeclaration.tpls(defDecls), constrs(defDecls)) // and whether we have all necessary declarations @@ -140,4 +139,4 @@ class InductiveDefinitions extends StructuralFeature("inductive_definition") wit } } -object InductiveDefinitionRule extends StructuralFeatureRule(classOf[InductiveDefinitions], "inductive_definition") \ No newline at end of file +object InductiveDefinitionRule extends StructuralFeatureRule(classOf[InductiveDefinitions], "inductive_definition") diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveMatch.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveMatch.scala index f7b26e522e..e8b2448cce 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveMatch.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveMatch.scala @@ -45,7 +45,7 @@ class InductiveMatch extends StructuralFeature("match") with TypedParametricTheo * Check that each definien matches the expected type */ override def expectedType(dd: DerivedDeclaration, c: Constant): Option[Term] = { - val (context, indParams, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.feature)) + val (context, indParams, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.compatibleFeatures)) val intDecls = parseInternalDeclarations(indD, controller, Some(indCtx)) Some(expectedType(dd, intDecls, indD.path, c)._1) @@ -59,7 +59,7 @@ class InductiveMatch extends StructuralFeature("match") with TypedParametricTheo * @param dd the derived declaration to be elaborated */ def elaborate(parent: ModuleOrLink, dd: DerivedDeclaration)(implicit env: Option[uom.ExtendedSimplificationEnvironment] = None) = { - val (context, indParams, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.feature)) + val (context, indParams, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.compatibleFeatures)) implicit val parent = indD.path val intDecls = parseInternalDeclarations(indD, controller, Some(indCtx)) diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveProofDefinitions.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveProofDefinitions.scala index c341a4f059..e1011d08e6 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveProofDefinitions.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveProofDefinitions.scala @@ -25,7 +25,7 @@ class InductiveProofDefinitions extends StructuralFeature("ind_proof") with Type * @param dd the derived declaration from which the inductive type(s) are to be constructed */ override def check(dd: DerivedDeclaration)(implicit env: ExtendedCheckingEnvironment) { - val (context, indParams, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.feature)) + val (context, indParams, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.compatibleFeatures)) checkParams(indCtx, indParams, Context(dd.parent)++context, env) } @@ -33,15 +33,21 @@ class InductiveProofDefinitions extends StructuralFeature("ind_proof") with Type * Checks that each definien matches the expected type */ override def expectedType(dd: DerivedDeclaration, c: Constant): Option[Term] = { - val (_, _, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.feature)) + val (_, _, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.compatibleFeatures)) - val intDecls = parseInternalDeclarations(indD, controller, Some(indCtx)) + val intDecls = parseInternalDeclarationsSubstitutingDefiniens(indD, controller, Some(indCtx)) val (constrdecls, tpdecls) = (constrs(intDecls), tpls(intDecls)) - val (_, _, indProofDeclMap, ctx) = inductionHypotheses(tpdecls, constrdecls, indCtx)(indD.path) + val (_, _, indPfCases, _) = inductionHypotheses(tpdecls, constrdecls, indCtx)(indD.path) - val intDecl = intDecls.find(_.name == c.name) - - utils.listmap(indProofDeclMap, intDecl) map (_.tp.get) + val intDecl = intDecls.find(_.name == c.name).getOrElse(throw ImplementationError("No declaration to give a definien for is found for "+c.path)) + val tp = utils.listmap(indPfCases, intDecl).map(_.tp.get) + //Replace references to the inductive claims by their definitions read earlier + val externalTp = tp map {tp => + val substs = indPfCases.filter(_._1.isTypeLevel).map({case (tpl: TypeLevel, claim: VarDecl) => + Sub(claim.name, dd.get(tpl.name).toTerm)}) + tp ^ substs + } + Some(externalTp.get) } /** @@ -52,7 +58,7 @@ class InductiveProofDefinitions extends StructuralFeature("ind_proof") with Type * @param dd the derived declaration to be elaborated */ def elaborate(parent: ModuleOrLink, dd: DerivedDeclaration)(implicit env: Option[uom.ExtendedSimplificationEnvironment] = None) = { - val (context, indParams, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.feature)) + val (context, indParams, indD, indCtx) = parseTypedDerivedDeclaration(dd, Some(inductiveUtil.compatibleFeatures)) implicit val parent = indD.path val intDecls = parseInternalDeclarations(indD, controller, None) @@ -60,8 +66,6 @@ class InductiveProofDefinitions extends StructuralFeature("ind_proof") with Type val intTplNames = intTpls map (_.name) - //The constructors amongst the following declarations will most likely be misparsed as outgoing termlevels - //However, as the below code doesn't use the distinction this is acceptable var decls = parseInternalDeclarationsWithDefiniens(dd, controller, Some(context)) val proof_paths = intTpls.map(t=>externalName(indD.path, proofName(t.name))) @@ -86,4 +90,4 @@ class InductiveProofDefinitions extends StructuralFeature("ind_proof") with Type } } -object InductiveProofDefinitionRule extends StructuralFeatureRule(classOf[InductiveProofDefinitions], "ind_proof") \ No newline at end of file +object InductiveProofDefinitionRule extends StructuralFeatureRule(classOf[InductiveProofDefinitions], "ind_proof") diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveTypes.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveTypes.scala index 8cd66ea830..73bc0f40a2 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveTypes.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InductiveTypes.scala @@ -15,6 +15,10 @@ import StructuralFeatureUtils._ import StructuralFeatureUtil._ object inductiveUtil { + /** + * name of the noConf declaration corresponding to c and d + */ + def noConfName(a:LocalName, b:LocalName):LocalName = {LocalName("no_conf_"+a.toString+"_"+b.toString)} /** * name of the declaration corresponding to n declared in noJunks */ @@ -31,7 +35,20 @@ object inductiveUtil { def proofName(n: LocalName): LocalName = {LocalName("ind_proof") / n} /** name of the applied version of the inductive definition declaration generated for constructors */ def appliedName(n: LocalName) : LocalName = {n / LocalName("Applied")} + def idAdditionalGenerated(c:Constant) = { + c.name match { + case al / b => + val a = LocalName(al.toString) + val generated = List(inductName(b),testerName(a),unapplierName(a),proofPredName(a),proofName(a),appliedName(a)) contains c.name + case nm if ((nm.toString contains "injective_") || (nm.toString contains "no_conf_")) => true + case _ => false + } + } + def feature = {"inductive"} + //features generating inductive types as output, which may be referenced by inductive definitions, inductive proofs, ... + def compatibleFeatures = {List(feature, ReflectionsUtil.feature)} + } import inductiveUtil._ @@ -75,6 +92,8 @@ class InductiveTypes extends StructuralFeature(inductiveUtil.feature) with Param // copy all the declarations decls foreach {d => elabDecls ::= d.toConstant} + + //check them tmdecls foreach { tmdecl => checkTermLevel(tmdecl, types)} // the no confusion axioms for the data constructors @@ -207,7 +226,7 @@ class InductiveTypes extends StructuralFeature(inductiveUtil.feature) with Param val (matches, argMatches) = matchTypeParametersInReturnType(b, a) if (matches) { // TODO: Check this doesn't generate ill-typed declarations for dependently-typed constructors - val newName = LocalName("no_conf_" + a.name.last+ "_" + b.name.last) + val newName = noConfName(a.name, b.name) val Ltp = () => { val (aCtx, aApplied) = a.argContext(None) val (bCtx, bApplied) = b.argContext(None) @@ -379,21 +398,33 @@ object InductiveTypes { /** * Produces the proof predicate for each Type-Level and the corresponding induction hypothesis for each constructor + * @param tpdecls the typelevels tpl_i declaring the mutually inductively defined types (tp_i) over which to apply induction + * @param tmdecls the termlevels leading to the inductive cases + * @param context the outer context of the declarations + * @return A pair consisting of + * 1) the proof predicate (claim) pred_i for each type tp_i, + * 2) a list of pairs (tpp_i, f_) for tpp_i the path of tpl_i, + * where f maps (tm, tp) |-> pred_i tm if the return type of tp is tp_i, else (tm, tp) |-> tm + * 3) a list of pairs (intdecl, claim) for each tpl or constructor intdecl, + * where claim is a variable for the inductive claim for intdecl + * 4) a context with all local names used in the function, + * it is used to prevent name clashes in this recursive function */ def inductionHypotheses(tpdecls : List[TypeLevel], tmdecls : List[TermLevel], context: Context)(implicit parent : GlobalName) = { var ctx = context + // var indProofDeclMap : List[(GlobalName, (Term, Term) => Term)] = Nil var predsMap : List[(InternalDeclaration, VarDecl)] = Nil //a context of the predicates for each inductively-defined type - //For a tpl: Pi r. type this returns a predicate of type Pi rBar.tpl(x) -> pred, where - //rBar:= flatMap (x => x zip applyPred x) + //For a tpl: Pi r. type this returns a predicate of type Pi rBar(x).tpl(x) -> pred, where + //rBar(x):= flatMap (x => x zip applyPred x) //x=r val preds = tpdecls map {tpl => val (argCon, dApplied) = tpl.argContext() val tp = PiOrEmpty(rBar(argCon, tpdecls, indProofDeclMap), Arrow(dApplied, Prop)) val pred = newVar(proofPredName(tpl.name).toString(), tp, Some(ctx++argCon)) - + val map : (GlobalName, (Term, Term) => Term) = (tpl.externalPath, { (tm, tp) => tp match { case OMS(p) if p == tpl.externalPath => Apply(pred.toTerm, tm) @@ -409,17 +440,17 @@ object InductiveTypes { } //The required assumptions for the constructors - val inductCases = constrs(tmdecls) map {tml => - val (argCon, dApplied) = tml.argContext() - val (tpl, tplArgs) = (tml.getTpl, tml.getTplArgs) - val pred = utils.listmap(predsMap, tpl).getOrElse(throw ImplementationError("")) - val claim = ApplyGeneral(pred.toTerm, tplArgs.+:(dApplied)) - val inductCase = newVar(proofPredName(tml.name), PiOrEmpty(rBar(argCon, tpdecls, indProofDeclMap), claim), ctx++argCon) - predsMap ::= (tml, inductCase) + val inductCases = constrs(tmdecls) map {constr => + val (argCon, dApplied) = constr.argContext() + val pred = utils.listmap(predsMap, constr.getTpl).get + val claim = ApplyGeneral(pred.toTerm, constr.getTplArgs.+:(dApplied)) + val assumptions = rBar(argCon, tpdecls, indProofDeclMap) + val inductCase = newVar(proofPredName(constr.name), PiOrEmpty(assumptions, claim), ctx++argCon) + predsMap ::= (constr, (inductCase)) ctx++=inductCase; inductCase } (preds, indProofDeclMap, predsMap, ctx) } } -object InductiveRule extends StructuralFeatureRule(classOf[InductiveTypes], "inductive") \ No newline at end of file +object InductiveRule extends StructuralFeatureRule(classOf[InductiveTypes], "inductive") diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InternalDeclaration.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InternalDeclaration.scala index bbfde11ff3..16484147cb 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InternalDeclaration.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/InternalDeclaration.scala @@ -170,29 +170,32 @@ object InternalDeclaration { * convert the given constant into the appropriate internal declaration * @param c the constant to convert * @param con the controller + * @param types the list of defined typelevels + * @param ctx the outer context of the declaration * @param isConstructor (optional) whether the declaration is a constructor and if so its typelevel + * , overrides the checks on the type done otherwise * Needs to be given for constructors over defined typelevels + * (as they may not have a type specified initially and matching with inferred types is overcomplicating things) * @precondition if isConstructor is given and its first part is true, the second part must be defined and contain the corresponding typelevel */ - def fromConstant(c: Constant, con: Controller, types: List[TypeLevel], ctx: Option[Context], isConstructor: Option[(Boolean, Option[GlobalName])] = None)(implicit parent : GlobalName) : InternalDeclaration = { - val tp = c.tp.get + def fromConstant(c: Constant, con: Controller, types: List[TypeLevel], ctx: Option[Context], isConstructor: Option[(Boolean, Option[GlobalName])] = None) : InternalDeclaration = { + val tp = c.tp.getOrElse(throw ImplementationError("type expected for declaration at "+c.path+" but none found.")) val FunType(args, ret) = tp val context = Some(ctx getOrElse Context.empty) - val p = c.path if (JudgmentTypes.isJudgment(ret)(con.globalLookup)) { - StatementLevel(p, args, c.df, context, Some(c.notC)) + StatementLevel(c.path, args, c.df, context, Some(c.notC)) } else { ret match { - case Univ(1) => TypeLevel(p, args, c.df, context, Some(c.notC)) + case Univ(1) => TypeLevel(c.path, args, c.df, context, Some(c.notC)) case Univ(x) if x != 1 => throw ImplementationError("unsupported universe") case r => val isConstructorMapped = isConstructor map ({d => (d._1, types.find(_.path == d._2.get))}) val isConstr: (Boolean, Option[TypeLevel]) = isConstructorMapped.getOrElse (ret match { - case ApplyGeneral(OMS(p), _) if (types map (_.path) contains p)=> (true, types.find(_.path == p)) + case ApplyGeneral(OMS(p), _) if (types map (_.path) contains p) => (true, types.find(_.path == p)) case t if (types map (_.df) contains Some(ret)) => (true, types.find(_.df == Some(ret))) case _ => (false, None) }) - if (isConstr._1) new Constructor(p, args, ret, isConstr._2.get, c.df, Some(c.notC), ctx) else new OutgoingTermLevel(p, args, ret, c.df, Some(c.notC), ctx) + if (isConstr._1) new Constructor(c.path, args, ret, isConstr._2.get, c.df, Some(c.notC), ctx) else new OutgoingTermLevel(c.path, args, ret, c.df, Some(c.notC), ctx) } } } @@ -344,7 +347,7 @@ sealed abstract class InternalDeclaration { } } - def toString(termPresenter:Option[Term=>String]) = { + def toString(termPresenter:Option[Term=>String] = None) = { def pre(t: Term):String = {termPresenter.getOrElse({tm:Term => present(tm, true)})(t)} val Type = if (isTypeLevel) "Typelevel" else if (isConstructor) "Constructor" else "Outgoing termlevel" Type+"("+ name+": "+pre(tp)+(if(df != None) " = "+pre(df.get) else "")+")" diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/Reflections.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/Reflections.scala index 55afcd47c1..4877986794 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/Reflections.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/Reflections.scala @@ -16,15 +16,20 @@ import StructuralFeatureUtil._ import inductiveUtil._ import structuralfeatures.InductiveTypes._ +object ReflectionsUtil { + def feature = {"reflect"} +} + /** theories as a set of types of expressions */ -class Reflections extends StructuralFeature("reflect") with TypedParametricTheoryLike { - - /** +class Reflections extends StructuralFeature(ReflectionsUtil.feature) with ReferenceLikeTypedParametricTheoryLike { + /** * Checks the validity of the inductive type(s) to be constructed * @param dd the derived declaration from which the inductive type(s) are to be constructed */ - override def check(dd: DerivedDeclaration)(implicit env: ExtendedCheckingEnvironment) {} - + override def check(dd: DerivedDeclaration)(implicit env: ExtendedCheckingEnvironment) { + val (_, _, indCtx, indParams, context) = getContent(dd) + checkParams(indCtx, indParams, Context(dd.parent)++context, env) + } /** * Elaborates an declaration of one or multiple mutual inductive types into their declaration, * as well as the corresponding no confusion and no junk axioms @@ -33,29 +38,27 @@ class Reflections extends StructuralFeature("reflect") with TypedParametricTheor * @param dd the derived declaration to be elaborated */ def elaborate(parent: ModuleOrLink, dd: DerivedDeclaration)(implicit env: Option[uom.ExtendedSimplificationEnvironment] = None) = { - - val (indDefPathGN, context, indParams) = ParamType.getParams(dd) + val (indDefPathGN, consts, indCtx, indParams, context) = getContent(dd) val indDefPath = indDefPathGN.toMPath - // TODO: figure out how to work with theory parameters - val (indCtx, consts) = controller.getO(indDefPath) match { - case Some(str) => str match { - case t: Theory => (Context.empty, getConstants(t.getDeclarations, controller)) - case t: StructuralElement if (t.feature =="theory") => - val decls = t.getDeclarations map {case d: Declaration => d case _ => throw LocalError("unsupported structural element at "+t.path)} - (Context.empty, getConstants(decls, controller)) - case m : ModuleOrLink => (Context.empty, getConstants(m.getDeclarations, controller)) - case t => throw GeneralError("reflection over unsupported target (expected module or link)"+t.path+" of feature "+t.feature + ": "+t) - } - case None => throw GeneralError("target of reflection not found at "+indDefPath) - } - - val declsPre = readInternalDeclarations(consts, controller, Some(context))(indDefPathGN) + + val declsPre = readInternalDeclarations(consts, controller, Some(context))(dd.path) val subst = indCtx zip indParams map {case (vd, param) => Sub(vd.name, param)} - val tr = OMSReplacer({p:GlobalName => if (p.module == indDefPath.module/indDefPath.name) Some(OMS(dd.modulePath ? p.name)) else None}) + val tr = OMSReplacer({p:GlobalName => if (p.module == indDefPath) Some(OMS(dd.modulePath ? p.name)) else None}) val decls = declsPre map (_.translate(ApplySubs(subst)).translate(TraversingTranslator(tr))) structuralfeatures.InductiveTypes.elaborateDeclarations(context, decls, controller, Some({c => log(defaultPresenter(c)(controller))}))(dd.path) } + + /** + * parse the derived declaration into its components + * @param dd the derived declaration + * @return returns a pair containing the mpath of the derived declaration, the constants defined in the referenced theory, + * the argument context of this derived declaration, the arguments provided to the referenced theory and the outer context + */ + def getContent(dd:DerivedDeclaration): (GlobalName, List[Constant], Context, List[Term], Context) = { + val (indDefPathGN, decls, indCtx, indParams, context) = getDecls(dd) + (indDefPathGN, getConstants(decls, controller)(dd.path.toMPath), indCtx, indParams, context) + } } object ReflectionRule extends StructuralFeatureRule(classOf[Reflections], "reflect") \ No newline at end of file diff --git a/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/StructuralFeaturesUtil.scala b/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/StructuralFeaturesUtil.scala index c3c7d40743..a951358f06 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/StructuralFeaturesUtil.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/lf/structuralfeatures/StructuralFeaturesUtil.scala @@ -66,11 +66,6 @@ object StructuralFeatureUtils { /** negate the statement in the type */ def neg(tp: Term) : Term = Arrow(tp, Contra) - /** To allow for more user friendly inductive-proof declarations - * Takes a predicate pr: ⊦ x ≐ y for x: A, y: A, a (initial) function f: A → B and returns a predicate ⊦ f a ≐ f b - * @param pr the predicate : ⊦ x ≐ y - * @param f the function f : ⊦ A → B - */ object CONG { val path = theory ? "CONG" def apply(A: Term, B: Term, x: Term, y: Term, p_xeqy: Term, f: Term) = ApplyGeneral(OMS(path), List(A,B,f,x,y, p_xeqy)) @@ -91,13 +86,18 @@ object StructuralFeatureUtils { } CONG(a, b, x, y, prDf, funcDf) } - + + /** To allow for more user friendly inductive-proof declarations + * Takes a predicate pr: ⊦ x ≐ y for x: A, y: A, a (initial) function f: A → B and returns a predicate ⊦ f a ≐ f b + * @param pr the predicate : ⊦ x ≐ y + * @param func the function func : ⊦ A → B + */ def Cong(pr: VarDecl, func: VarDecl) : Term = {Cong(pr.tp.get, pr.toTerm, func.tp.get, func.toTerm)} /** * Takes a predicate pr: ⊦ x ≐ y for x: C → D, y: C → D and a term tm: C and returns a predicate ⊦ f x ≐ f y, where f x := x tm - * @param pr the predicate : ⊦ x ≐ y - * @param f the function f : ⊦ f x ≐ f y + * @param prTp type of the predicate : ⊦ x ≐ y + * @param prDf definien of the predicate : ⊦ x ≐ y * @param tm the term tm: C * @return the type and and proof of the resulting predicate */ @@ -124,111 +124,183 @@ object StructuralFeatureUtils { CongAppl(prcTp, prcDf, tm) }) } - + /** * parse the declarations into constants, flattening PlainIncludes * @param decls the declarations to parse * @param con the controller + * @param parent (implicit) the (new) parent module of all read constants */ - def getConstants(decls: List[Declaration], con: Controller): List[Constant] = { - decls.map({d:Declaration => + def getConstants(decls: List[Declaration], con: Controller)(implicit parent: MPath): List[Constant] = { + val consts:List[Constant] = decls.flatMap { d: Declaration => d match { - case c: Constant => List(c) - case PlainInclude(from, to) => - val target = con.getO(to) + case c: Constant => List (c) + case PlainInclude(from, to) => + val target: Option[StructuralElement] = con.getO(from) target match { case Some(refDD: DerivedDeclaration) => parseInternalDeclarationsIntoConstants(refDD, con) - case Some(m : ModuleOrLink) => getConstants(m.getDeclarations, con) - case Some(t) => throw GeneralError("unsupported include of"+t.path) + case Some(m: ModuleOrLink) => getConstants(m.getDeclarations, con) + case Some(t) => throw GeneralError("unsupported include of " + t.path) case None => throw GeneralError("found empty include") } case _ => throw GeneralError("unsupported declaration") } - }).flatten + } + +// println("The constant declarations whoose module paths to fix: "+consts) +// println("The parent module path to set them to: "+parent) + + val names = consts map(_.name) + val repl = OMSReplacer(g => names.find(_ == g.name).map(_=>OMS(g.copy(module = parent)))) + val tr = TraversingTranslator(repl) + + val tpCf = consts.map(c => c.tpC.map(tr(Context.empty, _))) +// println("The fixed types of the constants: "+tpCf) + consts map {c => + Constant.apply(OMMOD(parent.module), c.name, c.alias, c.tpC.map(tr(Context.empty, _)), c.dfC.map(tr(Context.empty, _)), c.rl, c.notC) + } } - - /** - * Reads the internal declarations (assumed to be constants) of a derivedDeclaration unfolding PlainIncludes - * @param dd the derived declaration whoose internal declaration to read + + /** + * Parse the internal declarations of dd into constants unfolding includes + * @param dd the derived declaration whoose internal declarations to parse + * @param con the controller + * @precondition if isConstructor is given for a constant and its first part is true, the second part must be defined and contain the corresponding typelevel + */ + def parseInternalDeclarationsIntoConstants(dd: DerivedDeclaration, con: Controller): List[Constant] = { + val sf = con.extman.get(classOf[StructuralFeature], dd.feature).getOrElse(throw GeneralError("Structural feature "+dd.feature+" not found.")) + val consts = sf match { + case rldd : ReferenceLikeTypedParametricTheoryLike => parseReferenceLikeDerivedDeclaration(dd, con, rldd) + case _ => getConstants(dd.getDeclarations, con)(dd.modulePath) + } + consts + } + + /** + * Parse the internal declarations of dd + * @param dd the derived declaration whoose internal declarations to parse * @param con the controller + * @param sf the structural feature of the derived declaration */ - def parseInternalDeclarationsIntoConstants(dd:DerivedDeclaration, con: Controller) : List[Constant] = { - getConstants(dd.getDeclarations, con) + def parseReferenceLikeDerivedDeclaration(dd: DerivedDeclaration, con:Controller, sf: ReferenceLikeTypedParametricTheoryLike) : List[Constant] = { + getConstants(sf.getDecls(dd)._2, con)(dd.modulePath) + } + + + /** + * Reads in the internal declarations of the given derived declaration and parses them into internal declarations. + * It also will expand any references to previous declarations in terms of their definiens, if existent + * @param dd the derived declaration whoose declarations to parse + * @param con the controller, needed to parse the delarations + * @param ctx (optional) the (outer) context of the declarations + * @param intDeclsO (optional) the declarations for which to provide definiens + */ + def parseInternalDeclarationsSubstitutingDefiniens(dd:DerivedDeclaration, con: Controller, ctx: Option[Context] = None, intDeclsO: Option[List[InternalDeclaration]] = None): List[InternalDeclaration] = { + val consts = parseInternalDeclarationsIntoConstants(dd, con) + readInternalDeclarationsSubstitutingDefiniens(consts, con, ctx, intDeclsO) } /** * Reads in the given constants and parses them into Internal declarations. * It also will expand any references to previous declarations in terms of their definiens, if existent + * This is useful for e.g. inductive definitions, inductive matches and inductive proofs * @param decls the declarations to parse - * @param con the controller, needed to parse the delarations + * @param con the controller, needed to parse the declarations * @param ctx (optional) the (outer) context of the declarations - * @param isConstructor (optional) computes whether the declaration is a constructor and if so its typelevel - * Needs to be given for constructors over defined typelevels - * @precondition if isConstructor is given for a constant and its first part is true, the second part must be defined and contain the corresponding typelevel + * @param intDeclsO (optional) the internal declarations for which to provide definiens */ - def readInternalDeclarationsSubstitutingDefiniens(decls: List[Constant], con: Controller, ctx: Option[Context] = None, isConstructor: Option[Constant => (Boolean, Option[GlobalName])] = None)(implicit parent : GlobalName): List[InternalDeclaration] = { + def readInternalDeclarationsSubstitutingDefiniens(decls: List[Constant], con: Controller, ctx: Option[Context] = None, intDeclsO: Option[List[InternalDeclaration]] = None): List[InternalDeclaration] = { val context = ctx.getOrElse(Context.empty) var types: List[TypeLevel] = Nil + val isConstr = isConstructor(decls, intDeclsO) decls map {c => - val intDecl = fromConstant(c, con, types, ctx, isConstructor map (_(c))) + val intDecl = fromConstant(c, con, types, ctx, isConstr map (_(c))) def repDfs(df: GlobalName) = {utils.listmap(decls.map(t => (t.path, ApplyGeneral(t.df.get, context.map(_.toTerm)))), df)} def mpDf(df: Term):Term = OMSReplacer(repDfs)(df, Context.empty) val repDf = intDecl.df map (mpDf) - + val decl = intDecl match { - case constr: Constructor => new Constructor(constr.path, constr.args, constr.ret, constr.getTpl, repDf, constr.notC, constr.ctx) - case out: OutgoingTermLevel => new OutgoingTermLevel(out.path, out.args, out.ret, repDf, out.notC, out.ctx) - case d : TypeLevel => d.copy(df = repDf) - case d : StatementLevel => d.copy(df = repDf) - case _ => throw ImplementationError("invalid InternalDeclaration") - } + case constr: Constructor => new Constructor(constr.path, constr.args, constr.ret, constr.getTpl, repDf, constr.notC, constr.ctx) + case out: OutgoingTermLevel => new OutgoingTermLevel(out.path, out.args, out.ret, repDf, out.notC, out.ctx) + case d : TypeLevel => d.copy(df = repDf) + case d : StatementLevel => d.copy(df = repDf) + case _ => throw ImplementationError("invalid InternalDeclaration") + } decl match {case tpl: TypeLevel => types +:= tpl case _ =>} decl } } - + /** - * Reads in the internal declarations of the given derived declaration and parses them into internal declarations. - * It also will expand any references to previous declarations in terms of their definiens, if existent - * @param dd the derived declaration whoose declarations to parse - * @param con the controller, needed to parse the delarations - * @param ctx (optional) the (outer) context of the declarations - * @param isConstructor (optional) computes whether the declaration is a constructor and if so its typelevel - * Needs to be given for constructors over defined typelevels - * @precondition if isConstructor is given for a constant and its first part is true, the second part must be defined and contain the corresponding typelevel + * Construct a map computing for each declaration whether it is a constructor and if so its typelevel + * @param decls the declarations to check + * @param intDecls the derived declarations for which to provide definiens + * @return A function Constant => (Boolean, Option[GlobalName]) + * , defined by c |-> (true, Some(p)) if c is constructor of type OMS(p) + * and c |-> (false, None) if c is not a constructor + * The function throws an error if there is no corresponding declaration (i.e. of same name) to c in intDecls */ - def parseInternalDeclarationsSubstitutingDefiniens(dd:DerivedDeclaration, con: Controller, ctx: Option[Context] = None, isConstructor: Option[Constant => (Boolean, Option[GlobalName])] = None): List[InternalDeclaration] = { - var consts : List[Constant] = parseInternalDeclarationsIntoConstants(dd, con) - readInternalDeclarationsSubstitutingDefiniens(consts, con, ctx, isConstructor)(dd.path) + def isConstructor(decls: List[Constant], intDecls: List[InternalDeclaration]) : Constant => (Boolean, Option[GlobalName]) = { + val isConstrO:List[(Constant, (Boolean, Option[GlobalName]))] = decls map {c => + val intC = intDecls.find(_.name == c.name).getOrElse( + throw GeneralError("Definien for declaration "+c.name+" is missing. \nOnly found the declarations:\n"+intDecls.map(_.name))) + intC match { + case const : Constructor => (c, (true, Some(decls.find(_.name == const.getTpl.name).get.path))) + case _ => (c, (false, None)) + } + } + utils.listmap(isConstrO, _ : Constant).get } - + /** - * Parse the internal declarations of dd + * Construct a map computing for each declaration whether it is a constructor and if so its typelevel + * + * @param decls the declarations to check + * @param intDeclsO (optional) the derived declarations for which to provide definiens + * @return if intDeclsO is given, then return Some function Constant => (Boolean, Option[GlobalName]), + * defined by c |-> (true, Some(p)) if c is constructor of type OMS(p) + * * and c |-> (false, None) if c is not a constructor + * The function throws an error if there is no corresponding declaration (i.e. of same name) to c in intDecls + */ + def isConstructor(decls: List[Constant], intDeclsO: Option[List[InternalDeclaration]] = None) : Option[Constant => (Boolean, Option[GlobalName])] = { + intDeclsO map { intDecls => isConstructor(decls, intDecls) } + } + + /** + * Parse the internal declarations of dd into internal declarations * @param dd the derived declaration whoose internal declarations to parse * @param con the controller * @param ctx (optional) a context to parse the declarations in - * @param isConstructor (optional) computes whether the declaration is a constructor and if so its typelevel - * Needs to be given for constructors over defined typelevels + * @param intDeclsO (optional) the derived declarations for which to provide definiens * @precondition if isConstructor is given for a constant and its first part is true, the second part must be defined and contain the corresponding typelevel */ - def parseInternalDeclarations(dd: DerivedDeclaration, con: Controller, ctx: Option[Context] = None, isConstructor: Option[Constant => (Boolean, Option[GlobalName])] = None): List[InternalDeclaration] = { - readInternalDeclarations(parseInternalDeclarationsIntoConstants(dd, con), con, ctx, isConstructor)(dd.path) + def parseInternalDeclarations(dd: DerivedDeclaration, con: Controller, ctx: Option[Context] = None, intDeclsO: Option[List[InternalDeclaration]] = None): List[InternalDeclaration] = { + val consts = parseInternalDeclarationsIntoConstants(dd,con) + readInternalDeclarations(consts, con, ctx, intDeclsO)(dd.path) } - - def readInternalDeclarations(consts: List[Constant], con: Controller, ctx: Option[Context] = None, isConstructor: Option[Constant => (Boolean, Option[GlobalName])] = None)(implicit parent : GlobalName): List[InternalDeclaration] = { + + /** + * parse the given constants into internal declarations + * the parent modules of all internal declarations is set to the mpath of the parsed derived declaration + * @param consts the constants to parse + * @param con the controller + * @param ctx (optional) the context of the declarations + * @param intDeclsO (optional) the derived declarations for which to provide definiens + * @param parent (implicit) the path of the derived declaration that is parsed + * @return a list of internal declarations containing the parsed constants + */ + def readInternalDeclarations(consts: List[Constant], con: Controller, ctx: Option[Context] = None, intDeclsO: Option[List[InternalDeclaration]] = None)(implicit parent: GlobalName): List[InternalDeclaration] = { val context = ctx.getOrElse(Context.empty) var types: List[TypeLevel] = Nil consts map {c => - val intDecl = fromConstant(c, con, types, ctx, isConstructor map (_(c))) - + val intDecl = fromConstant(c, con, types, ctx, isConstructor(consts, intDeclsO).map(_(c))) intDecl match {case tpl: TypeLevel => types +:= tpl case _ =>} intDecl } } - } import StructuralFeatureUtils._ @@ -252,12 +324,10 @@ object TermConstructingFeatureUtil { * @param dd the derived declaration whoose internal declarations to parse * @param con the controller * @param ctx (optional) a context to parse the declarations in - * @param isConstructor (optional) computes whether the declaration is a constructor and if so its typelevel - * Needs to be given for constructors over defined typelevels - * @precondition if isConstructor is given for a constant and its first part is true, the second part must be defined and contain the corresponding typelevel - */ - def parseInternalDeclarationsWithDefiniens(dd: DerivedDeclaration, con: Controller, ctx: Option[Context], isConstructor: Option[Constant => (Boolean, Option[GlobalName])] = None): List[InternalDeclaration] = { - val decls = parseInternalDeclarationsSubstitutingDefiniens(dd, con, ctx, isConstructor) + * @param intDecls (optional) the internal declarations for which to parse the definiens + */ + def parseInternalDeclarationsWithDefiniens(dd: DerivedDeclaration, con: Controller, ctx: Option[Context], intDecls: Option[List[InternalDeclaration]] = None): List[InternalDeclaration] = { + val decls = parseInternalDeclarationsSubstitutingDefiniens(dd, con, ctx, intDecls) decls.map(d => if (d.df.isEmpty) throw GeneralError("Unsupported corresponding declaration: Expected constant with definien at "+d.path)) decls } @@ -273,4 +343,4 @@ object TermConstructingFeatureUtil { case Some(en) => en.objectSimplifier.toTranslator(en.rules, expDef)(ctx, tm) } -} \ No newline at end of file +} diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/Rules.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/Rules.scala index ef8f2bfdaa..107254cc82 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/Rules.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/Rules.scala @@ -98,6 +98,39 @@ object ComplexTheoryInfer extends InferenceRule(ModExp.complextheory, OfType.pat } } +/** + * DIAG : Inhabitable + */ +object DiagramTypeInhabitable extends InhabitableRule(ModExp.diagramtype) { + override def apply(solver: Solver)(term: Term)(implicit stack: Stack, history: History): Option[Boolean] = term match { + case OMS(this.head) => Some(true) + case _ => Some(false) + } +} + +object AnonymousDiagramInfer extends InferenceRule(ModExp.anonymousdiagram, OfType.path) { + def apply(solver: Solver)(tm: Term, covered: Boolean)(implicit stack: Stack, history: History) : Option[Term] = tm match { + case AnonymousDiagramCombinator(_) => + Some(DiagramType()) + case _ => + None + } +} + +object DiagramCheck extends TypingRule(ModExp.anonymousdiagram) { + override def apply(solver: Solver)(tm: Term, tp: Term)(implicit stack: Stack, history: History): Option[Boolean] = { + val simplificationUnit = SimplificationUnit(stack.context, expandDefinitions = true, fullRecursion = true, solverO = Some(solver)) + + solver.controller.simplifier(tm, simplificationUnit) match { + case AnonymousDiagramCombinator(_) => + Some(true) + + case _ => + Some(false) + } + } +} + object AnonymousTheoryInfer extends InferenceRule(ModExp.anonymoustheory, OfType.path) { def apply(solver: Solver)(tm: Term, covered: Boolean)(implicit stack: Stack, history: History) : Option[Term] = tm match { case AnonymousTheoryCombinator(_) => diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/diagdefinition/DiagramDefinition.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/diagdefinition/DiagramDefinition.scala deleted file mode 100644 index 96bc749ca0..0000000000 --- a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/diagdefinition/DiagramDefinition.scala +++ /dev/null @@ -1,87 +0,0 @@ -package info.kwarc.mmt.moduleexpressions.diagdefinition - -import info.kwarc.mmt.api._ -import info.kwarc.mmt.api.checking._ -import info.kwarc.mmt.api.modules._ -import info.kwarc.mmt.api.notations.Marker -import info.kwarc.mmt.api.objects._ -import info.kwarc.mmt.api.symbols._ -import info.kwarc.mmt.api.utils._ -import info.kwarc.mmt.moduleexpressions.operators.Common - -object DiagramDefinition { - val feature = "diagram" -} - -/** - * Structural Feature for Diagram Operators - * - * @example - * ''' - * theory Empty = ❚ - * diagram Collection := ?Empty EXTEND { coll : type ⟶ type } ❚ - * ''' - * - * It expects an [[AnonymousDiagramCombinator]] as its (normalized) definiens and then - * lifts the produced [[AnonymousDiagram]] to the usual named space of MMT things. - * For each node and arrow in the [[AnonymousDiagram]], it is checked whether the [[LocalName]] - * of that diagram element is an [[Common.ExistingName]]. If not, it is added to the "outer namespace" - * (i.e. the DPath of the document containing that `diagram` declaration) with the same local name. - */ -class DiagramDefinition extends ModuleLevelFeature(DiagramDefinition.feature) { - override def getHeaderNotation: List[Marker] = Nil - - /** */ - def check(dm: DerivedModule)(implicit env: ExtendedCheckingEnvironment) {} - - override def modules(dm: DerivedModule): List[Module] = { - val diag: Term = dm.dfC.normalized.getOrElse { - throw LocalError("no definiens found for " + dm.path) - } - val ad = diag match { - case AnonymousDiagramCombinator(ad) => ad - case df => - - // TODO should use proper error handler - log("The derived module had meta theory: " + dm.meta) - val rules = RuleSet.collectRules(controller, Context(dm.meta.get)).get(classOf[ComputationRule]).mkString(", ") - log("The used rules were " + rules) - throw LocalError("definiens did not normalize into a flat diagram: " + controller.presenter.asString(df)) - } - - /* defines the name of the generated modules */ - def labelToName(l: LocalName) = LocalName(dm.name.toPath + "_" + l.toPath) - - def labelToPath(l: LocalName) = l match { - case Common.ExistingName(p) => p - case _ => dm.parent ? labelToName(l) - } - - var oldNew: List[(LocalName, LocalName)] = Nil - val modules = ad.getElements.mapOrSkip { e => - e.label match { - case Common.ExistingName(_) => throw SkipThis - case _ => - } - val path = labelToPath(e.label) - val name = path.name - oldNew ::= (e.label, LocalName(path)) - log("diagram definition generates " + e.toStr(true)) - e match { - case node: DiagramNode => - val anonThy = node.theory - val df = TermContainer(anonThy.toTerm) - val thy = Theory(dm.parent, name, anonThy.mt, df = df) - thy - case arrow: DiagramArrow => - val anonMorph = arrow.morphism - val df = anonMorph.toTerm - val vw = View(dm.parent, name, OMMOD(labelToPath(arrow.from)), OMMOD(labelToPath(arrow.to)), Some(df), arrow.isImplicit) - vw - } - } - val adP = ad.relabel(l => utils.listmap(oldNew, l).getOrElse(l)) - dm.dfC.normalized = Some(adP.toTerm) - modules - } -} \ No newline at end of file diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/Combine.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/Combine.scala index 233db9e52f..d10a96185b 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/Combine.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/Combine.scala @@ -109,7 +109,9 @@ object ComputeCombine extends ComputationRule(Combine.path) { val impl_arrow = DiagramArrow(Combine.arrowLabel, ad_over.distNode.get, dist_node.label, new AnonymousMorphism(Nil), true) /* This is used in case theories are not built in a tiny-theories way. In this case, we need to generate names for the arrows. */ - val jointDiag: AnonymousDiagram = (Common.prefixLabels(ad1, LocalName("left")) union Common.prefixLabels(ad2, LocalName("right"))) + val jointDiag: AnonymousDiagram = (Common.prefixLabels(ad1, LocalName("left")) ++ Common.prefixLabels(ad2, LocalName("right"))).get + // the [[Option]] above should always be defined due to prefixing which + // prevents name clashes val List(map1, map2) = List(view1, view2).map { v => Common.asSubstitution(v).map { case (o, n) => OML(o, None, Some(n)) } } diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/Common.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/Common.scala index 1d279a0146..51033e796c 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/Common.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/Common.scala @@ -9,7 +9,7 @@ import info.kwarc.mmt.api.checking._ import info.kwarc.mmt.api.modules._ import info.kwarc.mmt.api.objects._ import info.kwarc.mmt.api.symbols._ -import info.kwarc.mmt.moduleexpressions.diagdefinition.DiagramDefinition +import info.kwarc.mmt.moduleexpressions.publication.DiagramPublisher object Combinators { val _path: MPath = ModExp._base ? "Combinators" @@ -191,7 +191,7 @@ object Common { case None => default } Some(at) - case Some(dm: DerivedModule) if dm.feature == DiagramDefinition.feature => + case Some(dm: DerivedModule) if dm.feature == DiagramPublisher.feature => dm.dfC.normalized flatMap { case AnonymousDiagramCombinator(ad) => ad.getDistNode map { n => n.theory } @@ -235,13 +235,32 @@ object Common { } } + /** + * Convert a set of [[Module]]s into an anonymous diagram. + * + * @todo Currently does not support [[View views]] since their (co)domain theories will be duplicates in the resulting + * diagram, possibly with name clashes. + */ + def asAnonymousDiagram(solver: CheckingCallback, modules: Set[Module])(implicit stack: Stack, history: History): AnonymousDiagram = { + assert(modules.forall(_.isInstanceOf[Theory]), "This method does not yet support modules other than theories. Read API doc of it") + val anonDiags = modules.flatMap(module => asAnonymousDiagram(solver, module.toTerm)) + + val diag = AnonymousDiagram( + anonDiags.flatMap(_.nodes).toList, + anonDiags.flatMap(_.arrows).toList, + distNode = None + ) + + diag + } + /** provides the base case of the function that elaborates a diagram expression (in the form of an [[AnonymousDiagram]]) */ def asAnonymousDiagram(solver: CheckingCallback, diag: Term)(implicit stack: Stack, history: History): Option[AnonymousDiagram] = { diag match { // named diagrams case OMMOD(p) => solver.lookup.getO(p) match { - case Some(dm: DerivedModule) if dm.feature == DiagramDefinition.feature => + case Some(dm: DerivedModule) if dm.feature == DiagramPublisher.feature => dm.dfC.normalized flatMap { case AnonymousDiagramCombinator(ad) => Some(ad) diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/DiagramOperator.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/DiagramOperator.scala index 20754fe515..0bd24066e2 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/DiagramOperator.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/DiagramOperator.scala @@ -212,26 +212,22 @@ trait MorphismOperatorFromLinearTheoryOperatorMixin[HelperContextType <: LinearT abstract class FunctorialLinearDiagramOperator[HelperContextType <: LinearTheoryOperatorContext](unaryConstant: UnaryConstantScala) extends DiagramOperator(unaryConstant) with MorphismOperatorFromLinearTheoryOperatorMixin[HelperContextType] { - override def transformDiagram(diag: AnonymousDiagram): OperatorResult[AnonymousDiagram] = { - def permuteLabel(label: LocalName): LocalName = label match { - case _ if diag.distNode.contains(label) => LocalName("pres") - case LocalName(List(ComplexStep(mPath))) => LocalName(mPath.toString) - case _ => label - } + def transformLabel(label: LocalName): LocalName = label + override def transformDiagram(diag: AnonymousDiagram): OperatorResult[AnonymousDiagram] = { assert(!diag.nodes.exists(_.label == LocalName("pres"))) val (newNodes, theoryContexts) = diag.nodes.map(node => transformTheoryAndGetContext(node.theory) match { case TransformedResult((newTheory, ctx)) => - (DiagramNode(permuteLabel(node.label), newTheory), (node.label, ctx)) + (DiagramNode(transformLabel(node.label), newTheory), (node.label, ctx)) case _ => ??? }).unzip match { case (newNodes, theoryContextsAsListsOfPairs) => (newNodes, theoryContextsAsListsOfPairs.toMap) } val newArrows = diag.arrows.map(arrow => { - val transformedDomain = newNodes.find(_.label == permuteLabel(arrow.from)).get.theory - val transformedCodomain = newNodes.find(_.label == permuteLabel(arrow.to)).get.theory + val transformedDomain = newNodes.find(_.label == transformLabel(arrow.from)).get.theory + val transformedCodomain = newNodes.find(_.label == transformLabel(arrow.to)).get.theory transformMorphism( arrow.morphism, @@ -239,15 +235,15 @@ abstract class FunctorialLinearDiagramOperator[HelperContextType <: LinearTheory transformedCodomain, theoryContexts(arrow.to) ) match { case TransformedResult(newMorphism) => arrow.copy( - label = permuteLabel(arrow.label), + label = transformLabel(arrow.label), morphism = newMorphism, - from = permuteLabel(arrow.from), - to = permuteLabel(arrow.to) + from = transformLabel(arrow.from), + to = transformLabel(arrow.to) ) case _ => ??? } }) - TransformedResult(AnonymousDiagram(newNodes, newArrows, diag.distNode.map(permuteLabel))) + TransformedResult(AnonymousDiagram(newNodes, newArrows, diag.distNode.map(transformLabel))) } } diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Closure.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Closure.scala new file mode 100644 index 0000000000..ec5894db9b --- /dev/null +++ b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Closure.scala @@ -0,0 +1,45 @@ +package info.kwarc.mmt.moduleexpressions.operators.meta + +import info.kwarc.mmt.api.DPath +import info.kwarc.mmt.api.archives.Archive +import info.kwarc.mmt.api.checking.{CheckingCallback, ComputationRule, History} +import info.kwarc.mmt.api.libraries.Lookup +import info.kwarc.mmt.api.modules.Module +import info.kwarc.mmt.api.objects._ +import info.kwarc.mmt.api.uom._ +import info.kwarc.mmt.api.utils.BreadthFirstSearch +import info.kwarc.mmt.moduleexpressions.operators.{Combinators, Common} + +object DiagramClosure extends FlexaryConstantScala(Combinators._path, "diagram_closure") + +object ComputeDiagramClosure extends ComputationRule(DiagramClosure.path) { + def apply(solver: CheckingCallback)(tm: Term, covered: Boolean)(implicit stack: Stack, history: History): Simplifiability = tm match { + case DiagramClosure(modulePathTerms@_*) => + val modulePaths = modulePathTerms.flatMap { + case OMMOD(modulePath) => List(modulePath) + case _ => + solver.error("Encountered non-module path given to diagram closure operator") + Nil + } + + val modules = modulePaths.map(solver.lookup.getModule) + val closedSetOfModules = getClosureOfModules( + solver.lookup, + modules.toSet, + withinDocuments = modules.map(_.parent).toSet + ) + val anonDiag = Common.asAnonymousDiagram(solver, closedSetOfModules) + + Simplify(anonDiag.toTerm) + } + + private def getClosureOfModules(lookup: Lookup, modules: Set[Module], withinDocuments: Set[DPath]): Set[Module] = { + BreadthFirstSearch.collect(modules.toSeq, module => { + module + .getAllIncludes + .map(_.from) + .filter(path => withinDocuments.contains(path.doc)) + .map(lookup.getModule).toSet + }) + } +} diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/DiagramAccessor.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/DiagramAccessor.scala similarity index 94% rename from src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/DiagramAccessor.scala rename to src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/DiagramAccessor.scala index e2343d128a..ddfb136c75 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/DiagramAccessor.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/DiagramAccessor.scala @@ -1,9 +1,10 @@ -package info.kwarc.mmt.moduleexpressions.operators +package info.kwarc.mmt.moduleexpressions.operators.meta import info.kwarc.mmt.api.checking.{CheckingCallback, ComputationRule, History} import info.kwarc.mmt.api.objects._ import info.kwarc.mmt.api.uom._ import info.kwarc.mmt.api.{GeneralError, LocalName} +import info.kwarc.mmt.moduleexpressions.operators.{Combinators, Common} object DiagramAccessor extends BinaryConstantScala(Combinators._path, "diagram_accessor") diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/DiagramFromFile.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/DiagramFromFile.scala new file mode 100644 index 0000000000..684192e9c4 --- /dev/null +++ b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/DiagramFromFile.scala @@ -0,0 +1,54 @@ +/** + * The "DIAG FROM FILE" operator returning the whole diagram for a given MMT filename. + */ + +package info.kwarc.mmt.moduleexpressions.operators.meta + +import info.kwarc.mmt.api.DPath +import info.kwarc.mmt.api.checking.{CheckingCallback, ComputationRule, History} +import info.kwarc.mmt.api.libraries.Lookup +import info.kwarc.mmt.api.modules.Module +import info.kwarc.mmt.api.objects._ +import info.kwarc.mmt.api.uom._ +import info.kwarc.mmt.api.utils.URI +import info.kwarc.mmt.lf.Strings +import info.kwarc.mmt.moduleexpressions.operators.{Combinators, Common} + +import scala.io.Source + +object DiagramFromFile extends UnaryConstantScala(Combinators._path, "diagram_from_file") + +/** + * @todo The "DIAG FROM FILE" operator is implemented in a very hacky way currently as there is currently + * no other way to get all modules declared in a file from within a [[ComputationRule]]. + * An API addition needs to be discussed. + */ +object ComputeDiagramFromFile extends ComputationRule(DiagramFromFile.path) { + def apply(solver: CheckingCallback)(tm: Term, covered: Boolean)(implicit stack: Stack, history: History): Simplifiability = tm match { + case DiagramFromFile(Strings(file)) => + val modules = getModulesForFile(file, solver.lookup) + Simplify(Common.asAnonymousDiagram(solver, modules).toTerm) + + case _ => + RecurseOnly(List(1)) + } + + private def getModulesForFile(file: String, lookup: Lookup): Set[Module] = { + val bufferedSource = Source.fromFile(file) + val surfaceSyntax = bufferedSource.mkString + bufferedSource.close() + + val namespace = { + raw"namespace\s+([^❚ ]+)".r.findFirstMatchIn(surfaceSyntax).get.group(1) + } + val theories = { + val theoryNameRegex = raw"theory\s+([^ =]+)".r + + theoryNameRegex.findAllMatchIn(surfaceSyntax).map(_.group(1)) + } + + val modulePaths = theories.map(DPath(URI(namespace)) ? _) + + modulePaths.flatMap(lookup.getO(_).map(_.asInstanceOf[Module])).toSet + } +} \ No newline at end of file diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Difference.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Difference.scala new file mode 100644 index 0000000000..811ca722b6 --- /dev/null +++ b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Difference.scala @@ -0,0 +1,46 @@ +package info.kwarc.mmt.moduleexpressions.operators.meta + +import info.kwarc.mmt.api.LocalName +import info.kwarc.mmt.api.checking.{CheckingCallback, ComputationRule, History} +import info.kwarc.mmt.api.objects._ +import info.kwarc.mmt.api.uom._ +import info.kwarc.mmt.lf.Strings +import info.kwarc.mmt.moduleexpressions.operators.{Combinators, Common} + +object DiagramDifferenceByDiagram extends BinaryConstantScala(Combinators._path, "diagram_difference_by_diagram") + +object DiagramDifferenceByLabel extends BinaryConstantScala(Combinators._path, "diagram_difference_by_label") + +object Blah { + def doIt(solver: CheckingCallback, terms: Seq[Term], fun: List[AnonymousDiagram] => Simplifiability)(implicit stack: Stack, history: History): Simplifiability = { + val potentialDiags = terms.map(Common.asAnonymousDiagram(solver, _)).toList + + val noneIndices = potentialDiags.zipWithIndex.collect { case (None, idx) => idx } + if (noneIndices.nonEmpty) { + RecurseOnly(noneIndices) + } else { + fun(potentialDiags.map(_.get)) + } + } +} + +object ComputeDiagramDifferenceByDiagram extends ComputationRule(DiagramDifferenceByDiagram.path) { + def apply(solver: CheckingCallback)(tm: Term, covered: Boolean)(implicit stack: Stack, history: History): Simplifiability = tm match { + case DiagramDifferenceByDiagram(diagTerm, diagToSubtractTerm) => + Blah.doIt(solver, List(diagTerm, diagToSubtractTerm), { + case List(diag, diagToSubtract) => + Simplify((diag - diagToSubtract).toTerm) + }) + } +} + +object ComputeDiagramDifferenceByLabel extends ComputationRule(DiagramDifferenceByLabel.path) { + def apply(solver: CheckingCallback)(tm: Term, covered: Boolean)(implicit stack: Stack, history: History): Simplifiability = tm match { + case DiagramDifferenceByLabel(diagTerm, Strings(label)) => + Blah.doIt(solver, List(diagTerm), { + case List(diag) => Simplify((diag - LocalName(label)).toTerm) + }) + + case DiagramDifferenceByLabel(_, _) => RecurseOnly(List(2)) + } +} \ No newline at end of file diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/Empty.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Empty.scala similarity index 84% rename from src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/Empty.scala rename to src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Empty.scala index cba3dd6f03..47f5311982 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/Empty.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Empty.scala @@ -1,9 +1,10 @@ -package info.kwarc.mmt.moduleexpressions.operators +package info.kwarc.mmt.moduleexpressions.operators.meta import info.kwarc.mmt.api.LocalName import info.kwarc.mmt.api.checking.{CheckingCallback, ComputationRule, History} -import info.kwarc.mmt.api.objects.{AnonymousDiagram, AnonymousTheory, DiagramNode, OMMOD, OMS, Stack, Term} +import info.kwarc.mmt.api.objects._ import info.kwarc.mmt.api.uom.{Recurse, Simplifiability, Simplify, UnaryConstantScala} +import info.kwarc.mmt.moduleexpressions.operators.Combinators /** operator for the empty theory of a given meta-theory */ object Empty extends UnaryConstantScala(Combinators._path, "empty") { diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/Extends.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Extends.scala similarity index 92% rename from src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/Extends.scala rename to src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Extends.scala index 2ffaac9e3f..eb7182ba8a 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/Extends.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Extends.scala @@ -1,10 +1,11 @@ -package info.kwarc.mmt.moduleexpressions.operators +package info.kwarc.mmt.moduleexpressions.operators.meta import info.kwarc.mmt.api.LocalName import info.kwarc.mmt.api.checking.{CheckingCallback, ComputationRule, History} -import info.kwarc.mmt.api.objects.{AnonymousDiagram, AnonymousMorphism, AnonymousTheory, DiagramArrow, DiagramNode, OML, OMLList, Stack, Term} +import info.kwarc.mmt.api.objects._ import info.kwarc.mmt.api.symbols.OMSReplacer import info.kwarc.mmt.api.uom.{FlexaryConstantScala, RecurseOnly, Simplifiability, Simplify} +import info.kwarc.mmt.moduleexpressions.operators.{Combinators, Common} object Extends extends FlexaryConstantScala(Combinators._path, "extends") { /** the label of the distinguished node after extension */ diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Prefix.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Prefix.scala new file mode 100644 index 0000000000..b3ac890e66 --- /dev/null +++ b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Prefix.scala @@ -0,0 +1,22 @@ +package info.kwarc.mmt.moduleexpressions.operators.meta + +import info.kwarc.mmt.api.checking.{CheckingCallback, ComputationRule, History} +import info.kwarc.mmt.api.objects._ +import info.kwarc.mmt.api.uom._ +import info.kwarc.mmt.lf.Strings +import info.kwarc.mmt.moduleexpressions.operators.Combinators + +object DiagramPrefix extends BinaryConstantScala(Combinators._path, "diagram_prefix") + +object ComputePrefixedDiagram extends ComputationRule(DiagramPrefix.path) { + def apply(solver: CheckingCallback)(tm: Term, covered: Boolean)(implicit stack: Stack, history: History): Simplifiability = tm match { + case DiagramPrefix(diagTerm, Strings(prefix)) => + Blah.doIt(solver, List(diagTerm), { + case List(diag) => + val prefixedDiagram = diag.relabel(_.prefixOrCreateLastSimpleStep(prefix)) + Simplify(prefixedDiagram.toTerm) + }) + + case DiagramPrefix(_, _) => RecurseOnly(List(2)) + } +} diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Union.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Union.scala new file mode 100644 index 0000000000..5bee89ae7a --- /dev/null +++ b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/operators/meta/Union.scala @@ -0,0 +1,26 @@ +package info.kwarc.mmt.moduleexpressions.operators.meta + +import info.kwarc.mmt.api.checking.{CheckingCallback, ComputationRule, History} +import info.kwarc.mmt.api.objects._ +import info.kwarc.mmt.api.uom._ +import info.kwarc.mmt.api.uom.Simplifiability.NoRecurse +import info.kwarc.mmt.moduleexpressions.operators.{Combinators, Common} + +object DiagramUnion extends FlexaryConstantScala(Combinators._path, "diagram_union") + +object ComputeDiagramUnion extends ComputationRule(DiagramUnion.path) { + def apply(solver: CheckingCallback)(tm: Term, covered: Boolean)(implicit stack: Stack, history: History): Simplifiability = tm match { + case DiagramUnion(terms @ _*) if terms.nonEmpty => + Blah.doIt(solver, terms, diags => { + AnonymousDiagram.union(diags : _*) match { + case Some(unionDiag) => Simplify(unionDiag.toTerm) + case _ => + solver.error("Refusing to union given diagrams due to name clashes") + NoRecurse + } + }) + + case DiagramUnion() => + NoRecurse + } +} \ No newline at end of file diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/publication/DiagramPublisher.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/publication/DiagramPublisher.scala new file mode 100644 index 0000000000..881019b723 --- /dev/null +++ b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/publication/DiagramPublisher.scala @@ -0,0 +1,83 @@ +package info.kwarc.mmt.moduleexpressions.publication + +import info.kwarc.mmt.api._ +import info.kwarc.mmt.api.checking._ +import info.kwarc.mmt.api.modules._ +import info.kwarc.mmt.api.notations.Marker +import info.kwarc.mmt.api.objects._ +import info.kwarc.mmt.api.symbols._ +import info.kwarc.mmt.api.uom.SimplificationUnit + +import scala.collection.mutable + +object DiagramPublisher { + /** + * @todo Eventually rename the structural feature for diagrams to "publish diagram" + */ + val feature = "diagram" +} + +/** + * Module-level structural feature for publishing diagrams into the current document namespace. + * + * @example + * ''' + * theory Empty = ❚ + * diagram Collection := ?Empty EXTEND { coll : type ⟶ type } ❚ + * ''' + * + * It expects an [[AnonymousDiagramCombinator]] as its (normalized) definiens and then + * publishes that diagram in the document namespace in which the structural feature itself is used.n + */ +class DiagramPublisher extends ModuleLevelFeature(DiagramPublisher.feature) { + override def getHeaderNotation: List[Marker] = Nil + + /** */ + def check(dm: DerivedModule)(implicit env: ExtendedCheckingEnvironment): Unit = {} + + override def modules(dm: DerivedModule): List[Module] = dm.dfC.normalized match { + case None => + throw LocalError("no definiens found for " + dm.path) + case Some(df) => + val simplificationUnit = SimplificationUnit(dm.getInnerContext, expandDefinitions = true, fullRecursion = true, solverO = None) + + controller.simplifier(df, simplificationUnit) match { + case AnonymousDiagramCombinator(anonDiag) => + getModulesForAnonymousDiagram(dm, dm.parent, anonDiag) + + case anyOtherSimplifiedDf => + // TODO should use proper error handler + log("The derived module had meta theory: " + dm.meta) + val rules = RuleSet.collectRules(controller, Context(dm.meta.get)).get(classOf[ComputationRule]).mkString(", ") + log("The used rules were " + rules) + throw LocalError("definiens did not normalize into a flat diagram: " + controller.presenter.asString(anyOtherSimplifiedDf)) + } + } + + private def getModulesForAnonymousDiagram(dm: DerivedModule, outerDocumentPath: DPath, anonDiag: AnonymousDiagram): List[Module] = { + // Export the diagram elements to document namespace surrounding the derived declaration. + val newNames: mutable.Map[LocalName, MPath] = mutable.HashMap() + + def labeller(diagElementName: LocalName): MPath = newNames.getOrElseUpdate(diagElementName, { + val supposedlyNewName = diagElementName.last match { + case SimpleStep(name) => outerDocumentPath ? name + case ComplexStep(path) => labeller(path.name) + } + + controller.getO(supposedlyNewName) match { + case None => + supposedlyNewName + case Some(_) => + throw LocalError(s"DiagramDefinition structural feature: cannot export diagram element with name ${diagElementName} to outer namespace due to name clash with pre-existing module therein") + } + }) + + val modules = anonDiag.toModules(labeller) + // TODO: Ask Florian why this is necessary + dm.dfC.normalized = Some(anonDiag.relabel(labeller(_).name).toTerm) + // A semantically equivalent line was previously in his source code here before + // I refactored. + + modules + } +} \ No newline at end of file diff --git a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/diagdefinition/InstanceElaborator.scala b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/publication/InstanceElaborator.scala similarity index 98% rename from src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/diagdefinition/InstanceElaborator.scala rename to src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/publication/InstanceElaborator.scala index 41bc7f8f2f..9c49b2510f 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/diagdefinition/InstanceElaborator.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/moduleexpressions/publication/InstanceElaborator.scala @@ -1,4 +1,4 @@ -package info.kwarc.mmt.moduleexpressions +package info.kwarc.mmt.moduleexpressions.publication import info.kwarc.mmt.api._ import objects._ diff --git a/src/mmt-lf/src/info/kwarc/mmt/sql/Syntax.scala b/src/mmt-lf/src/info/kwarc/mmt/sql/Syntax.scala index 2854f98371..6719680a1e 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/sql/Syntax.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/sql/Syntax.scala @@ -64,7 +64,7 @@ object SQLSyntax { def apply(u: U) = Value(this, u) } case object IntType extends BaseType[Int]("Int") - case object BoolType extends BaseType[java.lang.Boolean]("Boolean") + case object BoolType extends BaseType[Boolean]("Boolean") case object StringType extends BaseType[String]("String") case object FloatType extends BaseType[Double]("Double") case object UUIDType extends BaseType[java.util.UUID]("UUID") diff --git a/src/mmt-lf/src/info/kwarc/mmt/test/LFTest.scala b/src/mmt-lf/src/info/kwarc/mmt/test/LFTest.scala index fff623aaf6..731c68f63a 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/test/LFTest.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/test/LFTest.scala @@ -21,27 +21,67 @@ object LFTest extends MMTIntegrationTest( } } -/* -class MitMTest extends MMTTest("MMT/LFX","MitM/Foundation","MitM/smglom")("info.kwarc.mmt.lf.Plugin") { - behavior of "MitM" - // shouldhl("build MitM/Foundation mmt-omdoc") +object Latin2Test extends MMTIntegrationTest( + "MMT/urtheories","MMT/LATIN2" +)( + ExtensionSpec("info.kwarc.mmt.lf.Plugin") +) { + def main() : Unit = { - // shouldcheck("MitM/smglom",Orders.mitmsmglom:_*)() + } } -*/ object Orders { - val mitmcore = List( - "properties/functions.mmt" - ,"properties/relations.mmt" - ,"arithmetics/naturals.mmt" - ,"arithmetics/integers.mmt" - ,"arithmetics/rationals.mmt" - ,"arithmetics/reals.mmt" - ,"arithmetics/complex.mmt" - ,"graphs/graphs.mmt" + val latin2 = List( + "fundamentals/concepts.mmt", + "fundamentals/relations.mmt", + "fundamentals/equality.mmt", + "fundamentals/subtyping.mmt", + "logic/operators.mmt", + "logic/pl.mmt", + "logic/sfol.mmt", + "logic/booleans.mmt", + "logic/pl-sfol.mmt", + "logic/fol.mmt", + "logic/pl-tableaux.mmt", + "logic/metalogic.mmt", + "logic/pl-views.mmt", + "logic/pl_hilbert.mmt", + "logic/fol_hilbert.mmt", + "logic/drt.mmt", + "type_theory/operators.mmt", + "type_theory/function_types.mmt", + "type_theory/power_types.mmt", + "logic/hol.mmt", + "logic/modal.mmt", + "logic/multimodal.mmt", + "logic/dynamic.mmt", + "logic/hol_andrews.mmt", + "type_theory/type_equality.mmt", + "type_theory/type_erasure.mmt", + "type_theory/product_types.mmt", + "type_theory/category_theory.mmt", + "type_theory/congruence_types.mmt", + "type_theory/predicate_subtypes.mmt", + "type_theory/undefinedness.mmt", + "type_theory/quotation.mmt", + "type_theory/endofunctors/endofunctors.mmt", + "type_theory/endofunctors/endomagmas.mmt", + // "type_theory/endofunctors/monads.mmt", // TODO doesn't work + "type_theory/collection_types.mmt", + "set_theory/features.mmt", + "curry-howard/concepts.mmt", + "topology/topology.mmt", + "topology/topology_morph.mmt", + "proving/fundamentals.mmt", + "numbers/nat.mmt", + // "numbers/nat-induct.mmt", // TODO doesn't work + //"algebra/sets.mmt", + //"algebra/relations.mmt", + "algebra/magmas.mmt", + // "algebra/additive_magmas.mmt", // TODO doesn't work ) - val mitmsmglom = List( + val mitmcore = List( "arithmetics/naturals.mmt" ,"arithmetics/integers.mmt" ,"arithmetics/rationals.mmt" @@ -96,6 +136,9 @@ object Orders { ,"calculus/integration/LebesgueIntegral.mmt" ,"categories/categories.mmt" ,"categories/functors.mmt" + ,"geometry/common.mmt" + ,"geometry/planar.mmt" + ,"geometry/3D.mmt" ) val testgeneral = List( @@ -123,7 +166,7 @@ object Orders { ,"program.mmt" // ,"quantities.mmt" TODO broke the tests //,"sequences.mmt" TODO doesn't type check - ,"set.mmt" + //,"set.mmt" TODO doesn't type check ,"shallow_polymorphism.mmt" ,"sigma.mmt" // ,"IFIP21_tutorial.mmt" TODO broke the tests diff --git a/src/mmt-lf/src/info/kwarc/mmt/test/SvenProverTest.scala b/src/mmt-lf/src/info/kwarc/mmt/test/SvenProverTest.scala index 750c36808b..a95b6b360c 100644 --- a/src/mmt-lf/src/info/kwarc/mmt/test/SvenProverTest.scala +++ b/src/mmt-lf/src/info/kwarc/mmt/test/SvenProverTest.scala @@ -4,10 +4,9 @@ import info.kwarc.mmt.api._ import modules._ import symbols._ import objects._ -import proving.imperative._ import info.kwarc.mmt.lf._ - +/* object SvenProverTest extends MMTIntegrationTest("MMT/urtheories", "MMT/examples")() { def main { val thyURI = Path.parse("NAMESPACE?THYNAME") @@ -24,3 +23,4 @@ object SvenProverTest extends MMTIntegrationTest("MMT/urtheories", "MMT/examples } } } +*/ \ No newline at end of file diff --git a/src/mmt-lf/test/scala/info/kwarc/mmt/api/presentation/MMTSyntaxPresenterTest.scala b/src/mmt-lf/test/scala/info/kwarc/mmt/api/presentation/MMTSyntaxPresenterTest.scala new file mode 100644 index 0000000000..cb43c9baec --- /dev/null +++ b/src/mmt-lf/test/scala/info/kwarc/mmt/api/presentation/MMTSyntaxPresenterTest.scala @@ -0,0 +1,260 @@ +package info.kwarc.mmt.api.presentation + +import info.kwarc.mmt.api.documents.{Document, MRef} +import info.kwarc.mmt.api.frontend.Controller +import info.kwarc.mmt.api.metadata.MetaDatum +import info.kwarc.mmt.api.modules.{Theory, View} +import info.kwarc.mmt.api.notations._ +import info.kwarc.mmt.api.objects.{OMA, OMID, OMMOD, OMV, Term} +import info.kwarc.mmt.api.presentation.MMTSyntaxPresenterTest.controller +import info.kwarc.mmt.api.symbols.{Constant, FinalConstant, Structure, TermContainer, Visibility} +import info.kwarc.mmt.api.utils.URI +import info.kwarc.mmt.api.{ComponentParent, DPath, LocalName, NarrativeElement, StructuralElement, presentation} +import info.kwarc.mmt.lf.{ApplySpine, FunTerm, FunType} +import info.kwarc.mmt.test.MMTUnitTest + +import scala.xml.Node + +/** + * Unit test for [[MMTSyntaxPresenter]] + * + * It is located here in the mmt-lf module since we make use of LF function types and lambdas in the sample terms we test. + */ +object MMTSyntaxPresenterTest extends MMTUnitTest { + override def main(): Unit = { + val presenter = new MMTSyntaxPresenter() + presenter.init(controller) + + richAnnotatedConstantTest()(controller, presenter) + structureTest()(controller, presenter) + allLevelMetaTest()(controller, presenter) + } + + private def richAnnotatedConstantTest()(implicit ctrl: Controller, presenter: MMTSyntaxPresenter): Unit = { + val (thy, otherThy, c) = MMTSyntaxPresenterTestFixtures.createRichAnnotatedConstantTest() + val str = presenter.presentToString(c) + + log("Testing presentation of constant with type, definiens, metadata, and much more:") + logGroup { + log("Presented constant is:\n" + str) + + assertEqual(8, str.split("❘").length, "delimiter count of ❘ is unexpected") + + assertEqual(1, "❘\\s*role simplify\\s*[❘|❙]".r.findAllIn(str).length, "role incorrect") + assertEqual(1, "❘\\s*@ jud\\s*[❘|❙]".r.findAllIn(str).length, "alias incorrect") + assertEqual(1, "❘\\s*@ judgment\\s*[❘|❙]".r.findAllIn(str).length, "alias incorrect") + assertEqual(1, "❘\\s*# 1 \\|- 2 ∶ 3 prec 100\\s*[❘|❙]".r.findAllIn(str).length, "notation incorrect") + assertEqual(1, "❘\\s*meta metakey1 \\?S\\s*[❘|❙]".r.findAllIn(str).length, "meta datum incorrect") + assertEqual(1, "❘\\s*meta metakey2 \\(f\\s+x\\)\\s*[❘|❙]".r.findAllIn(str).length, "meta datum incorrect") + } + + controller.delete(thy.path) + controller.delete(otherThy.path) + } + + private def structureTest()(implicit ctrl: Controller, presenter: MMTSyntaxPresenter): Unit = { + val (innerThy, outerThy) = MMTSyntaxPresenterTestFixtures.createStructureTest() + val str = presenter.presentToString(outerThy) + + log("Testing presentation of structures and metadata referring to constants within those structures in form of meta keys and meta values:") + logGroup { + log("Presented outerThy is:\n" + str) + + assertEqual(1, "structure s1\\s+:\\s+[a-zA-Z.:/]+\\?InnerTheory\\s+❙".r.findAllIn(str).length, "structure declaration incorrect") + assertEqual(1, "structure s2\\s+:\\s+[a-zA-Z.:/]+\\?InnerTheory\\s+❙".r.findAllIn(str).length, "structure declaration incorrect") + assertEqual(1, "e\\s+meta s1/c s1/d\\s*?❘\\s*?meta s2/c s2/d".r.findAllIn(str).length, "meta datum incorrect") + } + + controller.delete(innerThy.path) + controller.delete(outerThy.path) + } + + private def allLevelMetaTest()(implicit ctrl: Controller, presenter: MMTSyntaxPresenter): Unit = { + val doc :: _ = MMTSyntaxPresenterTestFixtures.createAllLevelMetaTest() + log("Testing presentation of meta data across all levels (documents, modules, declarations):") + logGroup { + log(presenter.presentToString(doc)) + } + } +} + +object MMTSyntaxPresenterTestFixtures { + private val baseDocument: DPath = DPath(URI("https://MMTSyntaxPresenterTest.MMTUnitTest.test.mmt.kwarc.infoexample.com")) + + private def untypedConstant(home: Term, name: LocalName): FinalConstant = new FinalConstant( + home, + name, + alias = Nil, + tpC = TermContainer.empty(), + dfC = TermContainer.empty(), + rl = None, + notC = NotationContainer.empty(), + vs = Visibility.public + ) + + /** + * Builds the following theories and constants: + * + * {{{ + * theory T = + * judgement + * : {V:vocabulary} {Γ:Ctx V} Expr Γ ⟶ (Expr Γ ⟶ prop) + * ❘ = [V:vocabulary] [Γ:Ctx V] [e:Expr Γ] [E:Expr Γ] foo bar + * ❘ role simplify + * ❘ @ jud + * ❘ @ judgment + * ❘ # 1 |- 2 ∶ 3 prec 100 + * ❘ meta metakey1 ?S + * ❘ meta metakey2 (f x) + * ❙ + * ❚ + * + * theory S = ❚ + * }}} + * + * @return (T, S, judgement), all of them already added to the controller + */ + def createRichAnnotatedConstantTest()(implicit ctrl: Controller): (Theory, Theory, Constant) = { + val theoryPath = (baseDocument / "createRichAnnotatedConstant") ? "T" + val otherTheoryPath = theoryPath.parent ? "S" + + val notationContainer = NotationContainer(TextNotation( + fixity = Mixfix(List(SimpArg(1), Delim("|-"), SimpArg(2), Delim("∶"), SimpArg(3))), + precedence = Precedence.integer(100), + meta = None + )) + + val tpC = TermContainer.asParsed(FunType( + in = List( + (Some(LocalName("V")), OMID(theoryPath ? "vocabulary")), + (Some(LocalName("Γ")), ApplySpine(OMID(theoryPath ? "Ctx"), OMV("V"))), + (None, ApplySpine(OMID(theoryPath ? "Expr"), OMV("Γ"))), + (None, ApplySpine(OMID(theoryPath ? "Expr"), OMV("Γ"))) + ), + out = OMID(theoryPath ? "prop") + )) + + val dfC = TermContainer.asParsed(FunTerm( + in = List( + (LocalName("V"), OMID(theoryPath ? "vocabulary")), + (LocalName("Γ"), ApplySpine(OMID(theoryPath ? "Ctx"), OMV("V"))), + (LocalName("e"), ApplySpine(OMID(theoryPath ? "Expr"), OMV("Γ"))), + (LocalName("E"), ApplySpine(OMID(theoryPath ? "Expr"), OMV("Γ"))) + ), + out = ApplySpine(OMID(theoryPath ? "foo"), OMID(theoryPath ? "bar")) + )) + + val c = new FinalConstant( + home = OMMOD(theoryPath), + name = LocalName("judgement"), + alias = List(LocalName("jud"), LocalName("judgment")), + tpC = tpC, + dfC = dfC, + rl = Some("simplify"), + notC = notationContainer, + vs = Visibility.public + ) + + val thy = Theory.empty(theoryPath.parent, theoryPath.name, mt = None) + + c.metadata.add( + MetaDatum(theoryPath ? "metakey1", OMMOD(otherTheoryPath)) + ) + c.metadata.add( + MetaDatum( + theoryPath ? "metakey2", + OMA( + OMID(theoryPath ? "f"), + List(OMID(theoryPath ? "x")) + ) + ) + ) + + val otherThy = Theory.empty(otherTheoryPath.parent, otherTheoryPath.name, mt = None) + + List(thy, c, otherThy).foreach(ctrl.add(_)) + + (thy, otherThy, c) + } + + + /* + Builds the following theories: + + {{{ + theory MMTSyntaxPresenterTestStructure = + c ❙ + d ❙ + ❚ + + theory MMTSyntaxPresenterTestStructure = + structure s1 : ?Test = ❚ + structure s2 : ?Test = ❚ + + e ❘ meta s1/c s1/d ❘ meta s2/c s2/d ❙ + ❚ + }}} + */ + def createStructureTest()(implicit ctrl: Controller): (Theory, Theory) = { + val docPath = DPath(URI("https://MMTSyntaxPresenterTest.MMTUnitTest.test.mmt.kwarc.infoexample.com")) / "structuretest" + + val innerTheory: Theory = { + val innerTheory = Theory.empty(docPath, LocalName("InnerTheory"), mt = None) + val c = untypedConstant(innerTheory.toTerm, LocalName("c")) + val d = untypedConstant(innerTheory.toTerm, LocalName("d")) + + List(innerTheory, c, d).foreach(ctrl.add(_)) + innerTheory + } + + val outerTheory: Theory = { + val outerTheory = Theory.empty(docPath, LocalName("OuterTheory"), mt = None) + val s1 = Structure(outerTheory.toTerm, LocalName("s1"), innerTheory.toTerm, df = None, isImplicit = false, isTotal = true) + val s2 = Structure(outerTheory.toTerm, LocalName("s2"), innerTheory.toTerm, df = None, isImplicit = false, isTotal = true) + val e = untypedConstant(outerTheory.toTerm, LocalName("e")) + e.metadata.add(MetaDatum( + s1.path / LocalName("c"), + OMID(s1.path / LocalName("d")) + )) + e.metadata.add(MetaDatum( + s2.path / LocalName("c"), + OMID(s2.path / LocalName("d")) + )) + + List(outerTheory, s1, s2, e).foreach(ctrl.add(_)) + outerTheory + } + + (innerTheory, outerTheory) + } + + def createAllLevelMetaTest()(implicit ctrl: Controller): List[StructuralElement] = { + val doc = new Document(baseDocument / "allLevelMetaTest") + + val metakey1 = doc.path ? "metatheory" ? "symbol1" + val metavalue1 = OMID(doc.path ? "metatheory" ? "value1") + + val metakey2 = doc.path ? "metatheory" ? "symbol2" + val metavalue2 = OMID(doc.path ? "metatheory" ? "value2") + + val metadata = List(MetaDatum(metakey1, metavalue1), MetaDatum(metakey2, metavalue2)) + + doc.metadata.add(metadata : _*) + val thy = Theory.empty(doc.path, LocalName("thy"), mt = None) + thy.metadata.add(metadata : _*) + + val c = untypedConstant(thy.toTerm, LocalName("c")) + c.metadata.add(metadata : _*) + + val view = View(doc.path, LocalName("vw"), from = thy.toTerm, to = thy.toTerm, isImplicit = false) + view.metadata.add(metadata : _*) + + val structuralThings = List(doc, thy, c, view) + structuralThings.foreach(ctrl.add(_)) + + ctrl.add(MRef(doc.path, thy.path)) + ctrl.add(MRef(doc.path, view.path)) + + structuralThings + } +} diff --git a/src/mmt-odk/src/info/kwarc/mmt/odk/ODKTest.scala b/src/mmt-odk/src/info/kwarc/mmt/odk/ODKTest.scala index 1eccf3541e..c8d99f923f 100644 --- a/src/mmt-odk/src/info/kwarc/mmt/odk/ODKTest.scala +++ b/src/mmt-odk/src/info/kwarc/mmt/odk/ODKTest.scala @@ -33,7 +33,7 @@ object MitMTest extends MMTIntegrationTest( "MMT/urtheories", "MMT/LFX", "MitM/Foundation", - "MitM/smglom" + "MitM/Core" )( ExtensionSpec("info.kwarc.mmt.lf.Plugin"), ExtensionSpec("info.kwarc.mmt.odk.Plugin") @@ -50,7 +50,32 @@ object MitMTest extends MMTIntegrationTest( logGroup { shouldHandleLine("log+ structure-checker-simple") - shouldCheck("MitM/smglom",Orders.mitmsmglom:_*)(onlyfiles = true) + shouldCheck("MitM/Core",Orders.mitmcore:_*)(onlyfiles = true) + } + } +} + +object LATIN2Test extends MMTIntegrationTest( + "MMT/urtheories", + "MMT/LFX", + "MMT/LATIN2" +)( + ExtensionSpec("info.kwarc.mmt.lf.Plugin"), + ExtensionSpec("info.kwarc.mmt.odk.Plugin") +){ + def main(): Unit = { + //shouldClearTarget("MMT/urtheories", "bin") + //shouldHandleLine("build MMT/urtheories scala-bin") + + //shouldClearTarget("MMT/LFX", "bin") + //shouldHandleLine("build MMT/LFX scala-bin") + + shouldClearTarget("MMT/LATIN2", "bin") + shouldHandleLine("build MMT/LATIN2 scala-bin") + + logGroup { + shouldHandleLine("log+ structure-checker-simple") + shouldCheck("MMT/LATIN2",Orders.latin2:_*)(onlyfiles = true) } } } \ No newline at end of file diff --git a/src/mmt-odk/src/info/kwarc/mmt/odk/Singular/Importer.scala b/src/mmt-odk/src/info/kwarc/mmt/odk/Singular/Importer.scala index 673c2b9108..6c046f4568 100644 --- a/src/mmt-odk/src/info/kwarc/mmt/odk/Singular/Importer.scala +++ b/src/mmt-odk/src/info/kwarc/mmt/odk/Singular/Importer.scala @@ -73,7 +73,7 @@ class SingularImporter extends Importer { case JSONString(s) => s case JSONBoolean(false) => return None }, - jo.getAs(classOf[java.lang.Boolean],"is_global"), + jo.getAs(classOf[Boolean],"is_global"), jo.getAsString("doc_string_plain"), jo.apply("guessed_output_type").map(_.asInstanceOf[JSONString].value) ) diff --git a/src/mmt-odk/src/info/kwarc/mmt/odk/codecs/BaseTypes.scala b/src/mmt-odk/src/info/kwarc/mmt/odk/codecs/BaseTypes.scala index 85482f94b6..89270cbb8c 100644 --- a/src/mmt-odk/src/info/kwarc/mmt/odk/codecs/BaseTypes.scala +++ b/src/mmt-odk/src/info/kwarc/mmt/odk/codecs/BaseTypes.scala @@ -43,9 +43,9 @@ object TMString extends EmbedStringToJSON(new LiteralsAsStringsCodec(Codecs.stan object BoolAsString extends EmbedStringToJSON(new LiteralsAsStringsCodec(Codecs.boolAsString, MitM.BoolLit)) -object BoolAsInt extends LiteralsCodec[java.lang.Boolean,JSON](Codecs.boolAsInt, MitM.BoolLit) { +object BoolAsInt extends LiteralsCodec[scala.Boolean,JSON](Codecs.boolAsInt, MitM.BoolLit) { val codeType = IntType - def encodeRep(b: java.lang.Boolean) = if (b) JSONInt(1) else JSONInt(0) + def encodeRep(b: scala.Boolean) = if (b) JSONInt(1) else JSONInt(0) def decodeRep(j: JSON) = j match { case JSONInt(x) if x.toInt == 1 => true case JSONInt(x) if x.toInt == 0 => false @@ -53,9 +53,9 @@ object BoolAsInt extends LiteralsCodec[java.lang.Boolean,JSON](Codecs.boolAsInt, } } -object StandardBool extends LiteralsCodec[java.lang.Boolean,JSON](Codecs.standardBool, MitM.BoolLit) { +object StandardBool extends LiteralsCodec[scala.Boolean,JSON](Codecs.standardBool, MitM.BoolLit) { val codeType = BooleanType - def encodeRep(b: java.lang.Boolean) = JSONBoolean(b) + def encodeRep(b: scala.Boolean) = JSONBoolean(b) def decodeRep(j: JSON) = j match { case JSONBoolean(b) => b case _ => throw CodecNotApplicable diff --git a/src/mmt-pvs/src/info/kwarc/mmt/pvs/Plugin.scala b/src/mmt-pvs/src/info/kwarc/mmt/pvs/Plugin.scala index 0bb63f5538..b85345c401 100644 --- a/src/mmt-pvs/src/info/kwarc/mmt/pvs/Plugin.scala +++ b/src/mmt-pvs/src/info/kwarc/mmt/pvs/Plugin.scala @@ -42,7 +42,7 @@ object PVSNotation extends NotationExtension { case _ => false } /** called to construct a term after a notation produced by this was used for parsing */ - def constructTerm(op: GlobalName, subs: Substitution, con: Context, args: List[Term], attrib: Boolean, not: TextNotation) + def constructTerm(op: GlobalName, subs: Substitution, con: Context, args: List[Term], not: TextNotation) (implicit unknown: () => Term): Term = { require(args.length == 2) ??? @@ -63,7 +63,7 @@ object PVSNotation extends NotationExtension { }) val fixity = Mixfix(List(SimpArg(1),Delim("%w"),delim,Delim("%w"),SimpArg(2))) val notation = TextNotation(fixity,Precedence.integer(0),None) - Some(PragmaticTerm(fun,sub,con,List(a,b),false,notation,Position.positions(OMA(OMS(fun),List(a,b))))) + Some(PragmaticTerm(fun,sub,con,List(a,b),notation,Position.positions(OMA(OMS(fun),List(a,b))))) } } @@ -225,7 +225,7 @@ object NatLitSubtype extends SubtypingRule { } -object PVSHOAS extends NestedHOASNotation(HOAS(pvsapply.path,pvslambda.path,expr.path),LF.hoas) +object PVSHOAS extends NestedHOASNotation(HOAS(pvsapply.path,pvslambda.path),LF.hoas) import PVSTheory._ diff --git a/src/mmt-stex/src/info/kwarc/mmt/stex/LaTeXBuildTarget.scala b/src/mmt-stex/src/info/kwarc/mmt/stex/LaTeXBuildTarget.scala index 58f3ccab15..9412ba7976 100644 --- a/src/mmt-stex/src/info/kwarc/mmt/stex/LaTeXBuildTarget.scala +++ b/src/mmt-stex/src/info/kwarc/mmt/stex/LaTeXBuildTarget.scala @@ -47,7 +47,7 @@ abstract class LaTeXBuildTarget extends TraversingBuildTarget with STeXAnalysis override def start(args: List[String]) { anaStartArgs(args) - pipeOutput = optionsMap.get(pipeOutputOption).isDefined + pipeOutput = optionsMap.contains(pipeOutputOption) optionsMap.get(timeoutOption).foreach(v => timeoutVal = v.getIntVal) optionsMap.get(key).foreach(v => nameOfExecutable = v.getStringVal) optionsMap.get("execute").foreach { v => @@ -78,7 +78,6 @@ abstract class LaTeXBuildTarget extends TraversingBuildTarget with STeXAnalysis "% this file defines root path local repository", "\\defpath{MathHub}{" + a.root.up.up.getPath + "}", "\\mhcurrentrepos{" + groupRepo, - "\\libinput{WApersons}", "% we also set the base URI for the LaTeXML transformation", "\\baseURI[\\MathHub{}]{https://mathhub.info/" + groupRepo ) diff --git a/src/mmt-stex/src/info/kwarc/mmt/stex/LaTeXML.scala b/src/mmt-stex/src/info/kwarc/mmt/stex/LaTeXML.scala index 2e251be986..ee4cf88749 100644 --- a/src/mmt-stex/src/info/kwarc/mmt/stex/LaTeXML.scala +++ b/src/mmt-stex/src/info/kwarc/mmt/stex/LaTeXML.scala @@ -23,7 +23,9 @@ class AllPdf extends LaTeXDirTarget { if (bt.isDir) { val a = bt.archive val ls = getAllFiles(bt).map(f => FileBuildDependency("pdflatex", a, bt.inPath / f)) - BuildSuccess(ls :+ DirBuildDependency("alltex", a, bt.inPath, Nil), Nil) + val at = DirBuildDependency("alltex", a, bt.inPath, Nil) + val lp = DirBuildDependency("localpaths", a, bt.inPath, Nil) + BuildSuccess(ls :+ at :+ lp, Nil) } else BuildResult.empty } @@ -40,6 +42,41 @@ class AllPdf extends LaTeXDirTarget { } } +class LocalPaths extends LaTeXDirTarget { + val key : String = "localpaths" + + override def estimateResult(bt: BuildTask) : BuildSuccess = { + if (bt.isDir) { + val lp : ResourceDependency = PhysicalDependency(File(bt.dirName + "localpaths.tex")) + BuildSuccess(Nil,List(lp)) + } else { BuildResult.empty } + } + + override def buildDir(a: Archive, in: FilePath, dir: File, force: Boolean) : BuildResult = { + val target : File = dir / ("localpaths.tex") + var success : Boolean = false + + /* Only create file if it has a sibling tex file */ + val siblings : Boolean = dir.children.exists(s => s.getExtension.contains("tex") && s.name != "localpaths.tex") + + /* forcing recreation of file means deleting the old one. */ + if (force && target.exists()) { target.delete() } + + if (force || (!target.exists() && siblings)) { + createLocalPaths(a, dir) + success = true + } + + if (success) { BuildSuccess(Nil, List(PhysicalDependency(target))) } + else if (!siblings) { + log("No sibling .tex-file, localpaths.tex not created") + BuildResult.empty + } + else BuildEmpty("up-to-date") + } + +} + class AllTeX extends LaTeXDirTarget { val key: String = "alltex" @@ -48,19 +85,32 @@ class AllTeX extends LaTeXDirTarget { BuildSuccess(Nil, getAllFiles(bt).map(f => PhysicalDependency(bt.inFile / f))) } else { val used = super.estimateResult(bt).used.collect { - case d@FileBuildDependency(k, _, _) if List("tex-deps", "sms").contains(k) => d + case d@FileBuildDependency(k, _, _) if List("tex-deps").contains(k) => d } - BuildSuccess(used, Nil) + val lp = DirBuildDependency("localpaths", bt.archive, bt.inPath, Nil) + BuildSuccess(used :+ lp, Nil) } } + def forgetSMSDeps(in : Map[Dependency, Set[Dependency]]) : Map[Dependency, Set[Dependency]] = + { + def goodDependency(dep : Dependency) : Boolean = dep match { + case fbd @ FileBuildDependency(_,_,_) if (List("tex-deps","alltex").contains(fbd.key)) => true + case PhysicalDependency(_) => true + case _ => false + } + + var clean : Map[Dependency, Set[Dependency]] = in.filter(kv => goodDependency(kv._1)) + for ((k,v) <- clean) { clean = clean + (k -> v.filter(goodDependency)) } + clean + } + def buildDir(a: Archive, in: FilePath, dir: File, force: Boolean): BuildResult = { val dirFiles = getDirFiles(a, dir, includeFile) var success = false if (dirFiles.nonEmpty) { - createLocalPaths(a, dir) - val deps = getDepsMap(getFilesRec(a, in)) - val ds = Relational.flatTopsort(controller, deps) + val deps = forgetSMSDeps(getDepsMap(getFilesRec(a, in))) + val ds : List[Dependency] = Relational.newFlatTopsort(controller,deps) val ts = ds.collect { case bd: FileBuildDependency if List(key, "tex-deps").contains(bd.key) => bd }.map(d => d.archive / inDim / d.inPath) @@ -88,14 +138,14 @@ class AllTeX extends LaTeXDirTarget { val ls = langFiles(lang, files) val w = new StringBuilder def writeln(s: String): Unit = w.append(s + "\n") - ambleText("pre", a, lang).foreach(writeln) + ambleText(preOrPost = "pre", a, lang).foreach(writeln) writeln("") ls.foreach { f => writeln("\\begin{center} \\LARGE File: \\url{" + f + "} \\end{center}") writeln("\\input{" + File(f).stripExtension + "} \\newpage") writeln("") } - ambleText("post", a, lang).foreach(writeln) + ambleText(preOrPost = "post", a, lang).foreach(writeln) val newContent = w.result val outPath = getOutPath(a, all) if (force || !all.exists() || File.read(all) != newContent) { @@ -128,8 +178,13 @@ class SmsGenerator extends LaTeXBuildTarget { override def includeDir(n: String): Boolean = !n.endsWith("tikz") + override def estimateResult(bt: BuildTask): BuildSuccess = { + val BuildSuccess(u, p) = super.estimateResult(bt) + val lp = DirBuildDependency("localpaths", bt.archive, bt.inPath, Nil) + BuildSuccess(u :+ lp,p) + } + def reallyBuildFile(bt: BuildTask): BuildResult = { - createLocalPaths(bt) try { createSms(bt.archive, bt.inFile, bt.outFile) logSuccess(bt.outPath) @@ -161,7 +216,8 @@ class LaTeXML extends LaTeXBuildTarget { override def includeDir(n: String): Boolean = !n.endsWith("tikz") - val outDim = RedirectableDimension("latexml") + val outDim : ArchiveDimension = RedirectableDimension("latexml") + // the latexml client private var latexmlc = "latexmlc" private var latexmls = "latexmls" @@ -224,9 +280,9 @@ class LaTeXML extends LaTeXBuildTarget { controller.getEnvVar("LATEXMLPRELOADS").getOrElse("").split(" ").filter(_.nonEmpty) paths = getStringList(optionsMap, "path") ++ controller.getEnvVar("LATEXMLPATHS").getOrElse("").split(" ").filter(_.nonEmpty) - reboot = optionsMap.get("reboot").isDefined + reboot = optionsMap.contains("reboot") if (reboot) expire = 1 - nopost = optionsMap.get("nopost").isDefined + nopost = optionsMap.contains("nopost") } private def str2Level(lev: String): Level.Level = lev match { @@ -388,7 +444,6 @@ class LaTeXML extends LaTeXBuildTarget { val logFile = bt.outFile.setExtension("ltxlog") lmhOut.delete() logFile.delete() - createLocalPaths(bt) val realProfile = if (profileSet) profile else getProfile(bt.archive).getOrElse(profile) val argSeq = Seq(latexmlc, bt.inFile.toString, @@ -454,6 +509,12 @@ class LaTeXML extends LaTeXBuildTarget { val outDir = getFolderOutFile(a, curr.path).up if (outDir.isDirectory) outDir.deleteDir } + + override def estimateResult(bt: BuildTask): BuildSuccess = { + val BuildSuccess(u, p) = super.estimateResult(bt) + val lp = DirBuildDependency("localpaths", bt.archive, bt.inPath, Nil) + BuildSuccess(u :+ lp,p) + } } /** pdf generation */ @@ -479,10 +540,11 @@ class PdfLatex extends LaTeXBuildTarget { } override def estimateResult(bt: BuildTask): BuildSuccess = { - val bs@BuildSuccess(used, provided) = super.estimateResult(bt) + val BuildSuccess(used, provided) = super.estimateResult(bt) + val lp = DirBuildDependency("localpaths", bt.archive, bt.inPath, Nil) if (bt.inPath.name.startsWith("all.")) { - BuildSuccess(used :+ DirBuildDependency("alltex", bt.archive, bt.inPath.dirPath, Nil), provided) - } else bs + BuildSuccess(used :+ DirBuildDependency("alltex", bt.archive, bt.inPath.dirPath, Nil) :+ lp, provided) + } else BuildSuccess(used :+ lp, provided) } protected def runPdflatex(bt: BuildTask, output: StringBuffer): Int = { @@ -520,7 +582,6 @@ class PdfLatex extends LaTeXBuildTarget { val pdfFile = bt.inFile.setExtension("pdf") pdfFile.delete() bt.outFile.delete() - createLocalPaths(bt) val output = new StringBuffer() try { val exit = runPdflatex(bt, output) @@ -566,6 +627,12 @@ class TikzSvg extends PdfLatex override val outExt : String = "svg" override val outDim : ArchiveDimension = content + override def estimateResult(bt: BuildTask): BuildSuccess = { + val BuildSuccess(u, p) = super.estimateResult(bt) + val lp = DirBuildDependency("localpaths", bt.archive, bt.inPath, Nil) + BuildSuccess(u :+ lp,p) + } + override def includeDir(n: String): Boolean = n.endsWith("tikz") override def reallyBuildFile(bt: BuildTask): BuildResult = @@ -578,7 +645,6 @@ class TikzSvg extends PdfLatex val svgFile : File = bt.outFile bt.outFile.delete() - createLocalPaths(bt) val output = new StringBuffer() try { diff --git a/src/mmt-stex/src/info/kwarc/mmt/stex/STeXImporter.scala b/src/mmt-stex/src/info/kwarc/mmt/stex/STeXImporter.scala index 3f1b201fef..bb2a2101fa 100644 --- a/src/mmt-stex/src/info/kwarc/mmt/stex/STeXImporter.scala +++ b/src/mmt-stex/src/info/kwarc/mmt/stex/STeXImporter.scala @@ -475,7 +475,7 @@ class DocumentImporter(bt:BuildTask,importer:STeXImporter) { val macroMk = Delim("\\" + macro_name) val notArgs = macroMk :: (0 until nrArgs).toList.flatMap(i => Delim("{") :: SimpArg(i + 1) :: Delim("}") :: Nil) val stexScope = NotationScope(None, "stex" :: "tex" :: Nil, 0) - val texNotation = new TextNotation(Mixfix(notArgs), Precedence.integer(0), None, stexScope) + val texNotation = new TextNotation(Mixfix(notArgs), Precedence.integer(0), None, false, stexScope) c.notC.parsingDim.set(texNotation) } catch { case e: Exception => @@ -641,7 +641,7 @@ class DocumentImporter(bt:BuildTask,importer:STeXImporter) { } val languages = "mathml" :: Nil val scope = NotationScope(variant, languages, 0) - val notation = new TextNotation(Mixfix(markers), precedence, None, scope) + val notation = new TextNotation(Mixfix(markers), precedence, None, false, scope) notation } @@ -716,10 +716,10 @@ class DocumentImporter(bt:BuildTask,importer:STeXImporter) { } val prec = Precedence.integer(0) val verbScope = NotationScope(None, sTeX.getLanguage(thy.path).toList, 0) - val not = TextNotation.fromMarkers(prec, None, verbScope)(markers: _*) + val not = TextNotation.fromMarkers(prec, None, false, verbScope)(markers: _*) const.notC.verbalizationDim.set(not) if (hasArgs) { - val notOp = TextNotation.fromMarkers(prec, None, verbScope)(markersOp: _*) + val notOp = TextNotation.fromMarkers(prec, None, false, verbScope)(markersOp: _*) const.notC.verbalizationDim.set(not) } val doc = controller.getDocument(const.parent.parent / (const.parent.name + ".omdoc"), d => "cannot find parent doc for reindexing" + d) @@ -915,7 +915,7 @@ class DocumentImporter(bt:BuildTask,importer:STeXImporter) { val argName = (n \ "@name").text val argNr = if (argMap.isDefinedAt(argName)) argMap(argName) else if ((argName == "args" || argName == "arg") && argMap.isDefinedAt("arg1")) argMap("arg1") else ??? val precO = None //Some(getPrecedence(n)) - val props = CommonMarkerProperties(precO,None) + val props = CommonMarkerProperties.noProps.copy(precedence = precO) val delim = n.child.find(_.label == "separator") match { case None => makeDelim(",") case Some(sep) => diff --git a/src/project/build.properties b/src/project/build.properties index 6adcdc753f..0837f7a132 100644 --- a/src/project/build.properties +++ b/src/project/build.properties @@ -1 +1 @@ -sbt.version=1.3.3 +sbt.version=1.3.13 diff --git a/src/project/prefix.travis.yml b/src/project/prefix.travis.yml deleted file mode 100644 index 666b5fe91d..0000000000 --- a/src/project/prefix.travis.yml +++ /dev/null @@ -1,54 +0,0 @@ -## Any content in this file will be prepended to .travis.yml -## Any lines starting with '##' will be ignored -## =============================================================== -## =============================================================== - -# +===============================================================+ -# |THIS FILE HAS BEEN AUTO-GENERATED USING `sbt genTravisYML` | -# |ANY CHANGES WILL BE OVERWRITTEN | -# +===============================================================+ - -# these values were configured in src/project/prefix.travis.yml - -# configuration for deploy -env: - global: - - ENCRYPTION_LABEL: "25a07036478c" - - COMMIT_AUTHOR_EMAIL: "45969094+kwarcbot@users.noreply.github.com" - - JAVA_OPTS: "-Xmx8192m" - -# use java, and install sbt on OS X -language: java - - -# meta -- email notification for builds -notifications: - email: - on_success: change - on_failure: always - on_error: always - on_start: never - on_cancel: never - - -# speed up cloning of the git repository -# we only need a clone depth of '1' -git: - depth: 1 - -# cache the dependencies for sbt so that we do not need to re-download them all the time -# adapted from https://www.scala-sbt.org/0.13/docs/Travis-CI-with-sbt.html -cache: - directories: - - $HOME/.ivy2/cache - - $HOME/.sbt - -before_cache: - # Cleanup the cached directories to avoid unnecessary cache updates - - find $HOME/.ivy2/cache -name "ivydata-*.properties" -print -delete - - find $HOME/.sbt -name "*.lock" -print -delete - -# +===============================================================+ -# |Anything below this line has been generated automatically | -# |from src/travis.sbt. | -# +===============================================================+ diff --git a/src/project/src/main/scala/travis/Config/TravisConfig.scala b/src/project/src/main/scala/travis/Config/TravisConfig.scala deleted file mode 100644 index 12d469a421..0000000000 --- a/src/project/src/main/scala/travis/Config/TravisConfig.scala +++ /dev/null @@ -1,23 +0,0 @@ -package travis.Config - -import travis.Matrix._ -import travis.yaml._ - -/** - * A single Travis CI build configuration - * @param globals - * @param stages - */ -case class TravisConfig(trueGlobals: Map[String, List[String]], globals: MatrixSet, stages: TravisStage*) { - private val trueGlobalsYAML : YAMLStructure = trueGlobals.mapValues(k => ScriptKey(k).value) - def toYAML: YAMLStructure = { - val stagesDesc : YAMLSequence = stages.map(_.toStageDesc).map(YAMLSequence.from(_)).foldLeft(YAMLSequence.empty)( _ ++ _) - Map( - "stages" -> stagesDesc, - "jobs" -> YAMLStructure(Map( - "include" -> stages.map(_.toJobList(this)).foldLeft(YAMLSequence.empty)(_ ++ _) - ), None) - ) ++ trueGlobalsYAML - } - def serialize : String = toYAML.serialize -} diff --git a/src/project/src/main/scala/travis/Config/TravisJob.scala b/src/project/src/main/scala/travis/Config/TravisJob.scala deleted file mode 100644 index 5ad55a1702..0000000000 --- a/src/project/src/main/scala/travis/Config/TravisJob.scala +++ /dev/null @@ -1,46 +0,0 @@ -package travis.Config - -import travis.Matrix._ -import travis.yaml.{YAMLSequence, YAMLString} - -/** - * A single Job being run by travis - * - * @param description A short, human-readable, description of the job - * @param script A script to executed when running the job - * @param keySet a set of keys that will be expanded in the build matrix - * @param expansion How to handle expansion of defaults - */ -case class TravisJob(description: String, script: List[String], keySet: MatrixSet = MatrixSet(), expansion: ExpansionHandling = DefaultExpansionHandling) { - private val descriptionKey = DescriptionKey(description) - private val scriptKey = ScriptKey(script) - - /** expands this job into a concrete list [[]] assignments */ - def expand(stage: TravisStage, config: TravisConfig): List[MatrixAssignment] = { - expansion((keySet << config.globals).expand).map(ma => { - ma - .%(descriptionKey, { ys: YAMLSequence => descriptionKey.value ++ ys }) - .%(scriptKey, { ys: YAMLSequence => scriptKey.value }) - }) - } -} - -/** Describes how to handle expansion of defaults */ -sealed abstract class ExpansionHandling { - def apply(list: List[MatrixAssignment]): List[MatrixAssignment] -} - -/** use all default cases */ -case object DefaultExpansionHandling extends ExpansionHandling { - def apply(list: List[MatrixAssignment]): List[MatrixAssignment] = list -} - -/** use only the first default case */ -case object FirstExpansion extends ExpansionHandling { - def apply(list: List[MatrixAssignment]): List[MatrixAssignment] = List(list.head) -} - -/** use only the last default case */ -case object LastExpansion extends ExpansionHandling { - def apply(list: List[MatrixAssignment]): List[MatrixAssignment] = List(list.last) -} diff --git a/src/project/src/main/scala/travis/Config/TravisStage.scala b/src/project/src/main/scala/travis/Config/TravisStage.scala deleted file mode 100644 index 5b6d138418..0000000000 --- a/src/project/src/main/scala/travis/Config/TravisStage.scala +++ /dev/null @@ -1,25 +0,0 @@ -package travis.Config - -import travis.Matrix._ -import travis.yaml.{YAMLSequence, YAMLString, YAMLStructure} - -/** - * Represents a single stage of Travis Testing - * @param name the name of the current stage - * @param description a (human-readable) description of the current stage - * @param condition an (optional) condition to check when running the stage - * @param jobs the jobs this stage consists of - */ -case class TravisStage(name: String, description: String, condition: Option[String] = None)(jobs: TravisJob*) { - /** expands this stage into a travis map representing the stage */ - def toStageDesc: YAMLStructure = { - Map(("name", YAMLString.fromString(name))) ++ condition.map(c => Map(("if", YAMLString.fromString(c)))).getOrElse(Map()) - } - - /** expands this job into a concrete list of travis.yml maps representing the job */ - def toJobList(config: TravisConfig): YAMLSequence = { - val jobList : List[MatrixAssignment] = jobs.flatMap(_.expand(this, config)).toList - val jobStructList : List[YAMLStructure] = ((jobList.head ++ StageKey(name)) :: jobList.tail).map(_.toStructure) - YAMLSequence.fromSequence(jobStructList).withComment(description).asInstanceOf[YAMLSequence] - } -} diff --git a/src/project/src/main/scala/travis/Matrix/MatrixAssignment.scala b/src/project/src/main/scala/travis/Matrix/MatrixAssignment.scala deleted file mode 100644 index 4a8efff96c..0000000000 --- a/src/project/src/main/scala/travis/Matrix/MatrixAssignment.scala +++ /dev/null @@ -1,56 +0,0 @@ -package travis.Matrix - -import travis.yaml._ - -/** - * A concrete assignment of [[MatrixKey]]s - * @param nameMap - */ -case class MatrixAssignment(nameMap: Map[String, MatrixKey[YAML]]){ - implicit def toMap: Map[String, YAML] = nameMap.toList.map(_._2.toPair).toMap - - def ++(ass: MatrixAssignment) = MatrixAssignment(nameMap ++ ass.nameMap) - def ++(key: MatrixKey[YAML]) = MatrixAssignment(nameMap ++ Map((key.name, key))) - - /** Adds a new value of the given value to this [[MatrixAssignment]] - * - * @param name Name of value to mixin - * @param f function that mixes in the new values - * @param default the default value to be added if no values exists - * @return - */ - def %[T <: YAML](name: String, f: T => YAML, default: => MatrixKey[YAML]): MatrixAssignment = { - // run the map of the given string - var newMap : Map[String, MatrixKey[YAML]] = nameMap.mapValues(mk => { - if(mk.name == name){ - try { - mk.value[YAML](f(mk.value.asInstanceOf[T])) - } catch { - case _: java.lang.ClassCastException => mk - } - } else { mk } - }) - - // if there was no name in the newMap, we add the defaults - if(!newMap.contains(name)){ - newMap = newMap ++ Map((name, default)) - } - - // and return the new MatrixAssignment - MatrixAssignment(newMap) - } - - /** Mixes ina new MatrixKey */ - def %[T <: YAML](key: MatrixKey[T], f: T => YAML) : MatrixAssignment = %[T](key.name, f, key) - - lazy val toStructure: YAMLStructure = toMap -} - -object MatrixAssignment { - def apply(nameList: List[MatrixKey[YAML]]) = new MatrixAssignment( - nameList.groupBy(_.name).mapValues({ - case List(h) => h - case _ => throw new Exception("nameList should contain exactly one MatrixKey per _.name") - }) - ) -} diff --git a/src/project/src/main/scala/travis/Matrix/MatrixKey.scala b/src/project/src/main/scala/travis/Matrix/MatrixKey.scala deleted file mode 100644 index f3cb00e5a9..0000000000 --- a/src/project/src/main/scala/travis/Matrix/MatrixKey.scala +++ /dev/null @@ -1,79 +0,0 @@ -package travis.Matrix - -import travis.yaml._ - -/** - * Represents a single configurable Travis CI Setting that can have multiple values to be expanded - * - * @param name name of the setting to be expanded - * @param value value to be stored in the YAML file - * @param expand Boolean indicating if this value should be expanded or not - * @tparam T [[YAML]] type of the value to be stored - */ -sealed abstract class MatrixKey[+T <: YAML](val name: String, val value: T, val expand: Boolean = true) { - /** the key used in the struct when expanding this [[MatrixKey]] */ - val structKey: String = name - - /** a pair representing the expanded value of this key */ - def toPair: (String, T) = (structKey, value) - - /** returns a new [[MatrixKey]] of the same type that will never be expanded */ - def asStatic: MatrixKey[T] = new MatrixKey[T](name, value, false) {} - - /** returns a new [[MatrixKey]] of the same type, but assigned within a different group */ - def group(group: String): MatrixKey[T] = new MatrixKey[T](group, value, expand) {override val structKey: String = name} - - /** returns a new [[MatrixKey]] with the value v instead of the current value */ - def value[S <: YAML](v : S) : MatrixKey[S] = new MatrixKey[S](name, v, expand = expand){ - override val structKey: String = MatrixKey.this.structKey - } -} - -// Linux -case object Linux extends MatrixKey[YAMLString]("os", "linux") - -case object Precise extends MatrixKey[YAMLString]("dist", "precise") // 12.04 -case object Trusty extends MatrixKey[YAMLString]("dist", "trusty") // 14.04 - -case object SudoTrue extends MatrixKey[YAMLBoolean]("sudo", true) -case object SudoFalse extends MatrixKey[YAMLBoolean]("sudo", false) -case object SudoRequired extends MatrixKey[YAMLString]("sudo", "required") - - -// OS X -case object OSX extends MatrixKey[YAMLString]("os", "osx") -sealed abstract class OSXVersion(version: String) extends MatrixKey[YAMLString]("osx_version", version) -case object XCode92 extends OSXVersion("xcode9.2") // OS X 10.12 -case object XCode91 extends OSXVersion("xcode9.1") // OS X 10.12 -case object XCode90 extends OSXVersion("xcode9") // OS X 10.12 -case object XCode83 extends OSXVersion("xcode8.3") // OS X 10.12 -case object XCode80 extends OSXVersion("xcode8") // OS X 10.11 -case object XCode73 extends OSXVersion("xcode7.3") // OS X 10.11 -case object XCode64 extends OSXVersion("xcode6.4") // OS X 10.10 - -/** the language being used */ -case class Language(language : String) extends MatrixKey[YAMLString]("language", language) - -/** a set of environment variables to be set */ -case class Env(env: Map[String, String]) extends MatrixKey[YAMLSequence]("env", YAMLSequence.from(env.map(kv => YAMLString.fromString(s"${kv._1}=${kv._2}")).toSeq :_*)) - -// Languages -/** the versions of the JDK being used */ -sealed abstract class JDK(version: String) extends MatrixKey[YAMLString]("jdk", version) -case object IBMJava8 extends MatrixKey[YAMLString]("jdk", "ibmjava8") -case object OpenJDK6 extends MatrixKey[YAMLString]("jdk", "openjdk6") -case object OpenJDK7 extends MatrixKey[YAMLString]("jdk", "openjdk7") -case object OpenJDK8 extends MatrixKey[YAMLString]("jdk", "openjdk8") -case object OpenJDK11 extends MatrixKey[YAMLString]("jdk", "openjdk11") -// case object OracleJDK8 extends MatrixKey[YAMLString]("jdk", "oraclejdk8") -// case object OracleJDK9 extends MatrixKey[YAMLString]("jdk", "oraclejdk9") - - -/** the versions of Scala being used */ -case class Scala[YAMLString](version: String) extends MatrixKey("scala", YAMLString.fromString(version)) - -/** internal matrix keys */ -sealed abstract class InternalMatrixKey[+T <: YAML](override val name: String, override val value: T) extends MatrixKey[T](name, value, false) -private[travis] case class StageKey(stage: String) extends InternalMatrixKey[YAMLString]("stage", stage) -private[travis] case class ScriptKey(script: List[String]) extends InternalMatrixKey[YAMLSequence]("script", script.map(YAMLString.fromString)) -private[travis] case class DescriptionKey(info: String) extends InternalMatrixKey[YAMLSequence]("env", YAMLSequence.from(YAMLString.fromString("INFO='" + info + "'"))) diff --git a/src/project/src/main/scala/travis/Matrix/MatrixSet.scala b/src/project/src/main/scala/travis/Matrix/MatrixSet.scala deleted file mode 100644 index e893a6b9a1..0000000000 --- a/src/project/src/main/scala/travis/Matrix/MatrixSet.scala +++ /dev/null @@ -1,116 +0,0 @@ -package travis.Matrix - -import travis.yaml.YAML - -/** - * A [[MatrixSet]] is a tree with each consisting of a list of [[MatrixKey]]s - */ -sealed abstract class MatrixSet { - /** flatten this MatrixSet into a list of [[MatrixKey]]s */ - def flatten: List[UnexpandedMatrixAssignment] - - /** expands this set into a concrete list of [[MatrixAssignment]]s */ - def expand : List[MatrixAssignment] = flatten.flatMap(_.expand) - - /** adds a given set of globals to this MatrixSet, that is keys which are not set yet, will be added to each of the expanded elements */ - def <<(globals: MatrixSet) : MatrixSet - - /** adds a given set to this MatrixSet, to be expanded in addition to each existing value */ - def &&(set : MatrixSet) : MatrixSet - - /** adds a second option to this MatrixSet, to be expanded in addition to each existing value */ - def ||(set : MatrixSet) : AnyMatrixSet -} - -object MatrixSet { - /** Creates a new MatrixSet given a set of MatrixKeys */ - implicit def apply(keys: MatrixKey[YAML]*) : MatrixSet = ListMatrixSet(keys.toList) -} - -/** - * A [[MatrixSet]] leaf consisting of a list of [[MatrixKey]]s - * @param lst - */ -case class ListMatrixSet(lst : List[MatrixKey[YAML]]) extends MatrixSet { - def flatten: List[UnexpandedMatrixAssignment] = List(UnexpandedMatrixAssignment(lst)) - def <<(globals: MatrixSet) : MatrixSet = AnyMatrixSet( - globals.flatten.map(v => {ListMatrixSet( - UnexpandedMatrixAssignment(lst).withDefaults(v).values - )}) - ) - def &&(set : MatrixSet) : MatrixSet = set match { - case ListMatrixSet(lst2) => ListMatrixSet(lst ::: lst2) - case AnyMatrixSet(options) => AnyMatrixSet(options.map(this && _)) - } - def ||(set: MatrixSet) : AnyMatrixSet = AnyMatrixSet(List(this, set)) -} - -/** - * A [[MatrixSet]] branch consisting of a list of [[MatrixSet]]s - * @param options - */ -case class AnyMatrixSet(options: List[MatrixSet]) extends MatrixSet { - def flatten: List[UnexpandedMatrixAssignment] = options.flatMap(_.flatten) - def <<(globals: MatrixSet) : MatrixSet = AnyMatrixSet(options.map(_ << globals)) - def &&(set : MatrixSet) : MatrixSet = AnyMatrixSet(options.map(_ && set)) - def ||(set: MatrixSet) : AnyMatrixSet = AnyMatrixSet(options ::: List(set)) -} - -/** represents a single assignment in the simplified matrix set */ -case class UnexpandedMatrixAssignment(values: List[MatrixKey[YAML]]) { - /** checks if this assignment contains a given name */ - def contains(name : String) : Boolean = values.exists(_.name == name) - - /** checks if this assignment contains a given matrix assignment */ - def withDefaults(assignment: UnexpandedMatrixAssignment) : UnexpandedMatrixAssignment = UnexpandedMatrixAssignment( - assignment.values.filterNot(f => contains(f.name)) ::: values - ) - - /** expands this into a list of concrete list of matrix assignments */ - def expand: List[MatrixAssignment] = { - // partition into keys that should and should not be expanded - val (matrixValues, staticValues) = values.partition(_.expand) - - val nonExpanded = MatrixAssignment(staticValues) - - val matrixArgs = matrixValues.groupBy(_.name).values.filter(_.nonEmpty).toList.sortBy(_.head.name) - val expanded : List[List[MatrixKey[YAML]]] = cartesianProduct(matrixArgs) - - if(matrixArgs.nonEmpty) (if(matrixArgs.lengthCompare(1) == 0) expanded.transpose else expanded).map(MatrixAssignment(_) ++ nonExpanded) else List(nonExpanded) - } - - /** - * Compute the cartesian product of a list of lists - * Adapted from https://rosettacode.org/wiki/Cartesian_product_of_two_or_more_lists#Scala - */ - private def cartesianProduct[T](lst: List[List[T]]): List[List[T]] = { - - /** - * Prepend single element to all lists of list - * @param e single elemetn - * @param ll list of list - * @param a accumulator for tail recursive implementation - * @return list of lists with prepended element e - */ - def pel(e: T, - ll: List[List[T]], - a: List[List[T]] = Nil): List[List[T]] = - ll match { - case Nil => a.reverse - case x :: xs => pel(e, xs, (e :: x) :: a ) - } - - lst match { - case Nil => Nil - case x :: Nil => List(x) - case x :: _ => - x match { - case Nil => Nil - case _ => - lst.par.foldRight(List(x))( (l, a) => - l.flatMap(pel(_, a)) - ).map(_.dropRight(x.size)) - } - } - } -} diff --git a/src/project/src/main/scala/travis/yaml/YAML.scala b/src/project/src/main/scala/travis/yaml/YAML.scala deleted file mode 100644 index 695f1865bd..0000000000 --- a/src/project/src/main/scala/travis/yaml/YAML.scala +++ /dev/null @@ -1,44 +0,0 @@ -package travis.yaml - -/** Implements a subset of YAML objects which can be easily serialized */ -sealed trait YAML { - /** an optional comment attached to this YAML object */ - val comment: Option[String] - /** helper function to implement setting a given comment */ - protected def setComment(comment : Option[String]) : YAML - - /** creates a copy of this object, with the given comment */ - def withComment(comment: String) : YAML = setComment(Some(comment)) - /** creates a copy of this object, with the comment removed */ - def dropComment : YAML = setComment(None) - - /** serialize this YAML object into a string */ - def serialize: String - - /** helper function to serialize a comment with a given prefix */ - private[yaml] def serializeComment(prefix : String = "") : Option[String] = comment.map(YAML.serializeComment(_, prefix)) - /** helper function to serialize with a given prefix (i.e. spacing) in front of new lines */ - private[yaml] def serializeAs(prefix: String) : String = prefix + YAML.serializeWithPrefix(serialize, prefix) -} - -private[yaml] object YAML { - def serializeWithPrefix(value: String, prefix: String) : String = { - val spacePrefix = " " * prefix.length - - val lines : List[String] = value.split("\n").toList - if(lines.isEmpty) "" else (lines.head :: lines.tail.map(spacePrefix +)).mkString("\n") - } - def serializeComment(comment: String, prefix : String = "") : String = { - serializeWithPrefix(comment.split("\n").map("# " +).mkString("\n"), prefix) - } - def prefixComment[T <: YAML](obj: T, comment: Option[String]) : T = { - val newComment = obj.comment match { - case Some(c) => Some(comment.map(c + "\n" + _).getOrElse(c)) - case None => comment - } - obj.setComment(newComment).asInstanceOf[T] - } -} - -/** helper trait used to split implementation of YAML into several files */ -private[yaml] trait YAMLImplementation extends YAML diff --git a/src/project/src/main/scala/travis/yaml/YAMLLiteral.scala b/src/project/src/main/scala/travis/yaml/YAMLLiteral.scala deleted file mode 100644 index d71dfe443d..0000000000 --- a/src/project/src/main/scala/travis/yaml/YAMLLiteral.scala +++ /dev/null @@ -1,63 +0,0 @@ -package travis.yaml - -/** - * A Literal, i.e. an object that is not composed of other YAML objects - * @param value Value this literal is serialized into - * @param comment Optional comment - */ -sealed abstract class YAMLLiteral(val value: String, val comment: Option[String] = None) extends YAMLImplementation { - /** serialize this literal into a string */ - def serialize: String = if(comment.isDefined) s"$value ${YAML.serializeComment(comment.get, value + " ")}" else value -} - -/** - * - * @param comment Optional comment - */ -case class YAMLNull(override val comment: Option[String] = None) extends YAMLLiteral("null", comment) { - /** Creates a new [[YAMLNull]] with the given comment */ - protected def setComment(comment : Option[String]) : YAMLNull = copy(comment = comment) -} - -/** - * A String - * @param s String that this [[YAMLString]] represents - * @param comment Optional comment - */ -case class YAMLString(s: String, override val comment: Option[String] = None) extends YAMLLiteral(YAMLString.serializeString(s), comment) { - /** Creates a new [[YAMLString]] with the given comment */ - protected def setComment(comment : Option[String]) : YAMLString = copy(comment = comment) -} - -object YAMLString { - private val wordBlackList = List("true", "false", "null", "yes", "no") - - private[yaml] def serializeString(s: String) : String = { - if (s.matches("^[A-Za-z_]+[A-Za-z0-9_]*$") && !wordBlackList.contains(s.toLowerCase)) s else { - if(!s.contains("\"")) "\"" + s + "\"" else "'" + s.replace("'", "''") + "'" - } - } - - implicit def toString(ys: YAMLString) : String = ys.s - implicit def fromString(s: String) : YAMLString = YAMLString(s, None) -} - -/** represents a number */ -case class YAMLNumber(d: Double, override val comment: Option[String]) extends YAMLLiteral(d.toString, comment) { - /** Creates a new [[YAMLNumber]] with the given comment */ - protected def setComment(comment : Option[String]) : YAMLNumber = copy(comment = comment) -} -object YAMLNumber { - implicit def toDouble(yn: YAMLNumber) : Double = yn.d - implicit def fromDouble(d : Double) : YAMLNumber = YAMLNumber(d, None) -} - -/** represents a boolean */ -case class YAMLBoolean(b : Boolean, override val comment: Option[String]) extends YAMLLiteral(b.toString, comment) { - /** Creates a new [[YAMLBoolean]] with the given comment */ - protected def setComment(comment : Option[String]) : YAMLBoolean = copy(comment = comment) -} -object YAMLBoolean { - implicit def toBoolean(yb: YAMLBoolean) : Boolean = yb.b - implicit def fromBoolean(b : Boolean) : YAMLBoolean = YAMLBoolean(b, None) -} diff --git a/src/project/src/main/scala/travis/yaml/YAMLSequence.scala b/src/project/src/main/scala/travis/yaml/YAMLSequence.scala deleted file mode 100644 index 8ef9583e4f..0000000000 --- a/src/project/src/main/scala/travis/yaml/YAMLSequence.scala +++ /dev/null @@ -1,44 +0,0 @@ -package travis.yaml - -import scala.collection.mutable.ListBuffer - -/** - * A Sequence, i.e. a sequence of [[YAML]] objects - * @param items Items contained within this sequence - * @param comment Optional comment - */ -case class YAMLSequence(items: Seq[YAML], comment: Option[String]) extends YAMLImplementation { - /** Creates a new [[YAMLSequence]] with the given comment */ - protected def setComment(comment : Option[String]) : YAMLSequence = copy(comment = comment) - - /** serialize this sequence into a string */ - def serialize: String = { - // create a new array of lines for this string - val structure = new ListBuffer[String] - - // add a comment (if needed) - serializeComment().foreach(structure +=) - - items.foreach(ai => { - val (i: YAML, c: Option[String]) = (ai.dropComment, ai.comment) - c.foreach(c => structure += YAML.serializeComment(c)) - structure += i.serializeAs("- ") - }) - - structure.toList.mkString("\n") - } - - /** generates a new sequence by adding values at the end of the existing sequence */ - def ++(s : Seq[YAML]) : YAMLSequence = YAMLSequence(items ++ s, comment) - /** generates a new sequence by adding a second sequence at the end of this sequence */ - def ++(s : YAMLSequence) : YAMLSequence = s.items.toList match { - case Nil => this - case h :: tail => ++(YAML.prefixComment(h, s.comment) :: tail) - } -} -object YAMLSequence { - def empty : YAMLSequence = YAMLSequence(Nil, None) - implicit def toSequence(ys: YAMLSequence): Seq[YAML] = ys.items - def from(elems: YAML*) : YAMLSequence = fromSequence(elems) - implicit def fromSequence(s : Seq[YAML]) : YAMLSequence = YAMLSequence(s, None) -} diff --git a/src/project/src/main/scala/travis/yaml/YAMLStructure.scala b/src/project/src/main/scala/travis/yaml/YAMLStructure.scala deleted file mode 100644 index 3500750127..0000000000 --- a/src/project/src/main/scala/travis/yaml/YAMLStructure.scala +++ /dev/null @@ -1,68 +0,0 @@ -package travis.yaml - -import scala.collection.mutable.ListBuffer - -/** - * A Structure, i.e. key-value mapping of Strings to YAML Options - * @param map Mappings within this YAMLStructure - * @param comment Optional comment - */ -case class YAMLStructure(map: Map[String, YAML], comment: Option[String] = None) extends YAMLImplementation { - /** Creates a new [[YAMLStructure]] with the given comment */ - protected def setComment(comment : Option[String]) : YAMLStructure = copy(comment = comment) - - /** creates a new [[YAMLStructure]] by adding a set of key-value pairs to this structure */ - def ++(m : Map[String, YAML]) : YAMLStructure = YAMLStructure(map ++ m, comment) - - /** creates a new [[YAMLStructure]] by adding another structure to the end of this list */ - def ++(s : YAMLStructure) : YAMLStructure = { - val newMap = s.map.toList - ++( - // prefix the first comment to the list that is being added - ((newMap.head._1, YAML.prefixComment[YAML](newMap.head._2, s.comment)) :: newMap.tail).toMap - ) - } - - /** creates a new [[YAMLStructure]] by removing a given set of key-value pairs from this structure */ - def --(keys: String*) : YAMLStructure = YAMLStructure(map -- keys, comment) - - /** serialize this structure into a string */ - def serialize: String = { - // create a new array of lines for this string - val structure = new ListBuffer[String] - - // add a comment (if needed) - serializeComment().foreach(structure +=) - - // add key: value for each string - map.toList.sortBy(_._1).foreach(kv => { - val (key: String, comment: Option[String], value: YAML) = (kv._1, kv._2.comment, kv._2.dropComment) - - // add the comment - comment.map(YAML.serializeComment(_)).foreach(structure +=) - - // literals get serialized in-line - val keySer = YAMLString.serializeString(key) + ":" - if(value.isInstanceOf[YAMLLiteral]){ - structure += value.serializeAs(keySer + " ") - // other values are serialized in a new line with more spaces - } else { - structure += keySer - structure += value.serializeAs(s" ") - } - }) - - // and make it a string - structure.toList.mkString("\n") - } - - /** turns this Structure into the underlying map object */ - implicit def toMap: Map[String, YAML] = map -} - -object YAMLStructure { - /** create an empty [[YAMLStructure]] */ - def empty : YAMLStructure = YAMLStructure(Map(), None) - /** creates a new [[YAMLStructure]] using only a map */ - implicit def apply(map: Map[String, YAML]) : YAMLStructure = new YAMLStructure(map, None) -} diff --git a/src/test/DiagramOperatorTest.scala b/src/test/DiagramOperatorTest.scala index a39418ee05..436dc91155 100644 --- a/src/test/DiagramOperatorTest.scala +++ b/src/test/DiagramOperatorTest.scala @@ -1,5 +1,5 @@ import info.kwarc.mmt.api.DPath -import info.kwarc.mmt.api.presentation.FlatMMTSyntaxPresenter +import info.kwarc.mmt.api.presentation.{FlatMMTSyntaxPresenter, MMTSyntaxPresenter} import info.kwarc.mmt.api.utils.URI trait DiagramOperatorHelper { @@ -18,6 +18,7 @@ trait DiagramOperatorHelper { object DiagramOperatorTest extends MagicTest("debug"/*, "DiagramDefinition"*/) with DiagramOperatorHelper { override def doFirst: Unit = { + super.doFirst // Only uncomment if rebuild is really necessary // hl("build MMT/urtheories -mmt-omdoc") // hl("build MMT/urtheories mmt-omdoc") @@ -28,9 +29,6 @@ object DiagramOperatorTest extends MagicTest("debug"/*, "DiagramDefinition"*/) w // Clean first to prevent some spurious caching errors hl("build Playground/diagops -mmt-omdoc") hl("build Playground/diagops mmt-omdoc") - - presenter = new FlatMMTSyntaxPresenter() - controller.extman.addExtension(presenter) } // This [[run]] method is run in parallel to the build process started above in [[doFirst]], @@ -63,6 +61,34 @@ object DiagramOperatorTest extends MagicTest("debug"/*, "DiagramDefinition"*/) w } } +object DiagramClosureTest extends MagicTest("debug", "diagram", "DiagramPublisher"/*, "object-simplifier"*/) with DiagramOperatorHelper { + override def doFirst: Unit = { + super.doFirst + // Only uncomment if rebuild is really necessary + // hl("build MMT/urtheories -mmt-omdoc") + hl("build MMT/urtheories mmt-omdoc module-expressions.mmt") + hl("build Playground/diagops mmt-omdoc closure/closure.mmt") + } + + override def run: Unit = { + } +} + +object DiagramUnionTest extends MagicTest("debug", "diagram", "DiagramPublisher"/*, "object-simplifier"*/) with DiagramOperatorHelper { + override def doFirst: Unit = { + super.doFirst + // Only uncomment if rebuild is really necessary + // hl("build MMT/urtheories -mmt-omdoc") + hl("build MMT/urtheories -mmt-omdoc module-expressions.mmt") + hl("build MMT/urtheories mmt-omdoc module-expressions.mmt") + hl("build Playground/diagops -mmt-omdoc union/union.mmt") + hl("build Playground/diagops mmt-omdoc union/union.mmt") + } + + override def run: Unit = { + } +} + /** * Debugging playground for Navid's implementation of diagram operators. * For debugging-debugging purposes only - might contain dirty code. @@ -73,6 +99,8 @@ object LATIN2Test extends MagicTest("debug", "DiagramDefinition") with DiagramOp private val latin : DPath = DPath(URI("latin:/")) override def doFirst: Unit = { + super.doFirst + hl("build MMT/urtheories mmt-omdoc") hl("build MMT/LATIN2 scala-bin") hl("build MMT/LATIN2 mmt-omdoc type_theory/operators.mmt") @@ -80,9 +108,6 @@ object LATIN2Test extends MagicTest("debug", "DiagramDefinition") with DiagramOp hl("build MMT/LATIN2 mmt-omdoc logic/operators.mmt") // hl("build MMT/LATIN2 lf-scala") - - presenter = new FlatMMTSyntaxPresenter() - controller.extman.addExtension(presenter) } override def run: Unit = { diff --git a/src/test/FrameITTest.scala b/src/test/FrameITTest.scala index e5f9a443d5..5ab9760fc8 100644 --- a/src/test/FrameITTest.scala +++ b/src/test/FrameITTest.scala @@ -1,8 +1,17 @@ -import info.kwarc.mmt.api.DPath -import info.kwarc.mmt.api.modules.View -import info.kwarc.mmt.api.presentation.FlatMMTSyntaxPresenter -import info.kwarc.mmt.api.utils.URI -import info.kwarc.mmt.moduleexpressions.operators.NamedPushoutUtils +import cats.effect.IO +import info.kwarc.mmt.api.metadata.MetaDatum +import info.kwarc.mmt.api.modules.Theory +import info.kwarc.mmt.api.notations.{Delim, Mixfix, MixfixNotation, NotationContainer, Precedence, SimpArg, TextNotation} +import info.kwarc.mmt.api.objects.{OMA, OMID, OML, OMMOD, OMV, Term} +import info.kwarc.mmt.api.presentation.MMTSyntaxPresenter +import info.kwarc.mmt.api.symbols.{FinalConstant, TermContainer, Visibility} +import info.kwarc.mmt.api.{GlobalName, LocalName, NamespaceMap, Path, presentation} +import info.kwarc.mmt.frameit.business.{KnownFact, TermPair, ViewCompletion} +import info.kwarc.mmt.frameit.communication.ServerEndpoints.{jsonBody, path, post} +import info.kwarc.mmt.lf.{ApplySpine, FunTerm, FunType} +import io.circe.Encoder +import io.circe.generic.auto._ +import io.finch.{Endpoint, Ok} /** * Playground for Navid's backend implementation of UFrameIT. @@ -12,42 +21,27 @@ import info.kwarc.mmt.moduleexpressions.operators.NamedPushoutUtils */ object FrameITTest extends MagicTest("debug") { + override val serverport: Option[Int] = None + override def doFirst: Unit = { + super.doFirst // Only uncomment if rebuild is really necessary - // hl("build MMT/urtheories -mmt-omdoc") + // hl("build FrameIT/frameworld mmt-omdoc") // hl("build MMT/urtheories mmt-omdoc") // Only uncomment if rebuild is really necessary // hl("build MitM/Foundation mmt-omdoc") // Clean first to prevent some spurious caching errors - hl("build Playground/frameit mmt-omdoc") - - presenter = new FlatMMTSyntaxPresenter() - controller.extman.addExtension(presenter) + // hl("build Playground/frameit mmt-omdoc") + //controller.extman.addExtension(new FrameitServerExtension) } - final protected val frameit: DPath = DPath(URI("https://example.com/frameit")) - final protected val pushout: DPath = frameit / "pushout" + private val frameworldArchiveNS = Path.parseD("http://mathhub.info/FrameIT/frameworld", NamespaceMap.empty) // This [[run]] method is run in parallel to the build process started above in [[doFirst]], // hence, we apply some dirty waiting mechanism here. override def run: Unit = { - val (newTheory, newView) = NamedPushoutUtils.computeCanonicalPushoutAlongDirectInclusion( - controller.getTheory(pushout ? "Elem"), - controller.getTheory(pushout ? "Nat"), - controller.getTheory(pushout ? "ListElem"), - pushout ? "ListNat", - controller.getAs(classOf[View], pushout ? "elemAsNat"), - pushout ? "listElemAsListNat" - ) - - controller.add(newTheory) - controller.add(newView) - - waitThenPrint(newTheory.path) - waitThenPrint(newView.path) - - sys.exit(0) + println("Test") } } diff --git a/src/test/JanesUseCase.scala b/src/test/JanesUseCase.scala index 1e44398de5..e9219c8226 100644 --- a/src/test/JanesUseCase.scala +++ b/src/test/JanesUseCase.scala @@ -5,16 +5,31 @@ import info.kwarc.mmt.MitM._ import info.kwarc.mmt.odk._ import Sage._ import GAP._ +import Graphtester.controller import Singular._ import info.kwarc.mmt.MitM.VRESystem._ import info.kwarc.mmt.api.objects.{OMA, OMS} +import info.kwarc.mmt.api.ontology.{DeclarationTreeExporter, DependencyGraphExporter, PathGraphExporter} import info.kwarc.mmt.api.symbols.FinalConstant +import info.kwarc.mmt.api.web.JSONBasedGraphServer import info.kwarc.mmt.lf.{Apply, ApplySpine} import info.kwarc.mmt.odk.OpenMath._ object JanesUseCase extends MagicTest("lmfdb", "mitm", "scscp","checkalign","translator") { def newTrace = new VRESystem.MitMComputationTrace(Some(t => MitM.present(t, s => controller.presenter.asString(s)))) - + + override def doFirst: Unit = { + super.doFirst + // Copied here because these lines were removed from MagicTest. + // Please reevaluate if they are necessary. If in doubt, leave them. They are just slow.) + controller.handleLine("extension info.kwarc.mmt.pvs.PVSImporter") + controller.handleLine(("extension info.kwarc.mmt.api.ontology.AlignmentsServer " + alignmentspath).trim) + controller.extman.addExtension(new DependencyGraphExporter) + controller.extman.addExtension(new DeclarationTreeExporter) + controller.extman.addExtension(new JSONBasedGraphServer) + controller.extman.addExtension(new PathGraphExporter) + } + // override val gotoshell = false def run { // turn on scscp on localhost:26134: diff --git a/src/test/LMFDBTest.scala b/src/test/LMFDBTest.scala index 1fd2ad0b8c..f9ccaee041 100644 --- a/src/test/LMFDBTest.scala +++ b/src/test/LMFDBTest.scala @@ -1,8 +1,11 @@ +import Graphtester.controller +import MitMTest.controller import info.kwarc.mmt.MitM.{MitM, MitMSystems} import info.kwarc.mmt.MitM.VRESystem.{MitMComputation, MitMComputationTrace} import info.kwarc.mmt.api.{LocalName, utils} import info.kwarc.mmt.api.objects._ import info.kwarc.mmt.api.ontology._ +import info.kwarc.mmt.api.web.JSONBasedGraphServer import info.kwarc.mmt.odk.LFX.LFList import info.kwarc.mmt.odk.LMFDB.LMFDB import info.kwarc.mmt.odk.OpenMath.Coding.{OMMiTMCoding, OMXMLCoding} @@ -11,6 +14,19 @@ import info.kwarc.mmt.odk.{IntegerLiterals, LFX, StringLiterals} // "impl-rule-gen" object LMFDBTest extends MagicTest("lmfdb", "mitm", "scscp", "debug") { override val gotoshell: Boolean = false + + override def doFirst: Unit = { + super.doFirst + // Copied here because these lines were removed from MagicTest. + // Please reevaluate if they are necessary. If in doubt, leave them. They are just slow.) + controller.handleLine("extension info.kwarc.mmt.pvs.PVSImporter") + controller.handleLine(("extension info.kwarc.mmt.api.ontology.AlignmentsServer " + alignmentspath).trim) + controller.extman.addExtension(new DependencyGraphExporter) + controller.extman.addExtension(new DeclarationTreeExporter) + controller.extman.addExtension(new JSONBasedGraphServer) + controller.extman.addExtension(new PathGraphExporter) + } + def run : Unit = { hl("extension info.kwarc.mmt.odk.LMFDB.Plugin") diff --git a/src/test/Latin2Test.scala b/src/test/Latin2Test.scala new file mode 100644 index 0000000000..861211948f --- /dev/null +++ b/src/test/Latin2Test.scala @@ -0,0 +1,21 @@ +import info.kwarc.mmt.api.DPath +import info.kwarc.mmt.api.presentation.FlatMMTSyntaxPresenter +import info.kwarc.mmt.api.utils.URI + +/** + * Playground for testing out MMT/LATIN2 things + * + * @author Navid + */ +object Latin2Test extends MagicTest("debug"/*, "DiagramDefinition"*/) { + + final protected val latin: DPath = DPath(URI("latin:/")) + + override def doFirst: Unit = { + // hl("file C:\\Users\\nroux\\Desktop\\kwarc-project\\code\\archives\\MathHub\\MMT\\LATIN2\\build-omdoc.msl") + hl("build MMT/LATIN2 lf-scala") + } + + override def run: Unit = { + } +} \ No newline at end of file diff --git a/src/test/MathHubTest.scala b/src/test/MathHubTest.scala index f5efaec7b8..75e1cbd255 100644 --- a/src/test/MathHubTest.scala +++ b/src/test/MathHubTest.scala @@ -1,6 +1,24 @@ +import Graphtester.controller +import LMFDBTest.controller +import info.kwarc.mmt.api.ontology.{DeclarationTreeExporter, DependencyGraphExporter, PathGraphExporter} +import info.kwarc.mmt.api.web.JSONBasedGraphServer + object MathHubTest extends MagicTest("mathhub") { override val serverport = Some(9000) override val gotoshell = true + + override def doFirst: Unit = { + super.doFirst + // Copied here because these lines were removed from MagicTest. + // Please reevaluate if they are necessary. If in doubt, leave them. They are just slow.) + controller.handleLine("extension info.kwarc.mmt.pvs.PVSImporter") + controller.handleLine(("extension info.kwarc.mmt.api.ontology.AlignmentsServer " + alignmentspath).trim) + controller.extman.addExtension(new DependencyGraphExporter) + controller.extman.addExtension(new DeclarationTreeExporter) + controller.extman.addExtension(new JSONBasedGraphServer) + controller.extman.addExtension(new PathGraphExporter) + } + def run: Unit = { hl("extension info.kwarc.mmt.mathhub.Server") } diff --git a/src/test/MaxTest.scala b/src/test/MaxTest.scala index dd52909d41..3835908840 100644 --- a/src/test/MaxTest.scala +++ b/src/test/MaxTest.scala @@ -1,3 +1,6 @@ +import LMFDBTest.controller +import info.kwarc.mmt.api.ontology.{DeclarationTreeExporter, DependencyGraphExporter, PathGraphExporter} +import info.kwarc.mmt.api.web.JSONBasedGraphServer //import info.kwarc.mmt.argsemcomp //import MagicTest.home //import info.kwarc.mmt.api.utils.File @@ -5,6 +8,19 @@ object Graphtester extends MagicTest("jgraph", "argcomp") { + + override def doFirst: Unit = { + super.doFirst + // Copied here because these lines were removed from MagicTest. + // Please reevaluate if they are necessary. If in doubt, leave them. They are just slow.) + controller.handleLine("extension info.kwarc.mmt.pvs.PVSImporter") + controller.handleLine(("extension info.kwarc.mmt.api.ontology.AlignmentsServer " + alignmentspath).trim) + controller.extman.addExtension(new DependencyGraphExporter) + controller.extman.addExtension(new DeclarationTreeExporter) + controller.extman.addExtension(new JSONBasedGraphServer) + controller.extman.addExtension(new PathGraphExporter) + } + def run : Unit = { //println(MagicTest.archiveRoot) //List(File(System.getProperty("user.home") / "MMT" / "myformalizations")find.(_exists).getOrElse(println("Does not exist")) diff --git a/src/test/MitMTest.scala b/src/test/MitMTest.scala index 6f5d498770..0b7199b69e 100644 --- a/src/test/MitMTest.scala +++ b/src/test/MitMTest.scala @@ -3,9 +3,24 @@ import MitM.MitM._ import odk._ import Sage._ import GAP._ +import Graphtester.controller import Singular._ +import info.kwarc.mmt.api.ontology.{DeclarationTreeExporter, DependencyGraphExporter, PathGraphExporter} +import info.kwarc.mmt.api.web.JSONBasedGraphServer object MitMTest extends MagicTest("lmfdb", "mitm", "scscp") { + override def doFirst: Unit = { + super.doFirst + // Copied here because these lines were removed from MagicTest. + // Please reevaluate if they are necessary. If in doubt, leave them. They are just slow.) + controller.handleLine("extension info.kwarc.mmt.pvs.PVSImporter") + controller.handleLine(("extension info.kwarc.mmt.api.ontology.AlignmentsServer " + alignmentspath).trim) + controller.extman.addExtension(new DependencyGraphExporter) + controller.extman.addExtension(new DeclarationTreeExporter) + controller.extman.addExtension(new JSONBasedGraphServer) + controller.extman.addExtension(new PathGraphExporter) + } + def run { // FR: systems are loaded ODK plugin, see file Config/Actions.scala, only warmup is needed // load the (default) configuration diff --git a/src/test/RunMichael.scala b/src/test/RunMichael.scala index 4a2803bec1..aaad39d883 100644 --- a/src/test/RunMichael.scala +++ b/src/test/RunMichael.scala @@ -1,9 +1,24 @@ +import Graphtester.controller +import info.kwarc.mmt.api.ontology.{DeclarationTreeExporter, DependencyGraphExporter, PathGraphExporter} +import info.kwarc.mmt.api.web.JSONBasedGraphServer import info.kwarc.mmt.api.{NamespaceMap, Path} import info.kwarc.mmt.got.GraphOptimizationTool import info.kwarc.mmt.jedit.MMTOptimizationAnnotationReader object RunMichael extends MagicTest { + override def doFirst: Unit = { + super.doFirst + // Copied here because these lines were removed from MagicTest. + // Please reevaluate if they are necessary. If in doubt, leave them. They are just slow.) + controller.handleLine("extension info.kwarc.mmt.pvs.PVSImporter") + controller.handleLine(("extension info.kwarc.mmt.api.ontology.AlignmentsServer " + alignmentspath).trim) + controller.extman.addExtension(new DependencyGraphExporter) + controller.extman.addExtension(new DeclarationTreeExporter) + controller.extman.addExtension(new JSONBasedGraphServer) + controller.extman.addExtension(new PathGraphExporter) + } + def run : Unit = { controller.extman.addExtension(new GraphOptimizationTool) val got : GraphOptimizationTool = controller.extman.get(classOf[GraphOptimizationTool]).head diff --git a/src/test/preamble.scala b/src/test/preamble.scala index d02ebb20e7..b4b5ff1055 100644 --- a/src/test/preamble.scala +++ b/src/test/preamble.scala @@ -2,7 +2,7 @@ import info.kwarc.mmt.api import info.kwarc.mmt.api._ import info.kwarc.mmt.api.frontend.{Logger, Run} import info.kwarc.mmt.api.ontology.{DeclarationTreeExporter, DependencyGraphExporter, PathGraphExporter} -import info.kwarc.mmt.api.presentation.{ConsoleWriter, MMTSyntaxPresenter} +import info.kwarc.mmt.api.presentation.{ConsoleWriter, FlatMMTSyntaxPresenter, MMTSyntaxPresenter} import info.kwarc.mmt.api.utils.File import info.kwarc.mmt.api.web.JSONBasedGraphServer @@ -46,11 +46,6 @@ abstract class Test(val archivepath: String, // add the plugins controller.handleLine("extension info.kwarc.mmt.lf.Plugin") controller.handleLine("extension info.kwarc.mmt.odk.Plugin") - controller.handleLine("extension info.kwarc.mmt.pvs.PVSImporter") - // controller.handleLine("extension info.kwarc.mmt.metamath.Plugin") - - controller.handleLine(("extension info.kwarc.mmt.api.ontology.AlignmentsServer " + alignmentspath).trim) - def doFirst: Unit = {} @@ -64,11 +59,6 @@ abstract class Test(val archivepath: String, */ def main(args: Array[String]): Unit = try { - - controller.extman.addExtension(new DependencyGraphExporter) - controller.extman.addExtension(new DeclarationTreeExporter) - controller.extman.addExtension(new JSONBasedGraphServer) - controller.extman.addExtension(new PathGraphExporter) doFirst if (serverport.isDefined) { //controller.handleLine("clear") @@ -131,7 +121,7 @@ object MagicTest { home / "MMT" / "myformalizations", // Max Mac // Navid - home / "Desktop" / "kwarc-project" / "code" / "archives" + home / "Desktop" / "FrameIT" / "archives" / "MathHub" ).find(_.exists).getOrElse(throw GeneralError("MagicTest failed: No known archive root")) } @@ -157,6 +147,9 @@ object MagicTest { /** * A magic test configuration that automatically figures out the paths to everything + * + * Use `override val serverport: Option[Int] = None` to make your tests faster if you don't need an MMT server + * spawning up. */ abstract class MagicTest(prefixes: String*) extends Test( MagicTest.archiveRoot.toString, @@ -167,23 +160,38 @@ abstract class MagicTest(prefixes: String*) extends Test( ) { override val gotoshell: Boolean = false - var presenter: MMTSyntaxPresenter = _ + final val presenter: MMTSyntaxPresenter = new FlatMMTSyntaxPresenter() + + override def doFirst: Unit = { + super.doFirst + controller.extman.addExtension(presenter) + } /** - * Waits - possibly ad infinitum - for the object identified by the path to appear in the [[controller]]. + * Waits - possibly ad infinitum - for the object identified by the path to appear in the [[controller]] + * and returns it. * * @param path A path to a theory, document etc. */ - final protected def waitUntilAvailable(path: Path): Unit = { - while (controller.getO(path).isEmpty) { + final protected def waitUntilAvailable(path: Path): StructuralElement = { + var elem: Option[StructuralElement] = None + while (true) { + elem = controller.getO(path) + if (elem.isDefined) { + return elem.get + } Thread.sleep(500) } + + throw new RuntimeException("This code must not be reached - bug in Scala compiler?") } - final protected def waitThenPrint(path: Path): Unit = { - waitUntilAvailable(path) + final protected def waitThenPrint(path: Path): StructuralElement = { + val element = waitUntilAvailable(path) presenter(controller.get(path))(ConsoleWriter) print("\n") + + element } final protected def space(): Unit = { diff --git a/src/travis.sbt b/src/travis.sbt deleted file mode 100644 index 6093b165e2..0000000000 --- a/src/travis.sbt +++ /dev/null @@ -1,92 +0,0 @@ -import Utils.utils -import sbt.Keys._ -import sbt._ -import travis.Config._ -import travis.Matrix._ - -import scala.io.Source - -val travisConfig = taskKey[TravisConfig]("Generate travis.yml configuration") -travisConfig := { - val ourScalaVersion: String = scalaVersion.value - - // convenience wrapper to run an sbt task and an optional check - def sbt(task: String, check: Option[String] = None): List[String] = List( - s"cd src && (cat /dev/null | sbt ++$ourScalaVersion $task) && cd .." - ) ::: check.toList - - // convenience functions for checks - def file(name: String): Option[String] = Some("[[ -f \"" + name + "\" ]]") - - def identical(name: String): Option[String] = Some("(git diff --quiet --exit-code \"" + name + "\")") - - def dir(name: String): Option[String] = Some("[[ -d \"" + name + "\" ]]") - - val LinuxTesting = MatrixSet( - Trusty, Language("scala"), Env(Map(("SBT_VERSION_CMD", "\"^validate\""))), - OpenJDK8, OpenJDK11 - ) - - // in principle we would test OS X as follows - // but this does not properly work, because Travis - // does not fully support OS X (yet?) - val OSXTesting = MatrixSet( - OSX, Language("java"), Env(Map(("SBT_VERSION_CMD", "\"^validate ^validateUniversal\""))), - XCode83 - ) - - TravisConfig( - Map( - // before installation, we need to make sure that we have sbt - // on Mac OS X, this means we need to install it via 'brew install' - // hopefully this will be provided by Travis CI in the future - "before_install" -> List("if [[ \"$TRAVIS_OS_NAME\" = \"osx\" ]]; then brew update; brew install sbt; fi"), - - // on the install step, we run 'sbt update' to install all the dependencies - // if this fails, we will get an errored test, instead of a failed one - "install" -> sbt("update"), - - // setup the test environment, so that the lmh versioning is ignored on devel - "before_script" -> List( - "export TEST_USE_BRANCH=$TRAVIS_BRANCH; echo TEST_USE_BRANCH=;" - ) - ), - - MatrixSet( - Scala(ourScalaVersion) - ) && LinuxTesting, /* (LinuxTesting && OSXTesting) if OS X testing is ever fixed )*/ - - - TravisStage("SelfCheck", "check that 'sbt genTravisYML' has been run")( - TravisJob("Check that `sbt genTravisYML` has been run", sbt("genTravisYML", identical(".travis.yml")), MatrixSet(OpenJDK8), expansion = FirstExpansion) - ), - - TravisStage("CompileAndCheck", "Check that our tests run and the code compiles")( - TravisJob("Check that unit tests run", sbt("test")), - TravisJob("Check mmt.jar generation and integration tests", - sbt("deploy", file("deploy/mmt.jar")) ::: List("java -cp deploy/mmt.jar info.kwarc.mmt.test.TestRunner")), - ), - - TravisStage("DeployCheck", "check that the 'apidoc' and 'deployLFCatalog' targets work")( - TravisJob("Check lfcatalog.jar generation using `sbt deployLFCatalog`", sbt("deployLFCatalog", file("deploy/lfcatalog/lfcatalog.jar"))), - TravisJob("Check that apidoc generation works", sbt("apidoc", dir("apidoc"))) - ), - - TravisStage("deploy", "deploy the api documentation", Some("branch = master"))( - TravisJob("Auto-deploy API documentation", List("bash scripts/travis/deploy_doc.sh"), MatrixSet(OpenJDK8), expansion = FirstExpansion) - ) - ) -} - - -val genTravisYML = taskKey[Unit]("Print out travis.yml configuration") -genTravisYML := { - // read the prefix and the config - val prefix = Source.fromFile(utils.value.src / "project" / "prefix.travis.yml").getLines.filter(!_.startsWith("##")).mkString("\n") - val config = travisConfig.value.serialize - - // and write it into .travis.yml - val outFile = utils.value.root / ".travis.yml" - IO.write(outFile, prefix + "\n" + config) - streams.value.log.info(s"Wrote $outFile") -}