diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..fa52750bc1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,18 @@ +.github/** +.gitignore +.travis.yml +LIZENZ.txt +README* +_parameters +debian/** +doc/** +ideas +pretty/** +sample-ghci-script +todo* +**.tex +**.pdf +Docker/** +GMP/CoLoSS/data/GMP.tar.gz +GMP/papers/** +HolLight/OcamlTools/** \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 06f4ccd5bd..d5724fc7e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -245,7 +245,7 @@ jobs: # ~ 496 MiB, 20 s - name: Upload cached stack if: env.DEBUG == 'true' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: stack0 path: ${{ runner.temp }}/stack0.tzst @@ -254,7 +254,7 @@ jobs: # ~ 9 MiB - name: Upload Hets source dir if: env.DEBUG == 'true' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: hets0 path: ${{ runner.temp }}/hets0.tzst @@ -302,7 +302,7 @@ jobs: - name: Upload Binary continue-on-error: true #if: github.event_name == 'push' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: hets server binary path: hets-server.xz @@ -311,7 +311,7 @@ jobs: # ~ 125 MiB , 15s - name: Upload Hets build dir if: github.event_name == 'pull_request' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: hets-server path: ${{ runner.temp }}/${{ env.HETS_ARC }} @@ -326,7 +326,7 @@ jobs: # ~ 496 MiB, 20 s - name: Upload post build stack if: env.DEBUG == 'true' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: stack1 path: ${{ runner.temp }}/stack1.tzst @@ -405,7 +405,7 @@ jobs: - name: Upload Binary continue-on-error: true #if: github.event_name == 'push' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: hets desktop binary path: hets.xz @@ -414,7 +414,7 @@ jobs: # ~ 17 s - name: Upload Hets build dir if: github.event_name == 'pull_request' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: hets-desktop path: ${{ runner.temp }}/${{ env.HETS_ARC }} @@ -459,7 +459,7 @@ jobs: uses: actions/checkout@v2 - name: Download Hets build dir - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: hets-server path: ${{ runner.temp }} @@ -501,7 +501,7 @@ jobs: needs: job_2a steps: - name: Download Hets build dir - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: hets-server path: ${{ runner.temp }} @@ -539,7 +539,7 @@ jobs: needs: job_2a steps: - name: Download Hets build dir - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: hets-server path: ${{ runner.temp }} @@ -575,7 +575,7 @@ jobs: needs: job_2a steps: - name: Download Hets build dir - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: hets-server path: ${{ runner.temp }} @@ -683,7 +683,7 @@ jobs: if: startsWith(env.FAKE_URL, 'http') && steps.download.outcome == 'failure' continue-on-error: true - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: docs path: ${{ runner.temp }}/docs.tgz diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000000..9e7819326e --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,21 @@ +name: Deploy Sphinx documentation to Pages + +on: + push: + branches: [master] # branch to trigger deployment + +jobs: + pages: + runs-on: ubuntu-20.04 + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + permissions: + pages: write + id-token: write + steps: + - id: deployment + uses: sphinx-notes/pages@v3 + with: + documentation_path: ./python/api/docs + requirements_path: ./python/api/docs/requirements.txt \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8763b9c59a..7bd923059d 100644 --- a/.gitignore +++ b/.gitignore @@ -15,16 +15,16 @@ rev.txt *.orig .stack-work/ stack -doc/*.aux -doc/*.bbl -doc/*.blg -doc/*.fdb_latexmk -doc/*.fls -doc/*.log -doc/*.out -doc/UserGuide.pdf -doc/hs2isa.ps -docs/ +/doc/*.aux +/doc/*.bbl +/doc/*.blg +/doc/*.fdb_latexmk +/doc/*.fls +/doc/*.log +/doc/*.out +/doc/UserGuide.pdf +/doc/hs2isa.ps +/docs/ __pycache__ .idea programatica/ @@ -199,3 +199,6 @@ NeSyPatterns/AS.hs NeSyPatterns/ATC_NeSyPatterns.der.hs NeSyPatterns/ATC_NeSyPatterns.hs OWL2/scripts/runTest +*.gresource +/python/api/docs/_build +/python/api/docs/code diff --git a/CMDL/Interface.hs b/CMDL/Interface.hs index a24bf255bd..26f02af3e6 100644 --- a/CMDL/Interface.hs +++ b/CMDL/Interface.hs @@ -22,7 +22,8 @@ import Comorphisms.LogicGraph (logicGraph) import Logic.Grothendieck #endif -import Proofs.AbstractState (getListOfConsCheckers, usableCC) +import Proofs.AbstractState (getAllConsCheckers, sublogicOfTheory, getCcName + , getListOfConsCheckers, usableCC) import System.IO diff --git a/Common/Consistency.hs b/Common/Consistency.hs index 49486e786d..853b59f32c 100644 --- a/Common/Consistency.hs +++ b/Common/Consistency.hs @@ -26,7 +26,7 @@ import Data.Data required for extending imports (no confusion) in Maude -} data Conservativity = Inconsistent - | Unknown String + | Unknown { conservativityUnknownReason :: String} | None | PCons | Cons @@ -53,8 +53,7 @@ translated sentence of the source. -} data ConservativityChecker sign sentence morphism = ConservativityChecker { checkerId :: String , checkerUsable :: IO (Maybe String) - , checkConservativity - :: (sign, [Named sentence]) - -> morphism - -> [Named sentence] - -> IO (Result (Conservativity, [sentence])) } + , checkConservativity :: (sign, [Named sentence]) -- source or initial signature and sentences + -> morphism -- morphism between specs + -> [Named sentence] -- target sentences of extended spec + -> IO (Result (Conservativity, [sentence])) } -- conservativity with obligations of the extensions diff --git a/Common/GtkGoal.hs b/Common/GtkGoal.hs index 5d4b840817..216ff0ad58 100644 --- a/Common/GtkGoal.hs +++ b/Common/GtkGoal.hs @@ -88,8 +88,8 @@ proofStatusToGStatus p = case goalStatus p of Proved False -> GInconsistent Proved True -> GProved Disproved -> GDisproved - Open (Reason s) -> - if any (isInfixOf "timeout" . map toLower) s then GTimeout else GOpen + Open reason -> + if any (isInfixOf "timeout" . map toLower) reason then GTimeout else GOpen -- | Converts a BasicProof into a GStatus basicProofToGStatus :: BasicProof -> GStatus diff --git a/Docker/hetsapi.Dockerfile b/Docker/hetsapi.Dockerfile index 424879310b..ac4d14c673 100644 --- a/Docker/hetsapi.Dockerfile +++ b/Docker/hetsapi.Dockerfile @@ -1,58 +1,101 @@ -FROM hyphen:20.04 +FROM spechub2/hyphen:22.04 AS base ENV TZ=Europe/Berlin -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - -RUN apt-get update && apt-get -y install locales && locale-gen en_US.UTF-8 -ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US:en -ENV LC_ALL en_US.UTF-8 - -RUN apt-get install -y software-properties-common apt-utils make -RUN apt-add-repository -y ppa:hets/hets -RUN apt-get update -RUN apt-get install -y openjdk-8-jdk-headless ant cabal-install\ - ksh perl-base tar xz-utils zip\ - libmysqlclient-dev\ - ghc-haddock libghc-missingh-dev\ - ghc>=7.10.3 happy\ - libghc-mutable-containers-dev\ - libghc-haxml-dev libghc-tar-dev libghc-random-dev libghc-parsec3-dev\ - libghc-fgl-dev libghc-xml-dev\ - libghc-http-dev libghc-warp-dev libghc-wai-extra-dev\ - libghc-split-dev libghc-file-embed-dev libghc-monad-logger-dev\ - libghc-yaml-dev libghc-esqueleto-dev>=2.5.3\ - libghc-persistent-dev>=2.7.0 libghc-persistent-template-dev>=2.5.2\ - libghc-persistent-postgresql-dev>=2.6.1\ - libghc-persistent-sqlite-dev>=2.6.2\ - libghc-utf8-string-dev libghc-relation-dev\ - libghc-persistent-mysql-dev\ - libghc-hexpat-dev\ - libghc-aterm-dev\ - libghc-xeno-dev\ - libghc-heap-dev - - -# RUN git clone https://github.com/spechub/Hets.git /hets -## OR -COPY ./ /hets - -WORKDIR /hets +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US:en +ENV LC_ALL=en_US.UTF-8 +ENV HETS_LIB=/opt/Hets-lib -RUN make derived +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone && \ + apt-get update && apt-get -y install locales && locale-gen en_US.UTF-8 && \ + apt-get install -y software-properties-common apt-utils make && \ +# Install build and runtime dependencies + apt-add-repository -y ppa:hets/hets && \ + apt-get update && \ + apt-get install -y openjdk-8-jdk-headless ant cabal-install\ + ksh perl-base tar xz-utils zip\ + libmysqlclient-dev\ + ghc-haddock libghc-missingh-dev\ + ghc>=7.10.3 happy haskell-stack\ + libghc-mutable-containers-dev\ + libghc-haxml-dev libghc-tar-dev libghc-random-dev libghc-parsec3-dev\ + libghc-fgl-dev libghc-xml-dev\ + libghc-http-dev libghc-warp-dev libghc-wai-extra-dev\ + libghc-split-dev libghc-file-embed-dev libghc-monad-logger-dev\ + libghc-yaml-dev libghc-esqueleto-dev>=2.5.3\ + libghc-persistent-dev>=2.7.0 libghc-persistent-template-dev>=2.5.2\ + libghc-persistent-postgresql-dev>=2.6.1\ + libghc-persistent-sqlite-dev>=2.6.2\ + libghc-utf8-string-dev libghc-relation-dev\ + libghc-persistent-mysql-dev\ + libghc-hexpat-dev\ + libghc-aterm-dev\ + libghc-xeno-dev\ + libghc-heap-dev + + +FROM base AS build-lib +RUN apt-get -y install git +# Get source +# RUN git clone --branch 2109_python_gui https://github.com/spechub/Hets.git /opt/hets +## Alternatively copy local files. +COPY ./ /opt/hets +WORKDIR /opt/hets/ +# Build and install haskell library +RUN make derived RUN runhaskell Setup.hs configure \ - --ghc --prefix=/ \ - --disable-executable-stripping \ - --disable-benchmarks \ - --libdir=/lib/haskell-packages/ghc/lib/x86_64-linux-ghc-8.6.5 \ - --libsubdir=hets-api-0.100.0 \ - --datadir=share \ - --datasubdir=hets-api \ - --haddockdir=/lib/ghc-doc/haddock/hets-api-0.100.0 \ - --docdir=share/doc/hets-api-doc \ - --package-db=/var/lib/ghc/package.conf.d \ - --disable-profiling \ - lib:Hets + --ghc --prefix=/ \ + --disable-executable-stripping \ + --disable-benchmarks \ + --libdir=/lib/haskell-packages/ghc/lib/x86_64-linux-ghc-8.6.5 \ + --dynlibdir=/lib/haskell-packages/ghc/lib/x86_64-linux-ghc-8.6.5 \ + --libsubdir=hets-api-0.100.0 \ + --datadir=share \ + --datasubdir=hets-api \ + --haddockdir=/lib/ghc-doc/haddock/hets-api-0.100.0 \ + --docdir=share/doc/hets-api-doc \ + --package-db=/var/lib/ghc/package.conf.d \ + --disable-profiling \ + lib:Hets RUN runhaskell Setup.hs build -j$(nproc) lib:Hets +RUN runhaskell Setup.hs copy --destdir=/tmp/hets-pkg +RUN runhaskell Setup.hs register --gen-script +RUN mv register.sh /tmp/install-hets.sh +RUN tar -czf /tmp/hets.tar.gz -C /tmp/hets-pkg/ . + +FROM build-lib AS debug +WORKDIR /opt/hets RUN runhaskell Setup.hs install +RUN \ + apt-get update && \ + apt-get install -y cvc-47 darwin eprover fact++ maude minisat pellet spass vampire yices z3 zchaff && \ + git clone https://github.com/spechub/Hets-lib.git ${HETS_LIB} + +ENV PYTHONPATH=/opt/hets/python/api/src + + +FROM base +COPY --from=build-lib /tmp/hets.tar.gz /tmp/hets.tar.gz +COPY --from=build-lib /tmp/install-hets.sh /tmp/install-hets.sh +# RUN tar -tzf /tmp/hets.tar.gz && ls -l /bin /bin/sh && false +RUN tar -xzkpom -C / -f /tmp/hets.tar.gz +RUN /tmp/install-hets.sh + +# Install provers and Hets-lib +RUN \ + apt-get update && \ + apt-get install -y cvc-47 darwin eprover fact++ maude minisat pellet spass vampire yices z3 zchaff && \ + git clone https://github.com/spechub/Hets-lib.git ${HETS_LIB} +# Install python library + +COPY --from=build-lib /opt/hets/python/api /opt/hets/python/api +RUN python3 -m pip install /opt/hets/python/api +# Add non-root user and cleanup +RUN \ + useradd -ms /bin/bash -u 921 hets && \ + rm -rf /opt/hets + +WORKDIR /home/hets + +USER hets diff --git a/Docker/hetsgui.Dockerfile b/Docker/hetsgui.Dockerfile new file mode 100644 index 0000000000..1845e2e0b5 --- /dev/null +++ b/Docker/hetsgui.Dockerfile @@ -0,0 +1,31 @@ +FROM spechub2/hets-api as base + +USER root + +COPY python/gui /opt/hetsgui + +RUN apt-get update && apt-get upgrade -y && \ + apt-get install -y libgtk-3-dev python3-gi python3-gi-cairo \ + gir1.2-gtk-3.0 python3-pip graphviz \ + libcanberra-gtk-module libcanberra-gtk3-module + +FROM base as debug + +ENV PYTHONPATH=${PYTHONPATH}:/opt/hetsgui/src + +RUN python3 -m pip install xdot numpy graphviz + +WORKDIR /root + +FROM base + +RUN cd /opt/hetsgui/src/hetsgui && \ + glib-compile-resources hetsgui.gresource.xml && \ + python3 -m pip install /opt/hetsgui && \ + rm -rf /opt/hetsgui + +WORKDIR /home/hets + +USER hets + +ENTRYPOINT /usr/local/bin/hets diff --git a/Docker/hyphen.Dockerfile b/Docker/hyphen.Dockerfile index 70231f6844..f964a888b1 100644 --- a/Docker/hyphen.Dockerfile +++ b/Docker/hyphen.Dockerfile @@ -1,5 +1,5 @@ -FROM ubuntu:20.04 +FROM ubuntu:22.04 RUN apt-get update && \ apt-get upgrade -y && \ @@ -22,5 +22,7 @@ packages = [ "hyphen" ]\n\ [tool.setuptools.package-data]\n\ "*" = [ "hslowlevel.*" ]\n' > /hyphen/pyproject.toml -RUN pip3 install /hyphen +RUN python3 -m pip install --upgrade pip setuptools + +RUN python3 -m pip install /hyphen diff --git a/Hets.cabal b/Hets.cabal index 793117ac0a..82f86bcbf0 100644 --- a/Hets.cabal +++ b/Hets.cabal @@ -694,6 +694,7 @@ Library HasCASL.TypeRel HasCASL.Unify HasCASL.VarDecl + HetsAPI.Refinement HolLight.ATC_HolLight HolLight.Helper HolLight.HolLight2DG diff --git a/HetsAPI/Commands.hs b/HetsAPI/Commands.hs index 7bb7f4327b..2060b4867d 100644 --- a/HetsAPI/Commands.hs +++ b/HetsAPI/Commands.hs @@ -34,7 +34,7 @@ module HetsAPI.Commands ( , HPC.getUsableConsistencyCheckers , HPC.proveNode , HPC.checkConsistency - , HPC.checkConservativityNode + , HPC.checkConservativityEdge -- Hets.InfoCommands , HIC.getGraphForLibrary diff --git a/HetsAPI/DataTypes.hs b/HetsAPI/DataTypes.hs index 3c47425f86..89328ee80c 100644 --- a/HetsAPI/DataTypes.hs +++ b/HetsAPI/DataTypes.hs @@ -5,26 +5,40 @@ License : GPLv2 or higher, see LICENSE.txt -} module HetsAPI.DataTypes ( Sentence - , SentenceByName + , TheorySentence + , TheorySentenceByName , SignatureJSON , SymbolJSON , GenericTransportType - , TheoryPointer) where -import qualified Common.OrderedMap as OMap + , TheoryPointer + , LinkPointer) where + import Data.ByteString.Lazy(ByteString) +import Data.Graph.Inductive.Graph (LNode, LEdge) +import Common.AS_Annotation(SenAttr(..)) import Common.LibName (LibName) -import Static.DevGraph (LibEnv, DGraph, DGNodeLab) +import qualified Common.OrderedMap as OMap + +import Logic.Comorphism (AnyComorphism(..)) +import Logic.Prover(ThmStatus(..)) + +import Static.DevGraph (LibEnv, DGraph, DGNodeLab, DGLinkLab) +import Static.GTheory (BasicProof(..)) + -import Data.Graph.Inductive.Graph (LNode) -- | In the API if it is not possible to export the generic type, they are converted to JSON. type GenericTransportType = ByteString + type Sentence = GenericTransportType -type SentenceByName = OMap.OMap String Sentence + +type TheorySentence = SenAttr Sentence (ThmStatus (AnyComorphism, BasicProof)) +type TheorySentenceByName = OMap.OMap String TheorySentence type SignatureJSON = GenericTransportType type SymbolJSON = GenericTransportType type TheoryPointer = (LibName, LibEnv, DGraph, LNode DGNodeLab) +type LinkPointer = (LibName, LibEnv, LEdge DGLinkLab) diff --git a/HetsAPI/InfoCommands.hs b/HetsAPI/InfoCommands.hs index aec266e6b1..ed3873a217 100644 --- a/HetsAPI/InfoCommands.hs +++ b/HetsAPI/InfoCommands.hs @@ -19,24 +19,44 @@ module HetsAPI.InfoCommands ( , getUnprovenGoals , prettySentence , prettySentenceOfTheory + , prettyTheory + , getDevelopmentGraphNodeType + , theorySentenceIsAxiom + , theorySentenceWasTheorem + , theorySentenceIsDefined + , theorySentenceGetTheoremStatus + , theorySentencePriority + , theorySentenceContent + , theorySentenceBestProof + , getLibraryDependencies + , getRefinementTree + , getAvailableSpecificationsForRefinement ) where -import HetsAPI.DataTypes (TheoryPointer) +import Data.Aeson (encode, eitherDecode, Result(..), ToJSON) +import Data.Dynamic +import Data.Graph.Inductive (LNode, LEdge, mkGraph, labNodes, subgraph, suc) +import qualified Data.Map as Map +import qualified Data.Set as Set +import Common.AS_Annotation (SenAttr (..)) +import Common.DocUtils (pretty, showDoc) import Common.LibName (LibName) -import Static.DevGraph (LibEnv, lookupDGraph, labNodesDG, labEdgesDG, DGraph, DGNodeLab, DGLinkLab, getDGNodeName) -import Data.Graph.Inductive (LNode, LEdge) -import Static.GTheory (G_theory (..), isProvenSenStatus) +import qualified Common.Lib.Graph as Graph +import qualified Common.Lib.Rel as Rel import qualified Common.OrderedMap as OMap -import Common.AS_Annotation (sentence, SenAttr (isAxiom)) -import Data.Aeson (encode, eitherDecode, Result(..)) + import Logic.Logic(Logic) -import Common.DocUtils (pretty) +import Logic.Prover(ThmStatus(..)) +import Logic.Comorphism(AnyComorphism(..)) +import HetsAPI.DataTypes (TheoryPointer, TheorySentenceByName, TheorySentence, Sentence) +import qualified HetsAPI.Refinement as Refinement -import Data.Dynamic +import Static.DevGraph (LibEnv, lookupDGraph, labNodesDG, labEdgesDG, DGraph, DGNodeLab, DGLinkLab, getDGNodeName, getRealDGNodeType, getLibDepRel, RTNodeLab, RTLinkLab, refTree, specRoots) +import Static.DgUtils (DGNodeType) +import Static.GTheory (G_theory (..), isProvenSenStatus, BasicProof(..)) -import HetsAPI.DataTypes (SentenceByName, Sentence) -- | @getDevelopmentGraphByName name env@ returns the development graph for the @@ -62,24 +82,51 @@ getEdgesFromDevelopmentGraph = fmap (\(_, _, x) -> x) . labEdgesDG -- getGlobalAnnotations :: DGraph -> GlobalAnnos -getAllSentences :: G_theory -> SentenceByName -getAllSentences (G_theory _ _ _ _ sens _) = OMap.map (encode . sentence) sens +theorySentenceIsAxiom :: SenAttr a (ThmStatus tStatus) -> Bool +theorySentenceIsAxiom = isAxiom + +theorySentenceWasTheorem :: SenAttr a (ThmStatus tStatus) -> Bool +theorySentenceWasTheorem = wasTheorem + +theorySentenceIsDefined :: SenAttr a (ThmStatus tStatus) -> Bool +theorySentenceIsDefined = isDef + +theorySentenceGetTheoremStatus :: SenAttr a (ThmStatus tStatus) -> [tStatus] +theorySentenceGetTheoremStatus = getThmStatus . senAttr + +theorySentencePriority :: SenAttr a (ThmStatus tStatus) -> Maybe String +theorySentencePriority = priority + +theorySentenceContent :: SenAttr a (ThmStatus tStatus) -> a +theorySentenceContent = sentence + +theorySentenceBestProof :: Ord proof => SenAttr a (ThmStatus (c, proof)) -> Maybe proof +theorySentenceBestProof sentence = + if null ts then Nothing else Just $ maximum $ map snd ts + where ts = theorySentenceGetTheoremStatus sentence + -getAllAxioms :: G_theory -> SentenceByName -getAllAxioms (G_theory _ _ _ _ sens _) = OMap.map (encode . sentence) +toTheorySentence :: ToJSON sentence => SenAttr sentence (ThmStatus (AnyComorphism, BasicProof)) -> TheorySentence +toTheorySentence sen = sen { sentence = encode . sentence $ sen } + +getAllSentences :: G_theory -> TheorySentenceByName +getAllSentences (G_theory _ _ _ _ sens _) = OMap.map toTheorySentence sens + +getAllAxioms :: G_theory -> TheorySentenceByName +getAllAxioms (G_theory _ _ _ _ sens _) = OMap.map toTheorySentence $ OMap.filter isAxiom sens -getAllGoals :: G_theory -> SentenceByName -getAllGoals (G_theory _ _ _ _ sens _) = OMap.map (encode . sentence) +getAllGoals :: G_theory -> TheorySentenceByName +getAllGoals (G_theory _ _ _ _ sens _) = OMap.map toTheorySentence $ OMap.filter (not . isAxiom) sens -getProvenGoals :: G_theory -> SentenceByName -getProvenGoals (G_theory _ _ _ _ sens _) = OMap.map (encode . sentence) +getProvenGoals :: G_theory -> TheorySentenceByName +getProvenGoals (G_theory _ _ _ _ sens _) = OMap.map toTheorySentence $ OMap.filter (\sen -> not (isAxiom sen) && isProvenSenStatus sen) sens -getUnprovenGoals :: G_theory -> SentenceByName -getUnprovenGoals (G_theory _ _ _ _ sens _) = OMap.map (encode . sentence) - $ OMap.filter (\sen -> isAxiom sen && isProvenSenStatus sen) sens +getUnprovenGoals :: G_theory -> TheorySentenceByName +getUnprovenGoals (G_theory _ _ _ _ sens _) = OMap.map toTheorySentence + $ OMap.filter (\sen -> not (isAxiom sen) && not (isProvenSenStatus sen)) sens prettySentence' :: Logic lid sublogics basic_spec sentence symb_items symb_map_items sign morphism symbol raw_symbol proof_tree => sentence -> lid -> Sentence -> String @@ -91,5 +138,23 @@ prettySentence :: Logic lid sublogics basic_spec sentence symb_items symb_map_it lid -> Sentence -> String prettySentence = prettySentence' (undefined :: sentence) +prettyTheory :: G_theory -> String +prettyTheory th = showDoc th "\n" + prettySentenceOfTheory :: G_theory -> Sentence -> String -prettySentenceOfTheory (G_theory lid _ _ _ _ _) = prettySentence lid \ No newline at end of file +prettySentenceOfTheory (G_theory lid _ _ _ _ _) = prettySentence lid + +getDevelopmentGraphNodeType :: DGNodeLab -> DGNodeType +getDevelopmentGraphNodeType = getRealDGNodeType + +getLibraryDependencies :: LibEnv -> [(LibName, LibName)] +getLibraryDependencies = + Rel.toList . Rel.intransKernel . getLibDepRel + + +getRefinementTree :: String -> DGraph -> Maybe (Graph.Gr Refinement.RefinementTreeNode Refinement.RefinementTreeLink) +getRefinementTree = Refinement.getRefinementTree + +getAvailableSpecificationsForRefinement :: DGraph -> [String] +getAvailableSpecificationsForRefinement = Refinement.getAvailableSpecificationsForRefinement + diff --git a/HetsAPI/Internal.hs b/HetsAPI/Internal.hs index d1198c02a2..40951477c7 100644 --- a/HetsAPI/Internal.hs +++ b/HetsAPI/Internal.hs @@ -6,29 +6,39 @@ License : GPLv2 or higher, see LICENSE.txt module HetsAPI.Internal ( fromJust - , Result, resultToMaybe, Diagnosis - , HetcatsOpts, defaultHetcatsOpts + , Result(..), resultToMaybe, Diagnosis + , HetcatsOpts(..), defaultHetcatsOpts , DGraph, DGNodeLab, DGLinkLab() + , DGNodeType, nodeTypeIsProven, nodeTypeIsProvenConsistent, nodeTypeIsReference + , DevGraphLinkType(..), DevGraphLinkKind(..), getDevGraphLinkType + -- , DGEdgeType, edgeTypeModInc, edgeTypeisInc + -- , DGEdgeTypeModInc, GlobalDef, HetDef, HidingDef, LocalDef, FreeOrCofreeDef, ThmType, thmEdgeType, isProvenEdge, isConservativ, isPending , developmentGraphNodeLabelName , developmentGraphEdgeLabelName - , ProofStatus - , GoalStatus + , developmentGraphEdgeLabelId + , ProofStatus(..) + , GoalStatus(..) , TimeOfDay , TacticScript + , tacticScriptContent , ProofState , ConsistencyStatus , consistencyStatusType - , SType + , consistencyStatusMessage + , SType(..) , ConsStatus , requiredConservativity , provenConservativity , linkStatus - , Conservativity + , Conservativity(..) , getNodeConsStatus + , getEdgeConsStatus , getConsOfStatus , isProvenConsStatusLink , showConsistencyStatus - , getDGNodeName + , isInternalNode + , referencedNodeLibName + , isNodeReferenceNode , ExtSign , plainSign , nonImportedSymbols @@ -44,39 +54,126 @@ module HetsAPI.Internal ( , Token , Id - , LibName + , LibName(..) + , getFilePath , LibEnv , IRI + + , Gr + , labNodes + , labEdges + , LNode + , LEdge + + , RTNodeLab (..) + , RTLinkLab (..) + , RTLinkType(..) + + + , showGlobalDoc + , showDoc + + -- , optsWithAnalysis + -- , optsWithGuiType + , optsWithUrlCatalog + , optsWithInfiles + , optsWithSpecNames + , optsWithTransNames + , optsWithLossyTrans + , optsWithViewNames + -- , optsWithIntype + , optsWithLibdirs + , optsWithModelSparQ + , optsWithCounterSparQ + , optsWithOutdir + -- , optsWithOuttypes + , optsWithDatabaseDoMigrate + , optsWithDatabaseOutputFile + , optsWithDatabaseConfigFile + , optsWithDatabaseSubConfigKey + , optsWithDatabaseFileVersionId + , optsWithDatabaseReanalyze + -- , optsWithDatabaseConfig + -- , optsWithDatabaseContext + , optsWithXupdate + , optsWithRecurse + , optsWithVerbose + , optsWithDefLogic + , optsWithDefSyntax + , optsWithOutputToStdout + -- , optsWithCaslAmalg + , optsWithInteractive + , optsWithConnectP + , optsWithConnectH + , optsWithUncolored + , optsWithXmlFlag + , optsWithApplyAutomatic + , optsWithComputeNormalForm + , optsWithDumpOpts + , optsWithDisableCertificateVerification + -- , optsWithIoEncoding + , optsWithUseLibPos + , optsWithUnlit + , optsWithServe + , optsWithListen + , optsWithPidFile + , optsWithWhitelist + , optsWithBlacklist + , optsWithRunMMT + , optsWithFullTheories + , optsWithOutputLogicList + , optsWithOutputLogicGraph + , optsWithFileType + , optsWithAccessToken + , optsWithHttpRequestHeaders + , optsWithFullSign + , optsWithPrintAST ) where import Data.Maybe (fromJust) import Data.Time (TimeOfDay) +import Common.DocUtils(showGlobalDoc, showDoc) import Common.ExtSign (ExtSign(..)) import Common.Consistency(Conservativity(..), showConsistencyStatus) import Common.GlobalAnnotations import Common.Id import Common.IRI -import Common.LibName (LibName) -import Common.Result (Result, resultToMaybe, Diagnosis) -import Driver.Options (HetcatsOpts, defaultHetcatsOpts) -import Static.DevGraph (DGraph, DGNodeLab(..), DGLinkLab(..), getNodeConsStatus, getNodeCons, getDGNodeName, globalAnnos, LibEnv) -import Static.DgUtils (ConsStatus(..), getConsOfStatus, isProvenConsStatusLink, NodeName) -import Logic.Prover (ProofStatus, GoalStatus, TacticScript) +import Common.LibName (LibName(..), getFilePath) +import Common.Lib.Graph (Gr(..)) +import Common.Result (Result(..), resultToMaybe, Diagnosis) +import Data.Graph.Inductive (labNodes, labEdges, LNode, LEdge) +import Driver.Options (HetcatsOpts(..), defaultHetcatsOpts) +import Static.DevGraph (DGraph, DGNodeLab(..), DGLinkLab(..), DGNodeInfo(..), getNodeConsStatus, getEdgeConsStatus, getNodeCons, getDGNodeName, globalAnnos, LibEnv, isInternalNode, getRealDGLinkType, isDGRef, dgn_libname, RTNodeLab(..), RTLinkLab(..), RTLinkType(..)) +import Static.DgUtils (ConsStatus(..), getConsOfStatus, isProvenConsStatusLink, NodeName, DGNodeType(..), DGEdgeType(..), DGEdgeTypeModInc(..), Scope(..), ThmTypes(..), FreeOrCofree(..), ConsStatus(..), getEdgeNum) +import Logic.Prover (ProofStatus(..), GoalStatus(..), TacticScript(..)) import Proofs.AbstractState (ProofState) -import Proofs.ConsistencyCheck (ConsistencyStatus(..), SType) +import Proofs.ConsistencyCheck (ConsistencyStatus(..), SType(..)) + +developmentGraphNodeLabelName :: DGNodeLab -> String +developmentGraphNodeLabelName = getDGNodeName -developmentGraphNodeLabelName :: DGNodeLab -> NodeName -developmentGraphNodeLabelName = dgn_name +developmentGraphNodeConsistencyStatus :: DGNodeLab -> Maybe ConsStatus +developmentGraphNodeConsistencyStatus node = case nodeInfo node of + DGNode _ status -> Just status + _ -> Nothing developmentGraphEdgeLabelName :: DGLinkLab -> String developmentGraphEdgeLabelName = dglName +developmentGraphEdgeLabelId :: DGLinkLab -> Int +developmentGraphEdgeLabelId = getEdgeNum . dgl_id + + consistencyStatusType :: ConsistencyStatus -> SType consistencyStatusType = sType + +consistencyStatusMessage :: ConsistencyStatus -> String +consistencyStatusMessage = sMessage + globalAnnotations :: DGraph -> GlobalAnnos globalAnnotations = globalAnnos @@ -94,3 +191,161 @@ literalAnnos = literal_annos prefixMap :: GlobalAnnos -> PrefixMap prefixMap = prefix_map + +nodeTypeIsProven :: DGNodeType -> Bool +nodeTypeIsProven = isProvenNode + +nodeTypeIsProvenConsistent :: DGNodeType -> Bool +nodeTypeIsProvenConsistent = isProvenCons + +nodeTypeIsReference :: DGNodeType -> Bool +nodeTypeIsReference = isRefType + +tacticScriptContent :: TacticScript -> String +tacticScriptContent (TacticScript c) = c + + +data DevGraphLinkKind = LinkKindGlobal | LinkKindLocal | LinkKindHiding | LinkKindFree | LinkKindCofree +data DevGraphLinkType = + DefinitionLink { linkTypeKind :: DevGraphLinkKind, linkTypeIsInclusion :: Bool, linkTypeIsHomogenoeous :: Bool } + | TheoremLink { linkTypeKind :: DevGraphLinkKind, linkTypeIsInclusion :: Bool, linkTypeIsHomogenoeous :: Bool, linkTypeIsProven :: Bool, linkTypeIsConservativ :: Bool, linkTypeIsPending :: Bool } + +getDevGraphLinkType :: DGLinkLab -> DevGraphLinkType +getDevGraphLinkType l = case edgeTypeModInc (getRealDGLinkType l) of + GlobalDef -> DefinitionLink LinkKindGlobal inclusion True + HetDef -> DefinitionLink LinkKindGlobal inclusion False + HidingDef -> DefinitionLink LinkKindHiding inclusion True + LocalDef -> DefinitionLink LinkKindLocal inclusion True + FreeOrCofreeDef freeOrCofree -> DefinitionLink (case freeOrCofree of + Free -> LinkKindFree + Cofree -> LinkKindCofree) inclusion True + ThmType {thmEdgeType=thmType, isProvenEdge=proven, isConservativ=conservativ, isPending=pending} -> TheoremLink kind inclusion homogeneous proven conservativ pending + where + (kind, homogeneous) = case thmType of + HidingThm -> (LinkKindHiding, True) + FreeOrCofreeThm freeOrCofree -> case freeOrCofree of + Free -> (LinkKindFree, True) + Cofree -> (LinkKindCofree, True) + GlobalOrLocalThm scope isHomogeneous -> case scope of + Global -> (LinkKindGlobal, isHomogeneous) + Local -> (LinkKindLocal, isHomogeneous) + where + inclusion = isInc . getRealDGLinkType $ l + + +referencedNodeLibName :: DGNodeLab -> LibName +referencedNodeLibName = dgn_libname + +isNodeReferenceNode :: DGNodeLab -> Bool +isNodeReferenceNode = isDGRef + + +-- optsWithAnalysis :: HetcatsOpts -> AnaType -> HetcatsOpts +-- optsWithAnalysis o v = o {analysis = v} +optsWithUrlCatalog :: HetcatsOpts -> [(String, String)] -> HetcatsOpts +optsWithUrlCatalog o v = o {urlCatalog = v} +optsWithInfiles :: HetcatsOpts -> [FilePath] -> HetcatsOpts +optsWithInfiles o v = o {infiles = v} +optsWithSpecNames :: HetcatsOpts -> [SIMPLE_ID] -> HetcatsOpts +optsWithSpecNames o v = o {specNames = v} +optsWithTransNames :: HetcatsOpts -> [SIMPLE_ID] -> HetcatsOpts +optsWithTransNames o v = o {transNames = v} +optsWithLossyTrans :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithLossyTrans o v = o {lossyTrans = v} +optsWithViewNames :: HetcatsOpts -> [SIMPLE_ID] -> HetcatsOpts +optsWithViewNames o v = o {viewNames = v} +-- optsWithIntype :: HetcatsOpts -> InType -> HetcatsOpts +-- optsWithIntype o v = o {intype = v} +optsWithLibdirs :: HetcatsOpts -> [FilePath] -> HetcatsOpts +optsWithLibdirs o v = o {libdirs = v} +optsWithModelSparQ :: HetcatsOpts -> FilePath -> HetcatsOpts +optsWithModelSparQ o v = o {modelSparQ = v} +optsWithCounterSparQ :: HetcatsOpts -> Int -> HetcatsOpts +optsWithCounterSparQ o v = o {counterSparQ = v} +optsWithOutdir :: HetcatsOpts -> FilePath -> HetcatsOpts +optsWithOutdir o v = o {outdir = v} +-- optsWithOuttypes :: HetcatsOpts -> [OutType] -> HetcatsOpts +-- optsWithOuttypes o v = o {outtypes = v} +optsWithDatabaseDoMigrate :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithDatabaseDoMigrate o v = o {databaseDoMigrate = v} +optsWithDatabaseOutputFile :: HetcatsOpts -> FilePath -> HetcatsOpts +optsWithDatabaseOutputFile o v = o {databaseOutputFile = v} +optsWithDatabaseConfigFile :: HetcatsOpts -> FilePath -> HetcatsOpts +optsWithDatabaseConfigFile o v = o {databaseConfigFile = v} +optsWithDatabaseSubConfigKey :: HetcatsOpts -> String -> HetcatsOpts +optsWithDatabaseSubConfigKey o v = o {databaseSubConfigKey = v} +optsWithDatabaseFileVersionId :: HetcatsOpts -> String -> HetcatsOpts +optsWithDatabaseFileVersionId o v = o {databaseFileVersionId = v} +optsWithDatabaseReanalyze :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithDatabaseReanalyze o v = o {databaseReanalyze = v} +-- optsWithDatabaseConfig :: HetcatsOpts -> DBConfig.DBConfig -> HetcatsOpts +-- optsWithDatabaseConfig o v = o {databaseConfig = v} +-- optsWithDatabaseContext :: HetcatsOpts -> DBConfig.DBContext -> HetcatsOpts +-- optsWithDatabaseContext o v = o {databaseContext = v} +optsWithXupdate :: HetcatsOpts -> FilePath -> HetcatsOpts +optsWithXupdate o v = o {xupdate = v} +optsWithRecurse :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithRecurse o v = o {recurse = v} +optsWithVerbose :: HetcatsOpts -> Int -> HetcatsOpts +optsWithVerbose o v = o {verbose = v} +optsWithDefLogic :: HetcatsOpts -> String -> HetcatsOpts +optsWithDefLogic o v = o {defLogic = v} +optsWithDefSyntax :: HetcatsOpts -> String -> HetcatsOpts +optsWithDefSyntax o v = o {defSyntax = v} +optsWithOutputToStdout :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithOutputToStdout o v = o {outputToStdout = v} +-- optsWithCaslAmalg :: HetcatsOpts -> [CASLAmalgOpt] -> HetcatsOpts +-- optsWithCaslAmalg o v = o {caslAmalg = v} +optsWithInteractive :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithInteractive o v = o {interactive = v} +optsWithConnectP :: HetcatsOpts -> Int -> HetcatsOpts +optsWithConnectP o v = o {connectP = v} +optsWithConnectH :: HetcatsOpts -> String -> HetcatsOpts +optsWithConnectH o v = o {connectH = v} +optsWithUncolored :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithUncolored o v = o {uncolored = v} +optsWithXmlFlag :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithXmlFlag o v = o {xmlFlag = v} +optsWithApplyAutomatic :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithApplyAutomatic o v = o {applyAutomatic = v} +optsWithComputeNormalForm :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithComputeNormalForm o v = o {computeNormalForm = v} +optsWithDumpOpts :: HetcatsOpts -> [String] -> HetcatsOpts +optsWithDumpOpts o v = o {dumpOpts = v} +optsWithDisableCertificateVerification :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithDisableCertificateVerification o v = o {disableCertificateVerification = v} +-- optsWithIoEncoding :: HetcatsOpts -> Enc -> HetcatsOpts +-- optsWithIoEncoding o v = o {ioEncoding = v} +optsWithUseLibPos :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithUseLibPos o v = o {useLibPos = v} +optsWithUnlit :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithUnlit o v = o {unlit = v} +optsWithServe :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithServe o v = o {serve = v} +optsWithListen :: HetcatsOpts -> Int -> HetcatsOpts +optsWithListen o v = o {listen = v} +optsWithPidFile :: HetcatsOpts -> FilePath -> HetcatsOpts +optsWithPidFile o v = o {pidFile = v} +optsWithWhitelist :: HetcatsOpts -> [[String]] -> HetcatsOpts +optsWithWhitelist o v = o {whitelist = v} +optsWithBlacklist :: HetcatsOpts -> [[String]] -> HetcatsOpts +optsWithBlacklist o v = o {blacklist = v} +optsWithRunMMT :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithRunMMT o v = o {runMMT = v} +optsWithFullTheories :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithFullTheories o v = o {fullTheories = v} +optsWithOutputLogicList :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithOutputLogicList o v = o {outputLogicList = v} +optsWithOutputLogicGraph :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithOutputLogicGraph o v = o {outputLogicGraph = v} +optsWithFileType :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithFileType o v = o {fileType = v} +optsWithAccessToken :: HetcatsOpts -> String -> HetcatsOpts +optsWithAccessToken o v = o {accessToken = v} +optsWithHttpRequestHeaders :: HetcatsOpts -> [String] -> HetcatsOpts +optsWithHttpRequestHeaders o v = o {httpRequestHeaders = v} +optsWithFullSign :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithFullSign o v = o {fullSign = v} +optsWithPrintAST :: HetcatsOpts -> Bool -> HetcatsOpts +optsWithPrintAST o v = o {printAST = v} + diff --git a/HetsAPI/ProveCommands.hs b/HetsAPI/ProveCommands.hs index 281e5c6dd6..110bb21d17 100644 --- a/HetsAPI/ProveCommands.hs +++ b/HetsAPI/ProveCommands.hs @@ -4,9 +4,11 @@ Copyright : (c) Otto-von-Guericke University of Magdeburg License : GPLv2 or higher, see LICENSE.txt -} module HetsAPI.ProveCommands ( - getAvailableComorphisms + getTheoryForSelection + , getAvailableComorphisms , getUsableProvers , getUsableConsistencyCheckers + , Interfaces.Utils.getUsableConservativityCheckers , proveNode , recordProofResult @@ -16,41 +18,51 @@ module HetsAPI.ProveCommands ( , recordConsistencyResult , checkConsistencyAndRecord - , checkConservativityNode + , checkConservativityEdge + , recordConservativityResult + , checkConservativityEdgeAndRecord , ProofOptions(..) , defaultProofOptions , ConsCheckingOptions(..) , defaultConsCheckingOptions , recomputeNode + + , genericProveBatch ) where import HetsAPI.DataTypes import Data.Functor () import qualified Data.Map as Map -import Data.Graph.Inductive (LNode) +import Data.Maybe (fromJust) +import Data.Graph.Inductive (LNode, LEdge) import Control.Monad.Trans ( MonadTrans(lift) ) +import Common.Consistency (Conservativity) +import Common.Id (nullRange) import Common.LibName (LibName) -import Common.ResultT (ResultT) +import qualified Common.OrderedMap as OMap +import Common.Result (Result(maybeResult, Result), maybeToResult) +import Common.ResultT (ResultT (..), liftR) import Comorphisms.LogicGraph (logicGraph) -import qualified Interfaces.Utils (checkConservativityNode) +import qualified Interfaces.Utils (checkConservativityNode, getUsableConservativityCheckers, checkConservativityEdgeWith, recordConservativityResult) import Logic.Comorphism (AnyComorphism) import Logic.Grothendieck (findComorphismPaths) -import Logic.Prover (ProofStatus, ProverKind (..)) +import Logic.Prover (ProofStatus (goalName), ProverKind (..)) -import Proofs.AbstractState (G_prover, ProofState, G_proof_tree, autoProofAtNode, G_cons_checker (..), getProverName, getConsCheckers, getCcName) +import Proofs.AbstractState (G_prover, ProofState, G_proof_tree, autoProofAtNode, G_cons_checker (..), G_conservativity_checker (..), getProverName, getConsCheckers, getCcName, makeTheoryForSentences) import qualified Proofs.AbstractState as PAS import Proofs.ConsistencyCheck (ConsistencyStatus, SType(..), consistencyCheck, sType) +import Proofs.BatchProcessing (genericProveBatch) import Static.ComputeTheory(updateLabelTheory, recomputeNodeLabel) -import Static.DevGraph (LibEnv, DGraph, DGNodeLab, ProofHistory, DGChange(..), dgn_theory, markNodeInconsistent, markNodeConsistent) -import Static.GTheory (G_theory (..), sublogicOfTh) +import Static.DevGraph (LibEnv, DGraph, DGNodeLab, DGLinkLab, ProofHistory, DGChange(..), globalTheory, markNodeInconsistent, markNodeConsistent) +import Static.GTheory (G_theory (..), sublogicOfTh, coerceThSens) import Static.History (changeDGH) data ProofOptions = ProofOptions { @@ -90,7 +102,11 @@ defaultConsCheckingOptions = ConsCheckingOptions { type ProofResult = (G_theory -- The new theory , [ProofStatus G_proof_tree]) -- ProofStatus of each goal +type ConservativityResult = (Conservativity, G_theory, G_theory) + +getTheoryForSelection :: [String] -> [String] -> [String] -> G_theory -> G_theory +getTheoryForSelection = makeTheoryForSentences -- | @getAvailableComorphisms theory@ yields all available comorphisms for @theory@ getAvailableComorphisms :: G_theory -> [AnyComorphism] @@ -108,8 +124,9 @@ getUsableProvers th = PAS.getUsableProvers ProveCMDLautomatic (sublogicOfTh th) -- | @proveNode theory prover comorphism@ proves all goals in @theory@ using all -- all axioms in @theory@. If @prover@ or @comorphism@ is @Nothing@ the first -- usable prover or comorphism, respectively, is used. -proveNode :: G_theory -> ProofOptions -> ResultT IO ProofResult -proveNode theory (ProofOptions proverM comorphismM useTh goals axioms timeout) = do +proveNode :: TheoryPointer -> ProofOptions -> ResultT IO ProofResult +proveNode (_, _, _, node) (ProofOptions proverM comorphismM useTh goals axioms timeout) = do + theory <- liftR . maybeToResult nullRange "No global theory!" . globalTheory . snd $ node (prover, comorphism) <- case (proverM, comorphismM) of (Just prover, Just comorphism) -> return (prover, comorphism) (Just prover, Nothing) -> do @@ -127,35 +144,47 @@ proveNode theory (ProofOptions proverM comorphismM useTh goals axioms timeout) = return (th, steps) recordProofResult :: TheoryPointer -> ProofResult -> LibEnv -recordProofResult (name, env, graph, node) (theory, statuses) = +recordProofResult (name, env, graph, node) (theory, statuses) = if null statuses then env - else Map.insert name ( updateLabelTheory env name graph node theory) env + else case new_theory of + Just th -> Map.insert name ( updateLabelTheory env name graph node th) env + Nothing -> env + where new_theory = do + original_theory <- globalTheory . snd $ node + recordProofResult' original_theory theory statuses + +recordProofResult' :: G_theory -> G_theory -> [ProofStatus G_proof_tree] -> Maybe G_theory +recordProofResult' (G_theory lid1 _ _ _ original_sens _) (G_theory lid2 syn sign signIdx result_sens sensIdx) statuses = do + original_sens' <- coerceThSens lid1 lid2 "String" original_sens + let new_sens = foldr (\status sens -> OMap.insert (goalName status) (fromJust . OMap.lookup (goalName status) $ result_sens) sens) original_sens' statuses + return $ G_theory lid2 syn sign signIdx new_sens sensIdx proveNodeAndRecord :: TheoryPointer -> ProofOptions -> ResultT IO (ProofResult, LibEnv) proveNodeAndRecord p@(_, _, _, node) opts = do - r <- proveNode (dgn_theory . snd $ node) opts + r <- proveNode p opts let env = recordProofResult p r return (r, env) checkConsistency :: TheoryPointer -> ConsCheckingOptions -> IO ConsistencyStatus -checkConsistency (libName, libEnv, dgraph, lnode) (ConsCheckingOptions ccM comorphismM b timeout) = do - let theory = dgn_theory . snd $ lnode - (cc, comorphism) <- case (ccM, comorphismM) of - (Just cc, Just comorphism) -> return (cc, comorphism) - (Just cc, Nothing) -> do - let ccName = getCcName cc - comorphism <- (snd . head . filter ((== ccName) . getCcName . fst) <$> getUsableConsistencyCheckers theory) - return (cc, comorphism) - (Nothing, Just comorphism) -> do - cc <- (fst . head . filter ((== comorphism) . snd) <$> getUsableConsistencyCheckers theory) - return (cc, comorphism) - (Nothing, Nothing) -> head <$> (getUsableConsistencyCheckers theory) - - consistencyCheck b cc comorphism libName libEnv dgraph lnode timeout +checkConsistency (libName, libEnv, dgraph, lnode) (ConsCheckingOptions ccM comorphismM b timeout) = case globalTheory . snd $ lnode of + Nothing -> fail "No global theory!" + Just theory -> do + (cc, comorphism) <- case (ccM, comorphismM) of + (Just cc, Just comorphism) -> return (cc, comorphism) + (Just cc, Nothing) -> do + let ccName = getCcName cc + comorphism <- snd . head . filter ((== ccName) . getCcName . fst) <$> getUsableConsistencyCheckers theory + return (cc, comorphism) + (Nothing, Just comorphism) -> do + cc <- fst . head . filter ((== comorphism) . snd) <$> getUsableConsistencyCheckers theory + return (cc, comorphism) + (Nothing, Nothing) -> head <$> getUsableConsistencyCheckers theory + + consistencyCheck b cc comorphism libName libEnv dgraph lnode timeout recordConsistencyResult :: TheoryPointer -> ConsistencyStatus -> LibEnv -recordConsistencyResult (name, env, graph, node@(i, label)) consStatus = +recordConsistencyResult (name, env, graph, node@(i, label)) consStatus = if sType consStatus == CSUnchecked then env else Map.insert name (changeDGH graph $ SetNodeLab label @@ -170,11 +199,19 @@ checkConsistencyAndRecord p opts = do let env = recordConsistencyResult p r return (r, env) -checkConservativityNode ::LNode DGNodeLab -> LibEnv -> LibName - -> IO (String, LibEnv, ProofHistory) -checkConservativityNode = Interfaces.Utils.checkConservativityNode False +checkConservativityEdge :: LinkPointer -> G_conservativity_checker -> IO (Result ConservativityResult) +checkConservativityEdge (name, env, edge) = Interfaces.Utils.checkConservativityEdgeWith edge env name + +recordConservativityResult :: LinkPointer -> ConservativityResult -> LibEnv +recordConservativityResult (name, env, edge) = Interfaces.Utils.recordConservativityResult edge env name + +checkConservativityEdgeAndRecord :: LinkPointer -> G_conservativity_checker -> IO (Result (ConservativityResult, LibEnv)) +checkConservativityEdgeAndRecord linkPtr cc = runResultT $ do + r <- ResultT $ checkConservativityEdge linkPtr cc + let env = recordConservativityResult linkPtr r + return (r, env) recomputeNode :: TheoryPointer -> LibEnv recomputeNode (name, env, graph, node@(i, label)) = - Map.insert name (changeDGH graph $ SetNodeLab label + Map.insert name (changeDGH graph $ SetNodeLab label (i, recomputeNodeLabel env name graph node)) env diff --git a/HetsAPI/Python.hs b/HetsAPI/Python.hs index 7af5c4ca9f..e9e980f6c1 100644 --- a/HetsAPI/Python.hs +++ b/HetsAPI/Python.hs @@ -11,18 +11,29 @@ module HetsAPI.Python ( , PyProofOptions(..) , PyProofTree , PyConsChecker + , PyConservativityChecker , PyConsCheckingOptions(..) , PyGMorphism + , PyBasicProof(..) + , PyTheorySentence + , PyTheorySentenceByName , mkPyProofOptions -- Wrapped with Py* + + , getTheoryForSelection + , sublogicOfPyTheory , theoryOfNode , getUsableProvers , translateTheory , getAvailableComorphisms , getUsableConsistencyCheckers + , getUsableConservativityCheckers , checkConsistency , checkConsistencyAndRecord + , checkConservativityEdge + , checkConservativityEdgeAndRecord , proveNode + , recordProofResult , proveNodeAndRecord , getAllSentences , getAllAxioms @@ -30,9 +41,13 @@ module HetsAPI.Python ( , getProvenGoals , getUnprovenGoals , consCheckerName + , conservativityCheckerName + , conservativityCheckerUsable , comorphismName , proverName , prettySentence + , prettyTheory + , showTheory , defaultProofOptions , defaultConsCheckingOptions , globalTheory @@ -40,6 +55,7 @@ module HetsAPI.Python ( , comorphismOfGMorphism , signatureOfGMorphism , signatureOfTheory + , pyProofStatusOfPyBasicProof , logicNameOfTheory , logicDescriptionOfTheory @@ -53,10 +69,11 @@ module HetsAPI.Python ( , codomainOfGMorphism , isGMorphismInclusion , gMorphismToTransportType + , theorySentenceBestProof -- Unchanged re-export from Hets.ProveCommands - , HP.checkConservativityNode , HP.recomputeNode + , HP.recordConservativityResult -- Unchanged re-export from Hets.Commands , HC.automatic @@ -79,13 +96,22 @@ module HetsAPI.Python ( , HC.libFlatHeterogen , HC.qualifyLibEnv , HC.loadLibrary - , HC.showTheory - + , HI.getGraphForLibrary , HI.getNodesFromDevelopmentGraph , HI.getLNodesFromDevelopmentGraph , HI.getEdgesFromDevelopmentGraph , HI.getLEdgesFromDevelopmentGraph + , HI.getDevelopmentGraphNodeType + , HI.theorySentenceIsAxiom + , HI.theorySentenceWasTheorem + , HI.theorySentenceIsDefined + , HI.theorySentenceGetTheoremStatus + , HI.theorySentencePriority + , HI.theorySentenceContent + , HI.getLibraryDependencies + , HI.getRefinementTree + , HI.getAvailableSpecificationsForRefinement , fstOf3 , sndOf3 @@ -94,10 +120,15 @@ module HetsAPI.Python ( -- Datatypes , HDT.Sentence - , HDT.SentenceByName + , HDT.TheorySentence + , HDT.TheorySentenceByName , HDT.TheoryPointer + , HDT.LinkPointer , HDT.SignatureJSON , HDT.SymbolJSON + + , Refinement.RefinementTreeNode(..) + , Refinement.RefinementTreeLink(..) ) where @@ -107,33 +138,39 @@ import qualified HetsAPI.Commands as HC import qualified HetsAPI.ProveCommands as HP import qualified HetsAPI.InfoCommands as HI import qualified HetsAPI.DataTypes as HDT +import HetsAPI.DataTypes (TheorySentence, Sentence) +import Common.AS_Annotation (SenAttr(..)) +import Common.Consistency (Conservativity) import Common.DocUtils (pretty) import Common.ExtSign (ExtSign(..)) import Common.LibName (LibName) +import qualified Common.OrderedMap as OMap import Common.Result (Result) import Common.ResultT (ResultT (runResultT)) import Data.Aeson(encode, object, (.=)) import Data.Bifunctor (bimap) import Data.Functor -import Data.Graph.Inductive (LNode, lab) -import qualified Data.Map as Map +import Data.Graph.Inductive (lab, LEdge) import qualified Data.Set as Set import Logic.Comorphism (AnyComorphism(..), targetLogic, sourceLogic) import Logic.Grothendieck (GMorphism(..)) -import Logic.Logic (sym_of, language_name, description, dom, cod, isInclusion, symmap_of) -import Logic.Prover (ProofStatus(..)) +import Logic.Logic (sym_of, language_name, description, dom, cod, isInclusion) +import Logic.Prover (ProofStatus(..), ThmStatus(..)) + import Static.DevGraph (DGNodeLab (dgn_theory), DGLinkLab(dgl_morphism), LibEnv, DGraph, dgBody) import qualified Static.DevGraph (DGNodeLab(globalTheory)) import qualified Static.GTheory as GT -import Proofs.ConsistencyCheck (ConsistencyStatus) +import Static.GTheory (BasicProof(..), sublogicOfTh) + import qualified Proofs.AbstractState -import Proofs.AbstractState (G_prover, ProofState, G_proof_tree, G_cons_checker) -import HetsAPI.DataTypes (SentenceByName, Sentence) -import Common.XPath (BasicType(String)) -import Static.GTheory (G_theory) +import Proofs.AbstractState (G_prover, G_proof_tree(..), G_cons_checker, G_conservativity_checker, conservativityCheckerId) +import Proofs.ConsistencyCheck (ConsistencyStatus) + +import qualified HetsAPI.Refinement as Refinement + fstOf3 :: (a,b,c) -> a fstOf3 (x, _, _) = x @@ -149,9 +186,52 @@ thd (_, _, x) = x; data PyTheory = PyTheory GT.G_theory data PyProver = PyProver G_prover data PyConsChecker = PyConsChecker G_cons_checker +data PyConservativityChecker = PyConservativityChecker G_conservativity_checker data PyComorphism = PyComorphism AnyComorphism data PyProofTree = PyProofTree G_proof_tree + +toPyProofTree :: G_proof_tree -> PyProofTree +toPyProofTree = PyProofTree + +fromPyProofTree :: PyProofTree -> G_proof_tree +fromPyProofTree (PyProofTree tree ) = tree + data PyGMorphism = PyGMorphism GMorphism +data PyBasicProof = PyBasicProof BasicProof + | PyBasicProofGuessed + | PyBasicProofConjectured + | PyBasicProofHandwritten + +instance Eq PyBasicProof where + a == b = fromPyBasicProof a == fromPyBasicProof b + +instance Ord PyBasicProof where + compare a b = compare (fromPyBasicProof a) (fromPyBasicProof b) + +fromPyBasicProof :: PyBasicProof -> BasicProof +fromPyBasicProof pb = case pb of + PyBasicProof b -> b + PyBasicProofGuessed -> Guessed + PyBasicProofConjectured -> Conjectured + PyBasicProofHandwritten -> Handwritten + +toPyBasicProof :: BasicProof -> PyBasicProof +toPyBasicProof b = case b of + BasicProof _ _ -> PyBasicProof b + Guessed -> PyBasicProofGuessed + Conjectured -> PyBasicProofConjectured + Handwritten -> PyBasicProofHandwritten + +type PyTheorySentence = SenAttr Sentence (ThmStatus (PyComorphism, PyBasicProof)) +type PyTheorySentenceByName = OMap.OMap String PyTheorySentence + +toPyTheorySentence :: TheorySentence -> PyTheorySentence +toPyTheorySentence sen = sen {senAttr = ThmStatus (fmap (bimap PyComorphism toPyBasicProof) $ getThmStatus . senAttr $ sen)} + +pyProofStatusOfPyBasicProof :: PyBasicProof -> Maybe (ProofStatus PyProofTree) +pyProofStatusOfPyBasicProof b = case b of + PyBasicProof (BasicProof lid status) -> Just . toPyProofStatus $ status {proofTree = G_proof_tree lid $ proofTree status } + _ -> Nothing data PyProofOptions = PyProofOptions { proofOptsProver :: Maybe PyProver -- ^ The prover to use. If not set, it is selected automatically @@ -224,12 +304,11 @@ instance Show PyComorphism where show (PyComorphism c) = "PyComorphism "++ show c instance Show PyProofTree where - show (PyProofTree t) = "PyProofTree " ++ show t + show (PyProofTree t) = show t proverName :: PyProver -> String proverName (PyProver p) = Proofs.AbstractState.getProverName p - comorphismName :: PyComorphism -> String comorphismName (PyComorphism c) = show c @@ -248,9 +327,21 @@ sourceLogicDescriptionName (PyComorphism (Comorphism cid)) = description $ sourc consCheckerName :: PyConsChecker -> String consCheckerName (PyConsChecker cc) = Proofs.AbstractState.getCcName cc +conservativityCheckerName :: PyConservativityChecker -> String +conservativityCheckerName (PyConservativityChecker cc) = conservativityCheckerId cc + +conservativityCheckerUsable :: PyConservativityChecker -> IO (Maybe String) +conservativityCheckerUsable (PyConservativityChecker cc) = Proofs.AbstractState.conservativityCheckerUsable cc + theoryOfNode :: DGNodeLab -> PyTheory theoryOfNode = PyTheory . dgn_theory +sublogicOfPyTheory :: PyTheory -> String +sublogicOfPyTheory (PyTheory th) = show . sublogicOfTh $ th + +getTheoryForSelection :: [String] -> [String] -> [String] -> PyTheory -> PyTheory +getTheoryForSelection axioms goals theorems (PyTheory th) = PyTheory (HP.getTheoryForSelection axioms goals theorems th) + -- | @getUsableProvers theory@ checks for usable provers available on the machine getUsableProvers :: PyTheory -> IO [(PyProver, PyComorphism)] getUsableProvers (PyTheory th) = do @@ -259,20 +350,17 @@ getUsableProvers (PyTheory th) = do return $ fmap toPy provers toPyProofStatus :: ProofStatus G_proof_tree -> ProofStatus PyProofTree -toPyProofStatus status = ProofStatus { - goalName = goalName status - , goalStatus = goalStatus status - , usedAxioms = usedAxioms status - , usedProver = usedProver status - , proofTree = PyProofTree (proofTree status) - , usedTime = usedTime status - , tacticScript = tacticScript status - , proofLines = proofLines status - } - -proveNode :: PyTheory -> PyProofOptions -> IO (Result (PyTheory, [ProofStatus PyProofTree])) -proveNode (PyTheory theory) opts = - runResultT $ (\(th, statuses) -> (PyTheory th, toPyProofStatus <$> statuses)) <$> HP.proveNode theory (toProofOptions opts) +toPyProofStatus status = status { proofTree = toPyProofTree (proofTree status) } + +fromPyProofStatus :: ProofStatus PyProofTree -> ProofStatus G_proof_tree +fromPyProofStatus status = status {proofTree = fromPyProofTree (proofTree status)} + +proveNode :: HDT.TheoryPointer -> PyProofOptions -> IO (Result (PyTheory, [ProofStatus PyProofTree])) +proveNode ptr opts = + runResultT $ bimap PyTheory (toPyProofStatus <$>) <$> HP.proveNode ptr (toProofOptions opts) + +recordProofResult :: HDT.TheoryPointer -> (PyTheory, [ProofStatus PyProofTree]) -> LibEnv +recordProofResult ptr (PyTheory theory, statuses) = HP.recordProofResult ptr (theory, fmap fromPyProofStatus statuses) proveNodeAndRecord :: HDT.TheoryPointer -> PyProofOptions -> IO (Result ((PyTheory, [ProofStatus PyProofTree]), LibEnv)) proveNodeAndRecord ptr opts = @@ -295,30 +383,59 @@ checkConsistency ptr = HP.checkConsistency ptr . toConsCheckingOptions checkConsistencyAndRecord :: HDT.TheoryPointer -> PyConsCheckingOptions -> IO (ConsistencyStatus, LibEnv) checkConsistencyAndRecord ptr = HP.checkConsistencyAndRecord ptr . toConsCheckingOptions -getAllSentences :: PyTheory -> SentenceByName -getAllSentences (PyTheory theory) = HI.getAllSentences theory +getUsableConservativityCheckers :: LEdge DGLinkLab -> LibEnv -> LibName -> IO [PyConservativityChecker] +getUsableConservativityCheckers edge env = fmap (fmap PyConservativityChecker) . HP.getUsableConservativityCheckers edge env + +checkConservativityEdge :: PyConservativityChecker -> HDT.LinkPointer + -> IO (Result (Conservativity, PyTheory, PyTheory)) +checkConservativityEdge (PyConservativityChecker cc) linkPtr = do + res <- HP.checkConservativityEdge linkPtr cc + return $ do + (consv, expl, obl) <- res + return (consv, PyTheory expl, PyTheory obl) -getAllAxioms :: PyTheory -> SentenceByName -getAllAxioms (PyTheory theory) = HI.getAllAxioms theory +checkConservativityEdgeAndRecord :: PyConservativityChecker -> HDT.LinkPointer + -> IO (Result ((Conservativity, PyTheory, PyTheory), LibEnv)) +checkConservativityEdgeAndRecord (PyConservativityChecker cc) linkPtr = do + res <- HP.checkConservativityEdgeAndRecord linkPtr cc + return $ do + ((consv, expl, obl), env) <- res + return ((consv, PyTheory expl, PyTheory obl), env) -getAllGoals :: PyTheory -> SentenceByName -getAllGoals (PyTheory theory) = HI.getAllGoals theory -getProvenGoals :: PyTheory -> SentenceByName -getProvenGoals (PyTheory theory) = HI.getProvenGoals theory +getAllSentences :: PyTheory -> PyTheorySentenceByName +getAllSentences (PyTheory theory) = OMap.map toPyTheorySentence $ HI.getAllSentences theory -getUnprovenGoals :: PyTheory -> SentenceByName -getUnprovenGoals (PyTheory theory) = HI.getUnprovenGoals theory +getAllAxioms :: PyTheory -> PyTheorySentenceByName +getAllAxioms (PyTheory theory) = OMap.map toPyTheorySentence $ HI.getAllAxioms theory + +getAllGoals :: PyTheory -> PyTheorySentenceByName +getAllGoals (PyTheory theory) = OMap.map toPyTheorySentence $ HI.getAllGoals theory + +getProvenGoals :: PyTheory -> PyTheorySentenceByName +getProvenGoals (PyTheory theory) = OMap.map toPyTheorySentence $ HI.getProvenGoals theory + +getUnprovenGoals :: PyTheory -> PyTheorySentenceByName +getUnprovenGoals (PyTheory theory) = OMap.map toPyTheorySentence $ HI.getUnprovenGoals theory + +theorySentenceBestProof :: PyTheorySentence -> Maybe PyBasicProof +theorySentenceBestProof = HI.theorySentenceBestProof prettySentence :: PyTheory -> Sentence -> String prettySentence (PyTheory theory) = HI.prettySentenceOfTheory theory +prettyTheory :: PyTheory -> String +prettyTheory (PyTheory theory) = HI.prettyTheory theory + signatureOfTheory :: PyTheory -> ExtSign HDT.SignatureJSON HDT.SymbolJSON signatureOfTheory (PyTheory GT.G_theory { GT.gTheorySign = sig }) = ExtSign { plainSign = encode (plainSign sig), nonImportedSymbols = Set.map encode $ nonImportedSymbols sig } +showTheory :: PyTheory -> String +showTheory (PyTheory theory) = HC.showTheory theory + logicNameOfTheory :: PyTheory -> String logicNameOfTheory (PyTheory GT.G_theory { GT.gTheoryLogic = lid } ) = language_name lid @@ -338,33 +455,34 @@ gmorphismOfEdge = PyGMorphism . Static.DevGraph.dgl_morphism GMorphism Wrapper -------------------------------------------------------------------------------} comorphismOfGMorphism :: PyGMorphism -> PyComorphism -comorphismOfGMorphism (PyGMorphism (GMorphism {gMorphismComor = cid})) = PyComorphism (Comorphism cid) +comorphismOfGMorphism (PyGMorphism GMorphism {gMorphismComor = cid}) = PyComorphism (Comorphism cid) signatureOfGMorphism :: PyGMorphism -> ExtSign HDT.SignatureJSON HDT.SymbolJSON -signatureOfGMorphism (PyGMorphism (GMorphism {gMorphismSign = sig})) = ExtSign { +signatureOfGMorphism (PyGMorphism GMorphism {gMorphismSign = sig}) = ExtSign { plainSign = encode (plainSign sig), nonImportedSymbols = Set.map encode $ nonImportedSymbols sig } comorphismNameOfGMorphism :: PyGMorphism -> String -comorphismNameOfGMorphism (PyGMorphism (GMorphism {gMorphismComor = cid})) = language_name cid +comorphismNameOfGMorphism (PyGMorphism GMorphism {gMorphismComor = cid}) = language_name cid comorphismDescriptionOfGMorphism :: PyGMorphism -> String -comorphismDescriptionOfGMorphism (PyGMorphism (GMorphism {gMorphismComor = cid})) = description cid +comorphismDescriptionOfGMorphism (PyGMorphism GMorphism {gMorphismComor = cid}) = description cid domainOfGMorphism :: PyGMorphism -> HDT.GenericTransportType -domainOfGMorphism (PyGMorphism (GMorphism {gMorphismMor = m})) = encode $ dom m +domainOfGMorphism (PyGMorphism GMorphism {gMorphismMor = m}) = encode $ dom m codomainOfGMorphism :: PyGMorphism -> HDT.GenericTransportType -codomainOfGMorphism (PyGMorphism (GMorphism {gMorphismMor = m})) = encode $ cod m +codomainOfGMorphism (PyGMorphism GMorphism {gMorphismMor = m}) = encode $ cod m isGMorphismInclusion :: PyGMorphism -> Bool isGMorphismInclusion (PyGMorphism m) = isInclusion m gMorphismToTransportType :: PyGMorphism -> HDT.GenericTransportType -gMorphismToTransportType (PyGMorphism (GMorphism {gMorphismComor = cid, gMorphismSign = sig, gMorphismMor = mor})) = +gMorphismToTransportType (PyGMorphism GMorphism {gMorphismComor = cid, gMorphismSign = sig, gMorphismMor = mor}) = encode $ object ["nameOfComorphism" .= show cid, "signature" .= sig, "morphism" .= mor] -- symbolMapOfGMorphism :: PyGMorphism -> Map.Map HDT.SymbolJSON HDT.SymbolJSON -- symbolMapOfGMorphism (PyGMorphism (GMorphism {gMorphismMor = morphism})) = Map.map encode $ Map.mapKeys encode $ symmap_of (targetLogic morphism) morphism + diff --git a/HetsAPI/Refinement.hs b/HetsAPI/Refinement.hs new file mode 100644 index 0000000000..02561eaec8 --- /dev/null +++ b/HetsAPI/Refinement.hs @@ -0,0 +1,74 @@ +{- | +Description : HetsAPIs interface for refinment trees. +Copyright : (c) Otto-von-Guericke University of Magdeburg +License : GPLv2 or higher, see LICENSE.txt +-} +module HetsAPI.Refinement ( + RefinementTreeNode(..), + RefinementTreeLink(..), + getRefinementTree, + getAvailableSpecificationsForRefinement +) + +where + +import Static.DevGraph (DGraph (specRoots), RTNodeLab, RTLinkLab, refTree, rtl_type, RTLinkType (..)) + +import qualified Common.Lib.Graph as Graph +import Data.Graph.Inductive (mkGraph, Node, suc, subgraph, labNodes, out, inn, labEdges, LNode, LEdge, nodes) +import qualified Data.Map as Map +import qualified Data.Set as Set + +data RefinementTreeNode = RefinementTreeNode { + isRootNode :: !Bool, + rtNodeLab :: !RTNodeLab + } + +getRefinementTreeNode :: Graph.Gr RTNodeLab RTLinkLab -> LNode RTNodeLab -> LNode RefinementTreeNode +getRefinementTreeNode graph (x, lab) = (x, RefinementTreeNode (isRoot x graph) lab) + +type RefinementTreeLink = RTLinkLab + +getRefinementTreeEdge' :: (RTLinkLab -> Bool) + -> [LEdge RTLinkLab] + -> [LEdge RefinementTreeLink] +getRefinementTreeEdge' fn = filter (\ (_, _, e) -> fn e) + +roots :: String -> DGraph -> Maybe (Set.Set Node) +roots rspName = fmap Set.fromList . Map.lookup rspName . specRoots + +isRoot :: Node -> Graph.Gr a RTLinkLab -> Bool +isRoot n rTree = any (\ (_, _, llab) -> rtl_type llab == RTComp) ( + out rTree n) && not + (any (\ (_, _, llab) -> rtl_type llab == RTComp) $ + inn rTree n) + +ccomp :: String -> DGraph -> Maybe (Set.Set Node) +ccomp rspName dg = do + roots' <- roots rspName dg + return $ getConnectedComps roots' $ refTree dg + +getConnectedComps :: Set.Set Node -> Graph.Gr a b -> Set.Set Node +getConnectedComps nodes0 tree = + let nodes1 = Set.unions $ Set.map (Set.fromList . suc tree) nodes0 in + if Set.isSubsetOf nodes1 nodes0 + then nodes0 + else getConnectedComps (foldl (flip Set.insert) nodes0 nodes1) tree + +getRefinementTree :: String -> DGraph -> Maybe (Graph.Gr RefinementTreeNode RefinementTreeLink) +getRefinementTree rspName dg = do + ccomp' <- Set.toList <$> ccomp rspName dg + let + nods = map (getRefinementTreeNode $ refTree dg) vertices + nodeAliases = Map.fromList $ zip (nodes rTree) (fst <$> nods) + rTree = subgraph ccomp' (refTree dg) + vertices = labNodes rTree + arcs = labEdges rTree + edges' = arcListR ++ arcListC + edges = [( nodeAliases Map.! s, nodeAliases Map.! t, l) | (s, t, l) <- edges'] + arcListR = getRefinementTreeEdge' ((==) RTComp . rtl_type) arcs + arcListC = getRefinementTreeEdge' ((==) RTRefine . rtl_type) arcs + return $ mkGraph nods edges + +getAvailableSpecificationsForRefinement :: DGraph -> [String] +getAvailableSpecificationsForRefinement = Map.keys . specRoots diff --git a/Interfaces/Utils.hs b/Interfaces/Utils.hs index 0f6063c3ab..060eeb72d8 100644 --- a/Interfaces/Utils.hs +++ b/Interfaces/Utils.hs @@ -1,4 +1,5 @@ -{-# LANGUAGE CPP #-} +{-# LANGUAGE CPP, RecordWildCards #-} + {- | Module :./Interfaces/Utils.hs Description : utilitary functions @@ -24,10 +25,13 @@ module Interfaces.Utils , addCommandHistoryToState , checkConservativityNode , checkConservativityEdge + , checkConservativityEdgeWith + , recordConservativityResult + , getUsableConservativityCheckers , updateNodeProof ) where -import Interfaces.Command +import Interfaces.Command ( Command(CommentCmd) ) import Interfaces.DataTypes import Interfaces.GenericATPState import qualified Interfaces.Command as IC @@ -70,7 +74,7 @@ import Common.AS_Annotation (SenAttr (..), makeNamed, mapNamed) import qualified Common.Doc as Pretty import Common.Utils import qualified Control.Monad.Fail as Fail - +import Common.ResultT (ResultT(runResultT, ResultT)) #ifdef UNI_PACKAGE import GUI.Utils @@ -268,97 +272,126 @@ checkConservativityNode useGUI (nodeId, nodeLab) libEnv ln = do libEnv return (str, libEnv', history) -checkConservativityEdge :: Bool -> LEdge DGLinkLab -> LibEnv -> LibName - -> IO (String, LibEnv, LEdge DGLinkLab, ProofHistory) -checkConservativityEdge useGUI link@(source, target, linklab) libEnv ln - = do +getUsableConservativityCheckersForLogic :: Logic lid sublogics + basic_spec sentence symb_items symb_map_items + sign morphism symbol raw_symbol proof_tree + => lid -> IO [ConservativityChecker sign sentence morphism] +getUsableConservativityCheckersForLogic lid = filterM (fmap isNothing . checkerUsable) $ conservativityCheck lid - Just (G_theory lidT _ sigT _ sensT _) <- + +getUsableConservativityCheckers :: LEdge DGLinkLab -> LibEnv -> LibName -> IO [G_conservativity_checker] +getUsableConservativityCheckers (_, target, _) libEnv ln = do + Just (G_theory lidT _ _ _ _ _) <- return $ computeTheory libEnv ln target + usableCCs <- getUsableConservativityCheckersForLogic lidT + return $ G_conservativity_checker lidT <$> usableCCs + + +checkConservativityEdgeWith :: LEdge DGLinkLab -> LibEnv -> LibName -> G_conservativity_checker + -> IO (Result (Conservativity, G_theory, G_theory)) -- (conservativity, obligations holding in the source theory, obligations required to hold in an imported theory) +checkConservativityEdgeWith (source, target, linklab) libEnv ln (G_conservativity_checker lidCC cc) = do + Just (G_theory lidT _ signT _ sensT _) <- return $ computeTheory libEnv ln target - GMorphism cid _ _ morphism _ <- return $ dgl_morphism linklab - morphism' <- coerceMorphism (targetLogic cid) lidT - "checkconservativityOfEdge" morphism - let compMor = case dgn_sigma $ labDG (lookupDGraph ln libEnv) target of + Just (G_theory lidS _ signS _ sensS _) <- + return $ computeTheory libEnv ln source + cc' <- coerceConservativityChecker lidCC lidT "checkconservativityOfEdge0" cc + sensS' <- coerceThSens lidS lidT "checkconservativityOfEdge1" sensS + GMorphism cid _ _ morphism _ <- return $ dgl_morphism linklab + morphism' <- coerceMorphism (targetLogic cid) lidT + "checkconservativityOfEdge" morphism + let compMor = case dgn_sigma $ labDG (lookupDGraph ln libEnv) target of Nothing -> morphism' Just (GMorphism cid' _ _ morphism2 _) -> case coerceMorphism (targetLogic cid') lidT - "checkconservativityOfEdge" morphism2 - >>= comp morphism' of - Result _ (Just phi) -> phi - _ -> error "checkconservativityOfEdge: comp" - Just (G_theory lidS _ signS _ sensS _) <- - return $ computeTheory libEnv ln source - case coerceSign lidS lidT "checkconservativityOfEdge.coerceSign" signS of - Nothing -> return ( "no implementation for heterogeneous links" - , libEnv, link, SizedList.empty) - Just signS' -> do - sensS' <- coerceThSens lidS lidT "checkconservativityOfEdge1" sensS - let transSensSrc = propagateErrors "checkConservativityEdge2" - $ mapThSensValueM (map_sen lidT compMor) sensS' - usableCs <- filterM (fmap isNothing . checkerUsable) - $ conservativityCheck lidT - checkerR <- conservativityChoser useGUI usableCs - case maybeResult checkerR of - Nothing -> return (concatMap diagString $ diags checkerR, - libEnv, link, SizedList.empty) - Just theChecker -> do - let inputThSens1 = filter isAxiom $ toNamedList sensT - transSrcSens = Set.fromList - $ map sentence $ toNamedList transSensSrc - inputThSens = filter - ((`Set.notMember` transSrcSens) . sentence) - inputThSens1 - showObls = show . Pretty.vsep - . map (\ o -> print_named lidT . + "checkconservativityOfEdge" morphism2 + >>= comp morphism' of + Result _ (Just phi) -> phi + _ -> error "checkconservativityOfEdge: comp" + let sensTransS = propagateErrors "checkConservativityEdge2" + $ mapThSensValueM (map_sen lidT compMor) sensS' + let sensTransS' = Set.fromList $ map sentence $ toNamedList sensTransS + axiomsT = filter isAxiom $ toNamedList sensT + inputSensT = filter ((`Set.notMember` sensTransS') . sentence) axiomsT + case coerceSign lidS lidT "checkconservativityOfEdge.coerceSign" signS of + Nothing -> fail "no implementation for heterogeneous links" + Just signS' -> runResultT $ do + (consv, outputSensT) <- ResultT $ checkConservativity cc' + (plainSign signS', toNamedList sensS') + compMor inputSensT + let (explanations, obligations) = partition (`Set.member` sensTransS') outputSensT + let theoryForObligations sens = G_theory { + gTheoryLogic = lidT + , gTheorySyntax = Nothing + , gTheorySign = signT + , gTheorySignIdx = startSigId + , gTheorySens = toThSens $ fmap (\o -> (makeNamed "" o) {isAxiom = False}) sens + , gTheorySelfIdx = startThId + } + return (consv, theoryForObligations explanations, theoryForObligations obligations) + +conservativityResultToNewEdge :: LEdge DGLinkLab -> (Conservativity, G_theory, G_theory) -> (LEdge DGLinkLab, Bool) +conservativityResultToNewEdge (source, target, linklab) (consv, _, G_theory { gTheorySens = obligations }) = + let consv' = if Map.null obligations then consv else Unknown "unchecked obligations" + (newDglType, edgeChanged) = case dgl_type linklab of + ScopedLink sc dl (ConsStatus consv'' cs op) -> + let np = if consv' >= consv'' + then Proven conservativityRule emptyProofBasis + else LeftOpen + in (ScopedLink sc dl $ + ConsStatus consv'' (max cs $ max consv'' consv') np, np /= op) + t -> (t, False) + in ((source, target, linklab { dgl_type = newDglType }), edgeChanged) + +recordConservativityResult :: LEdge DGLinkLab -> LibEnv -> LibName -> (Conservativity, G_theory, G_theory) -> LibEnv +recordConservativityResult edge@(source, target, linklab) libEnv ln checkResult = + let dg = lookupDGraph ln libEnv + (provenEdge, edgeChanged) = conservativityResultToNewEdge edge checkResult + edgeChanges = if edgeChanged then + [ DeleteEdge (source, target, linklab) + , InsertEdge provenEdge ] else [] + nextDG = changesDGH dg edgeChanges + in if not edgeChanged then libEnv else + Map.insert ln (groupHistory dg conservativityRule nextDG) libEnv + + +checkConservativityEdge :: Bool -> LEdge DGLinkLab -> LibEnv -> LibName + -> IO (String, LibEnv, LEdge DGLinkLab, ProofHistory) +checkConservativityEdge useGUI link@(_, target, _) libEnv ln = do + Just (G_theory lidT _ sigT _ _ _) <- + return $ computeTheory libEnv ln target + usableCs <- getUsableConservativityCheckersForLogic lidT + checkerR <- conservativityChoser useGUI usableCs + case maybeResult checkerR of + Nothing -> return (concatMap diagString $ diags checkerR, + libEnv, link, SizedList.empty) + Just theChecker -> do + Result ds res <- checkConservativityEdgeWith link libEnv ln (G_conservativity_checker lidT theChecker) + (_, showRes, newLibEnv, provenEdge) <- case res of + Just conservativityResult@(consv, G_theory {gTheoryLogic = lidR1, gTheorySens = explanations}, G_theory {gTheoryLogic = lidR2, gTheorySens = obligations}) -> do + explanations' <- toNamedList <$> coerceThSens lidR1 lidT "checkConservativityEdge" explanations + obligations' <- toNamedList <$> coerceThSens lidR2 lidT "checkConservativityEdge" obligations + let showObls = show . Pretty.vsep + . map (print_named lidT . mapNamed (simplify_sen lidT - $ plainSign sigT) - $ (makeNamed "" o) {isAxiom = False}) - Result ds res <- - checkConservativity theChecker - (plainSign signS', toNamedList sensS') - compMor inputThSens - let (cs', showRes) = case res of - Just (cst, obs) -> - let (exSens, resObls) = partition - (`Set.member` transSrcSens) obs - in (if null resObls then cst - else Unknown "unchecked obligations" - , "The link is " ++ showConsistencyStatus cst - ++ case resObls of - [] -> case exSens of - [] -> "" - _ -> " because of the following axioms:\n" - ++ showObls exSens - _ -> " provided that the following obligations\n" - ++ "hold in an imported theory:\n" - ++ showObls resObls) - Nothing -> (Unknown "Unknown" - , "Could not determine whether link is conservative") - consNew csv = if cs' >= csv - then Proven conservativityRule emptyProofBasis - else LeftOpen - (newDglType, edgeChange) = case dgl_type linklab of - ScopedLink sc dl (ConsStatus consv cs op) -> - let np = consNew consv in - (ScopedLink sc dl $ - ConsStatus consv (max cs $ max consv cs') np, np /= op) - t -> (t, False) - provenEdge = ( source - , target - , linklab { dgl_type = newDglType } - ) - dg = lookupDGraph ln libEnv - let edgeChanges = if edgeChange then - [ DeleteEdge (source, target, linklab) - , InsertEdge provenEdge ] else [] - nextGr = changesDGH dg edgeChanges - newLibEnv = if edgeChange then Map.insert ln - (groupHistory dg conservativityRule nextGr) libEnv - else libEnv - history = snd $ splitHistory dg nextGr - myDiags = showRelDiags 4 ds - return ( showRes ++ "\n" ++ myDiags - , newLibEnv, provenEdge, history) + $ plainSign sigT)) + let libEnv' = recordConservativityResult link libEnv ln conservativityResult + let (provenEdge', _) = conservativityResultToNewEdge link conservativityResult + return (if null obligations' then consv + else Unknown "unchecked obligations" + , "The link is " ++ showConsistencyStatus consv + ++ if null obligations' then + (if null explanations' then "" + else " because of the following axioms:\n" + ++ showObls explanations') + else " provided that the following obligations\n" + ++ "hold in an imported theory:\n" + ++ showObls obligations', libEnv', provenEdge') + Nothing -> return (Unknown "Unknown", "Could not determine whether link is conservative", libEnv, link) + let dg = lookupDGraph ln libEnv + newDG = lookupDGraph ln newLibEnv + history = snd $ splitHistory dg newDG + myDiags = showRelDiags 4 ds + return ( showRes ++ "\n" ++ myDiags + , newLibEnv, provenEdge, history) updateNodeProof :: LibName -> IntState -> LNode DGNodeLab -> G_theory -> (IntState, [DGChange]) diff --git a/Logic/Prover.hs b/Logic/Prover.hs index a319d450f6..ae6ad3be10 100644 --- a/Logic/Prover.hs +++ b/Logic/Prover.hs @@ -150,24 +150,18 @@ data Theory sign sen proof_tree = data TacticScript = TacticScript String deriving (Eq, Ord, Show, Typeable) -- | failure reason -data Reason = Reason [String] deriving Typeable - -instance Ord Reason where - compare _ _ = EQ - -instance Eq Reason where - a == b = compare a b == EQ +type Reason = [String] -- | enumeration type representing the status of a goal data GoalStatus = - Open Reason -- ^ failure reason + Open { goalStatusOpenReason :: Reason } -- ^ failure reason | Disproved | Proved Bool -- ^ True means consistent; False inconsistent deriving (Eq, Ord, Typeable) instance Show GoalStatus where show gs = case gs of - Open (Reason l) -> unlines $ "Open" : l + Open reason -> unlines $ "Open" : reason Disproved -> "Disproved" Proved c -> "Proved" ++ if c then "" else "(inconsistent)" @@ -177,7 +171,7 @@ isOpenGoal gs = case gs of _ -> False openGoalStatus :: GoalStatus -openGoalStatus = Open $ Reason [] +openGoalStatus = Open [] -- | data type representing the proof status for a goal data ProofStatus proof_tree = ProofStatus diff --git a/Makefile b/Makefile index d050b25cfc..c42d6dfb23 100755 --- a/Makefile +++ b/Makefile @@ -681,7 +681,7 @@ distclean: clean_stack realclean clean_genRules ### interactive ghci: $(derived_sources) - $(STACK_EXEC) ghci $(HC_OPTS) + $(STACK_EXEC) ghci -fobject-code $(HC_OPTS) ### build only, don't link. Target was formerly known as 'build'. build-hets: hets.hs diff --git a/Proofs/AbstractState.hs b/Proofs/AbstractState.hs index 961ca20491..26ccd2dbef 100644 --- a/Proofs/AbstractState.hs +++ b/Proofs/AbstractState.hs @@ -24,6 +24,10 @@ module Proofs.AbstractState , coerceProver , G_cons_checker (..) , coerceConsChecker + , G_conservativity_checker(..) + , conservativityCheckerId + , conservativityCheckerUsable + , coerceConservativityChecker , ProofActions (..) , ProofState (..) , sublogicOfTheory @@ -33,6 +37,7 @@ module Proofs.AbstractState , toAxioms , getAxioms , getGoals + , makeTheoryForSentences , recalculateSublogicAndSelectedTheory , markProved , G_theory_with_prover (..) @@ -66,6 +71,7 @@ import qualified Common.OrderedMap as OMap import Common.Result as Result import Common.ResultT import Common.AS_Annotation +import Common.Consistency import Common.ExtSign import Common.Utils import Common.GraphAlgo (yen) @@ -144,6 +150,29 @@ getCcBatch (G_cons_checker _ p) = ccBatch p usableCC :: G_cons_checker -> IO Bool usableCC (G_cons_checker _ p) = fmap isNothing $ ccUsable p +data G_conservativity_checker = + forall lid sublogics basic_spec sentence symb_items + symb_map_items sign morphism symbol raw_symbol proof_tree + . Logic lid sublogics basic_spec sentence symb_items + symb_map_items sign morphism symbol raw_symbol proof_tree + => G_conservativity_checker lid (ConservativityChecker sign sentence morphism) + +coerceConservativityChecker :: + ( Logic lid1 sublogics1 basic_spec1 sentence1 symb_items1 symb_map_items1 + sign1 morphism1 symbol1 raw_symbol1 proof_tree1 + , Logic lid2 sublogics2 basic_spec2 sentence2 symb_items2 symb_map_items2 + sign2 morphism2 symbol2 raw_symbol2 proof_tree2 + , Fail.MonadFail m) + => lid1 -> lid2 -> String -> ConservativityChecker sign1 sentence1 morphism1 -> m (ConservativityChecker sign2 sentence2 morphism2) +coerceConservativityChecker = primCoerce + +conservativityCheckerId :: G_conservativity_checker -> String +conservativityCheckerId (G_conservativity_checker _ cc) = checkerId cc + +conservativityCheckerUsable :: G_conservativity_checker -> IO (Maybe String) +conservativityCheckerUsable (G_conservativity_checker _ cc) = checkerUsable cc + + coerceConsChecker :: ( Logic lid1 sublogics1 basic_spec1 sentence1 symb_items1 symb_map_items1 sign1 morphism1 symbol1 raw_symbol1 proof_tree1 @@ -340,22 +369,26 @@ prepareForProving st (G_prover lid4 p, co) = do p' <- coerceProver lid4 lidT "" p return $ G_theory_with_prover lidT th p' --- | creates the currently selected theory -makeSelectedTheory :: ProofState -> G_theory -makeSelectedTheory s = case currentTheory s of - G_theory lid syn sig si sens _ -> - -- axiom map, goal map - let (aMap, gMap) = OMap.partition isAxiom sens +makeTheoryForSentences :: [String] -> [String] -> [String] -> G_theory -> G_theory +makeTheoryForSentences axioms goals theorems (G_theory lid syn sig si sens _ ) = + let (aMap, gMap) = OMap.partition isAxiom sens -- proven goals map - pMap = OMap.filter isProvenSenStatus gMap - in - G_theory lid syn sig si + pMap = OMap.filter isProvenSenStatus gMap + in + G_theory lid syn sig si (Map.unions - [ filterMapWithList (selectedGoals s) gMap - , filterMapWithList (includedAxioms s) aMap - , markAsAxiom True $ filterMapWithList (includedTheorems s) pMap + [ filterMapWithList goals gMap + , filterMapWithList axioms aMap + , markAsAxiom True $ filterMapWithList theorems pMap ]) startThId +-- | creates the currently selected theory +makeSelectedTheory :: ProofState -> G_theory +makeSelectedTheory s = makeTheoryForSentences + (includedAxioms s) + (selectedGoals s) + (includedTheorems s) $ currentTheory s + {- | recalculation of sublogic upon (de)selection of goals, axioms and proven theorems diff --git a/Proofs/BatchProcessing.hs b/Proofs/BatchProcessing.hs index 4bff11c0a0..4c70a541de 100644 --- a/Proofs/BatchProcessing.hs +++ b/Proofs/BatchProcessing.hs @@ -221,8 +221,8 @@ genericProveBatch useStOpt tLimit extraOptions inclProvedThs saveProblem_batch let res0 = proofStatus res_cfg res = res0 { proofLines = resultOutput res_cfg, goalStatus = case goalStatus res0 of - Open (Reason l) | err == ATPTLimitExceeded -> - Open (Reason $ "Timeout" : l) + Open reason | err == ATPTLimitExceeded -> + Open ("Timeout" : reason) r -> r } pst' = addToLP g res pst goalsProcessedSoFar' = goalsProcessedSoFar + 1 diff --git a/README b/README index c13dc18604..842d459c78 100644 --- a/README +++ b/README @@ -4,6 +4,8 @@ Information not contained in this file can usually be found in the Hets user guide (doc/UserGuide.pdf), on the Hets web page http://hets.eu/ or within the source itself. +The Python interface is documented at https://spechub.github.io/Hets. + Installation ============ diff --git a/SoftFOL/MathServMapping.hs b/SoftFOL/MathServMapping.hs index 766fca836a..768f261c76 100644 --- a/SoftFOL/MathServMapping.hs +++ b/SoftFOL/MathServMapping.hs @@ -123,7 +123,7 @@ mapToGoalStatus :: MWStatus -- ^ goal status mapToGoalStatus stat = case foAtpStatus stat of Solved Theorem -> Proved True Solved CounterSatisfiable -> Disproved - s -> Open $ Reason [show s] + s -> Open [show s] {- | Gets the prover name from MathServResponse and extracts the name only diff --git a/SoftFOL/ProveMetis.hs b/SoftFOL/ProveMetis.hs index db30bb2b6c..637e8530f8 100644 --- a/SoftFOL/ProveMetis.hs +++ b/SoftFOL/ProveMetis.hs @@ -153,9 +153,9 @@ runMetis sps cfg saveTPTP thName nGoal = do getGoalStatus :: String -> GoalStatus getGoalStatus l = let ll = lines l in case mapMaybe (stripPrefix "SZS status") ll of - [] -> Open (Reason ll) + [] -> Open ll z@(s : _) -> case words s of w : _ | szsProved w -> Proved True | szsDisproved w -> Disproved - _ -> Open (Reason z) + _ -> Open z diff --git a/out.dot b/out.dot new file mode 100644 index 0000000000..44f7431485 --- /dev/null +++ b/out.dot @@ -0,0 +1,764 @@ +digraph G { +n78 [label="ATC/AS_Annotation.der.hs", color="red"]; +n77 [label="ATC/AS_Annotation.hs", color="red"]; +n88 [label="ATC/AS_Architecture.der.hs", color="red"]; +n87 [label="ATC/AS_Architecture.hs", color="red"]; +n93 [label="ATC/AS_Library.der.hs", color="red"]; +n92 [label="ATC/AS_Library.hs", color="red"]; +n86 [label="ATC/AS_Structured.der.hs", color="red"]; +n85 [label="ATC/AS_Structured.hs", color="red"]; +n104 [label="ATC/Consistency.der.hs", color="red"]; +n103 [label="ATC/Consistency.hs", color="red"]; +n83 [label="ATC/DefaultMorphism.der.hs", color="red"]; +n82 [label="ATC/DefaultMorphism.hs", color="red"]; +n116 [label="ATC/DevGraph.der.hs", color="red"]; +n115 [label="ATC/DevGraph.hs", color="red"]; +n110 [label="ATC/DgUtils.der.hs", color="red"]; +n109 [label="ATC/DgUtils.hs", color="red"]; +n101 [label="ATC/ExtSign.der.hs", color="red"]; +n100 [label="ATC/ExtSign.hs", color="red"]; +n90 [label="ATC/GlobalAnnotations.der.hs", color="red"]; +n89 [label="ATC/GlobalAnnotations.hs", color="red"]; +n128 [label="ATC/Graph.der.hs", color="red"]; +n127 [label="ATC/Graph.hs", color="red"]; +n64 [label="ATC/Grothendieck.der.hs", color="forestgreen"]; +n63 [label="ATC/Grothendieck.hs", color="red"]; +n131 [label="ATC/IRI.der.hs", color="red"]; +n130 [label="ATC/IRI.hs", color="red"]; +n119 [label="ATC/Id.der.hs", color="red"]; +n118 [label="ATC/Id.hs", color="red"]; +n98 [label="ATC/LibName.der.hs", color="red"]; +n97 [label="ATC/LibName.hs", color="red"]; +n125 [label="ATC/OrderedMap.der.hs", color="red"]; +n124 [label="ATC/OrderedMap.hs", color="red"]; +n107 [label="ATC/ProofTree.der.hs", color="red"]; +n106 [label="ATC/ProofTree.hs", color="red"]; +n95 [label="ATC/Prover.der.hs", color="red"]; +n94 [label="ATC/Prover.hs", color="red"]; +n122 [label="ATC/Result.der.hs", color="red"]; +n121 [label="ATC/Result.hs", color="red"]; +n113 [label="ATC/XGraph.der.hs", color="red"]; +n112 [label="ATC/XGraph.hs", color="red"]; +n244 [label="Adl/ATC_Adl.der.hs", color="red"]; +n243 [label="Adl/ATC_Adl.hs", color="red"]; +n245 [label="Adl/As.hs", color="forestgreen"]; +n246 [label="Adl/Sign.hs", color="forestgreen"]; +n34 [label="CASL/AS_Basic_CASL.der.hs", color="forestgreen"]; +n33 [label="CASL/AS_Basic_CASL.hs", color="red"]; +n134 [label="CASL/ATC_CASL.der.hs", color="red"]; +n133 [label="CASL/ATC_CASL.hs", color="red"]; +n136 [label="CASL/Morphism.hs", color="forestgreen"]; +n137 [label="CASL/Sign.hs", color="forestgreen"]; +n135 [label="CASL/Sublogic.hs", color="forestgreen"]; +n54 [label="CASL_DL/AS_CASL_DL.der.hs", color="forestgreen"]; +n53 [label="CASL_DL/AS_CASL_DL.hs", color="red"]; +n175 [label="CASL_DL/ATC_CASL_DL.der.hs", color="red"]; +n174 [label="CASL_DL/ATC_CASL_DL.hs", color="red"]; +n176 [label="CASL_DL/Sign.hs", color="forestgreen"]; +n177 [label="CASL_DL/Sublogics.hs", color="forestgreen"]; +n52 [label="COL/AS_COL.der.hs", color="forestgreen"]; +n51 [label="COL/AS_COL.hs", color="red"]; +n166 [label="COL/ATC_COL.der.hs", color="red"]; +n165 [label="COL/ATC_COL.hs", color="red"]; +n167 [label="COL/COLSign.hs", color="forestgreen"]; +n235 [label="CSL/AS_BASIC_CSL.hs", color="forestgreen"]; +n232 [label="CSL/ATC_CSL.der.hs", color="red"]; +n231 [label="CSL/ATC_CSL.hs", color="red"]; +n234 [label="CSL/Morphism.hs", color="forestgreen"]; +n233 [label="CSL/Sign.hs", color="forestgreen"]; +n236 [label="CSL/Symbol.hs", color="forestgreen"]; +n237 [label="CSL/TreePO.hs", color="forestgreen"]; +n281 [label="CSMOF/ATC_CSMOF.der.hs", color="red"]; +n280 [label="CSMOF/ATC_CSMOF.hs", color="red"]; +n282 [label="CSMOF/As.hs", color="forestgreen"]; +n283 [label="CSMOF/Sign.hs", color="forestgreen"]; +n50 [label="CoCASL/AS_CoCASL.der.hs", color="forestgreen"]; +n49 [label="CoCASL/AS_CoCASL.hs", color="red"]; +n163 [label="CoCASL/ATC_CoCASL.der.hs", color="red"]; +n162 [label="CoCASL/ATC_CoCASL.hs", color="red"]; +n164 [label="CoCASL/CoCASLSign.hs", color="forestgreen"]; +n24 [label="Common/AS_Annotation.der.hs", color="forestgreen"]; +n23 [label="Common/AS_Annotation.hs", color="red"]; +n105 [label="Common/Consistency.hs", color="forestgreen"]; +n84 [label="Common/DefaultMorphism.hs", color="forestgreen"]; +n102 [label="Common/ExtSign.hs", color="forestgreen"]; +n91 [label="Common/GlobalAnnotations.hs", color="forestgreen"]; +n132 [label="Common/IRI.hs", color="forestgreen"]; +n120 [label="Common/Id.hs", color="forestgreen"]; +n129 [label="Common/Lib/Graph.hs", color="forestgreen"]; +n99 [label="Common/LibName.hs", color="forestgreen"]; +n126 [label="Common/OrderedMap.hs", color="forestgreen"]; +n140 [label="Common/Prec.hs", color="forestgreen"]; +n108 [label="Common/ProofTree.hs", color="forestgreen"]; +n123 [label="Common/Result.hs", color="forestgreen"]; +n70 [label="CommonLogic/AS_CommonLogic.der.hs", color="forestgreen"]; +n69 [label="CommonLogic/AS_CommonLogic.hs", color="red"]; +n226 [label="CommonLogic/ATC_CommonLogic.der.hs", color="red"]; +n225 [label="CommonLogic/ATC_CommonLogic.hs", color="red"]; +n229 [label="CommonLogic/Morphism.hs", color="forestgreen"]; +n227 [label="CommonLogic/Sign.hs", color="forestgreen"]; +n230 [label="CommonLogic/Sublogic.hs", color="forestgreen"]; +n228 [label="CommonLogic/Symbol.hs", color="forestgreen"]; +n183 [label="ConstraintCASL/AS_ConstraintCASL.hs", color="forestgreen"]; +n182 [label="ConstraintCASL/ATC_ConstraintCASL.der.hs", color="red"]; +n181 [label="ConstraintCASL/ATC_ConstraintCASL.hs", color="red"]; +n60 [label="CspCASL/AS_CspCASL.der.hs", color="forestgreen"]; +n59 [label="CspCASL/AS_CspCASL.hs", color="red"]; +n58 [label="CspCASL/AS_CspCASL_Process.der.hs", color="forestgreen"]; +n57 [label="CspCASL/AS_CspCASL_Process.hs", color="red"]; +n169 [label="CspCASL/ATC_CspCASL.der.hs", color="red"]; +n168 [label="CspCASL/ATC_CspCASL.hs", color="red"]; +n173 [label="CspCASL/Morphism.hs", color="forestgreen"]; +n170 [label="CspCASL/SignCSP.hs", color="forestgreen"]; +n171 [label="CspCASL/SymbItems.hs", color="forestgreen"]; +n172 [label="CspCASL/Symbol.hs", color="forestgreen"]; +n201 [label="DFOL/AS_DFOL.hs", color="forestgreen"]; +n200 [label="DFOL/ATC_DFOL.der.hs", color="red"]; +n199 [label="DFOL/ATC_DFOL.hs", color="red"]; +n203 [label="DFOL/Morphism.hs", color="forestgreen"]; +n202 [label="DFOL/Sign.hs", color="forestgreen"]; +n204 [label="DFOL/Symbol.hs", color="forestgreen"]; +n66 [label="ExtModal/AS_ExtModal.der.hs", color="forestgreen"]; +n65 [label="ExtModal/AS_ExtModal.hs", color="red"]; +n221 [label="ExtModal/ATC_ExtModal.der.hs", color="red"]; +n220 [label="ExtModal/ATC_ExtModal.hs", color="red"]; +n222 [label="ExtModal/ExtModalSign.hs", color="forestgreen"]; +n223 [label="ExtModal/MorphismExtension.hs", color="forestgreen"]; +n224 [label="ExtModal/Sublogic.hs", color="forestgreen"]; +n254 [label="Fpl/ATC_Fpl.der.hs", color="red"]; +n253 [label="Fpl/ATC_Fpl.hs", color="red"]; +n72 [label="Fpl/As.der.hs", color="forestgreen"]; +n71 [label="Fpl/As.hs", color="red"]; +n255 [label="Fpl/Sign.hs", color="forestgreen"]; +n212 [label="Framework/AS.hs", color="forestgreen"]; +n211 [label="Framework/ATC_Framework.der.hs", color="red"]; +n210 [label="Framework/ATC_Framework.hs", color="red"]; +n262 [label="FreeCAD/ATC_FreeCAD.der.hs", color="red"]; +n261 [label="FreeCAD/ATC_FreeCAD.hs", color="red"]; +n263 [label="FreeCAD/As.hs", color="forestgreen"]; +n9 [label="GUI/Glade/GenericATP.glade", color="forestgreen"]; +n8 [label="GUI/Glade/GenericATP.hs", color="red"]; +n14 [label="GUI/Glade/LinkTypeChoice.glade", color="forestgreen"]; +n13 [label="GUI/Glade/LinkTypeChoice.hs", color="red"]; +n16 [label="GUI/Glade/NodeChecker.glade", color="forestgreen"]; +n15 [label="GUI/Glade/NodeChecker.hs", color="red"]; +n18 [label="GUI/Glade/ProverGUI.glade", color="forestgreen"]; +n17 [label="GUI/Glade/ProverGUI.hs", color="red"]; +n12 [label="GUI/Glade/Template.append.hs", color="forestgreen"]; +n20 [label="GUI/Glade/TextField.glade", color="forestgreen"]; +n19 [label="GUI/Glade/TextField.hs", color="red"]; +n22 [label="GUI/Glade/Utils.glade", color="forestgreen"]; +n21 [label="GUI/Glade/Utils.hs", color="red"]; +n139 [label="HasCASL/ATC_HasCASL.der.hs", color="red"]; +n138 [label="HasCASL/ATC_HasCASL.hs", color="red"]; +n141 [label="HasCASL/As.hs", color="forestgreen"]; +n142 [label="HasCASL/Le.hs", color="forestgreen"]; +n143 [label="HasCASL/Sublogic.hs", color="forestgreen"]; +n248 [label="HolLight/ATC_HolLight.der.hs", color="red"]; +n247 [label="HolLight/ATC_HolLight.hs", color="red"]; +n249 [label="HolLight/Sentence.hs", color="forestgreen"]; +n250 [label="HolLight/Sign.hs", color="forestgreen"]; +n251 [label="HolLight/Sublogic.hs", color="forestgreen"]; +n252 [label="HolLight/Term.hs", color="forestgreen"]; +n38 [label="Hybrid/AS_Hybrid.der.hs", color="forestgreen"]; +n37 [label="Hybrid/AS_Hybrid.hs", color="red"]; +n151 [label="Hybrid/ATC_Hybrid.der.hs", color="red"]; +n150 [label="Hybrid/ATC_Hybrid.hs", color="red"]; +n152 [label="Hybrid/HybridSign.hs", color="forestgreen"]; +n145 [label="Isabelle/ATC_Isabelle.der.hs", color="red"]; +n144 [label="Isabelle/ATC_Isabelle.hs", color="red"]; +n7 [label="Isabelle/IsaExport.dtd", color="forestgreen"]; +n4 [label="Isabelle/IsaExport.hs", color="red"]; +n146 [label="Isabelle/IsaSign.hs", color="forestgreen"]; +n209 [label="LF/AS.hs", color="forestgreen"]; +n206 [label="LF/ATC_LF.der.hs", color="red"]; +n205 [label="LF/ATC_LF.hs", color="red"]; +n208 [label="LF/Morphism.hs", color="forestgreen"]; +n207 [label="LF/Sign.hs", color="forestgreen"]; +n96 [label="Logic/Prover.hs", color="forestgreen"]; +n2 [label="Makefile", color="forestgreen"]; +n219 [label="Maude/AS_Maude.hs", color="forestgreen"]; +n214 [label="Maude/ATC_Maude.der.hs", color="red"]; +n213 [label="Maude/ATC_Maude.hs", color="red"]; +n216 [label="Maude/Morphism.hs", color="forestgreen"]; +n217 [label="Maude/Sentence.hs", color="forestgreen"]; +n215 [label="Maude/Sign.hs", color="forestgreen"]; +n218 [label="Maude/Symbol.hs", color="forestgreen"]; +n36 [label="Modal/AS_Modal.der.hs", color="forestgreen"]; +n35 [label="Modal/AS_Modal.hs", color="red"]; +n148 [label="Modal/ATC_Modal.der.hs", color="red"]; +n147 [label="Modal/ATC_Modal.hs", color="red"]; +n149 [label="Modal/ModalSign.hs", color="forestgreen"]; +n76 [label="NeSyPatterns/AS.der.hs", color="forestgreen"]; +n75 [label="NeSyPatterns/AS.hs", color="red"]; +n293 [label="NeSyPatterns/ATC_NeSyPatterns.der.hs", color="red"]; +n292 [label="NeSyPatterns/ATC_NeSyPatterns.hs", color="red"]; +n296 [label="NeSyPatterns/Morphism.hs", color="forestgreen"]; +n295 [label="NeSyPatterns/Sign.hs", color="forestgreen"]; +n294 [label="NeSyPatterns/Symbol.hs", color="forestgreen"]; +n197 [label="OMDoc/ATC_OMDoc.der.hs", color="red"]; +n196 [label="OMDoc/ATC_OMDoc.hs", color="red"]; +n198 [label="OMDoc/OMDocInterface.hs", color="forestgreen"]; +n266 [label="OWL2/AS.hs", color="forestgreen"]; +n265 [label="OWL2/ATC_OWL2.der.hs", color="red"]; +n264 [label="OWL2/ATC_OWL2.hs", color="red"]; +n269 [label="OWL2/Morphism.hs", color="forestgreen"]; +n272 [label="OWL2/Profiles.hs", color="forestgreen"]; +n270 [label="OWL2/ProfilesAndSublogics.hs", color="forestgreen"]; +n268 [label="OWL2/Sign.hs", color="forestgreen"]; +n271 [label="OWL2/Sublogic.hs", color="forestgreen"]; +n267 [label="OWL2/Symbols.hs", color="forestgreen"]; +n48 [label="Propositional/AS_BASIC_Propositional.der.hs", color="forestgreen"]; +n47 [label="Propositional/AS_BASIC_Propositional.hs", color="red"]; +n185 [label="Propositional/ATC_Propositional.der.hs", color="red"]; +n184 [label="Propositional/ATC_Propositional.hs", color="red"]; +n187 [label="Propositional/Morphism.hs", color="forestgreen"]; +n186 [label="Propositional/Sign.hs", color="forestgreen"]; +n189 [label="Propositional/Sublogic.hs", color="forestgreen"]; +n188 [label="Propositional/Symbol.hs", color="forestgreen"]; +n68 [label="QBF/AS_BASIC_QBF.der.hs", color="forestgreen"]; +n67 [label="QBF/AS_BASIC_QBF.hs", color="red"]; +n239 [label="QBF/ATC_QBF.der.hs", color="red"]; +n238 [label="QBF/ATC_QBF.hs", color="red"]; +n240 [label="QBF/Morphism.hs", color="forestgreen"]; +n242 [label="QBF/Sublogic.hs", color="forestgreen"]; +n241 [label="QBF/Symbol.hs", color="forestgreen"]; +n285 [label="QVTR/ATC_QVTR.der.hs", color="red"]; +n284 [label="QVTR/ATC_QVTR.hs", color="red"]; +n286 [label="QVTR/As.hs", color="forestgreen"]; +n287 [label="QVTR/Sign.hs", color="forestgreen"]; +n275 [label="RDF/AS.hs", color="forestgreen"]; +n274 [label="RDF/ATC_RDF.der.hs", color="red"]; +n273 [label="RDF/ATC_RDF.hs", color="red"]; +n278 [label="RDF/Morphism.hs", color="forestgreen"]; +n277 [label="RDF/Sign.hs", color="forestgreen"]; +n279 [label="RDF/Sublogic.hs", color="forestgreen"]; +n276 [label="RDF/Symbols.hs", color="forestgreen"]; +n62 [label="RelationalScheme/AS.der.hs", color="forestgreen"]; +n61 [label="RelationalScheme/AS.hs", color="red"]; +n191 [label="RelationalScheme/ATC_RelationalScheme.der.hs", color="red"]; +n190 [label="RelationalScheme/ATC_RelationalScheme.hs", color="red"]; +n192 [label="RelationalScheme/Sign.hs", color="forestgreen"]; +n179 [label="SoftFOL/ATC_SoftFOL.der.hs", color="red"]; +n178 [label="SoftFOL/ATC_SoftFOL.hs", color="red"]; +n180 [label="SoftFOL/Sign.hs", color="forestgreen"]; +n117 [label="Static/DevGraph.hs", color="forestgreen"]; +n111 [label="Static/DgUtils.hs", color="forestgreen"]; +n114 [label="Static/XGraph.hs", color="forestgreen"]; +n44 [label="Syntax/AS_Architecture.der.hs", color="forestgreen"]; +n43 [label="Syntax/AS_Architecture.hs", color="red"]; +n46 [label="Syntax/AS_Library.der.hs", color="forestgreen"]; +n45 [label="Syntax/AS_Library.hs", color="red"]; +n42 [label="Syntax/AS_Structured.der.hs", color="forestgreen"]; +n41 [label="Syntax/AS_Structured.hs", color="red"]; +n257 [label="THF/ATC_THF.der.hs", color="red"]; +n256 [label="THF/ATC_THF.hs", color="red"]; +n56 [label="THF/As.der.hs", color="forestgreen"]; +n55 [label="THF/As.hs", color="red"]; +n258 [label="THF/Cons.hs", color="forestgreen"]; +n259 [label="THF/Sign.hs", color="forestgreen"]; +n260 [label="THF/Sublogic.hs", color="forestgreen"]; +n74 [label="TPTP/AS.der.hs", color="forestgreen"]; +n73 [label="TPTP/AS.hs", color="red"]; +n289 [label="TPTP/ATC_TPTP.der.hs", color="red"]; +n288 [label="TPTP/ATC_TPTP.hs", color="red"]; +n290 [label="TPTP/Sign.hs", color="forestgreen"]; +n291 [label="TPTP/Sublogic.hs", color="forestgreen"]; +n158 [label="Temporal/AS_BASIC_Temporal.hs", color="forestgreen"]; +n157 [label="Temporal/ATC_Temporal.der.hs", color="red"]; +n156 [label="Temporal/ATC_Temporal.hs", color="red"]; +n161 [label="Temporal/Morphism.hs", color="forestgreen"]; +n159 [label="Temporal/Sign.hs", color="forestgreen"]; +n160 [label="Temporal/Symbol.hs", color="forestgreen"]; +n40 [label="TopHybrid/AS_TopHybrid.der.hs", color="forestgreen"]; +n39 [label="TopHybrid/AS_TopHybrid.hs", color="red"]; +n154 [label="TopHybrid/ATC_TopHybrid.der.hs", color="red"]; +n153 [label="TopHybrid/ATC_TopHybrid.hs", color="red"]; +n155 [label="TopHybrid/TopHybridSign.hs", color="forestgreen"]; +n194 [label="VSE/ATC_VSE.der.hs", color="red"]; +n193 [label="VSE/ATC_VSE.hs", color="red"]; +n195 [label="VSE/As.hs", color="forestgreen"]; +n3 [label="derived", color="red"]; +n25 [label="utils/DrIFT", color="red"]; +n26 [label="utils/DrIFT-src/ChaseImports.hs", color="forestgreen"]; +n27 [label="utils/DrIFT-src/CommandP.hs", color="forestgreen"]; +n28 [label="utils/DrIFT-src/DataP.hs", color="forestgreen"]; +n29 [label="utils/DrIFT-src/DrIFT.hs", color="forestgreen"]; +n30 [label="utils/DrIFT-src/ParseLib2.hs", color="forestgreen"]; +n31 [label="utils/DrIFT-src/RuleUtils.hs", color="forestgreen"]; +n32 [label="utils/DrIFT-src/UserRulesHetCATS.hs", color="forestgreen"]; +n5 [label="utils/DtdToHaskell", color="red"]; +n6 [label="utils/DtdToHaskell-src/DtdToHaskell.hs", color="forestgreen"]; +n80 [label="utils/GenerateRules/GenerateRules.hs", color="forestgreen"]; +n81 [label="utils/GenerateRules/ParseFile.hs", color="forestgreen"]; +n10 [label="utils/appendHaskellPreludeString", color="red"]; +n11 [label="utils/appendHaskellPreludeString.hs", color="forestgreen"]; +n79 [label="utils/genRules", color="red"]; +n24 -> n78 ; +n79 -> n78 ; +n78 -> n77 ; +n25 -> n77 ; +n44 -> n88 ; +n79 -> n88 ; +n88 -> n87 ; +n25 -> n87 ; +n46 -> n93 ; +n79 -> n93 ; +n93 -> n92 ; +n25 -> n92 ; +n42 -> n86 ; +n79 -> n86 ; +n86 -> n85 ; +n25 -> n85 ; +n105 -> n104 ; +n79 -> n104 ; +n104 -> n103 ; +n25 -> n103 ; +n84 -> n83 ; +n79 -> n83 ; +n83 -> n82 ; +n25 -> n82 ; +n117 -> n116 ; +n79 -> n116 ; +n116 -> n115 ; +n117 -> n115 ; +n25 -> n115 ; +n111 -> n110 ; +n79 -> n110 ; +n110 -> n109 ; +n25 -> n109 ; +n102 -> n101 ; +n79 -> n101 ; +n101 -> n100 ; +n25 -> n100 ; +n91 -> n90 ; +n79 -> n90 ; +n90 -> n89 ; +n25 -> n89 ; +n129 -> n128 ; +n79 -> n128 ; +n128 -> n127 ; +n25 -> n127 ; +n64 -> n63 ; +n25 -> n63 ; +n132 -> n131 ; +n79 -> n131 ; +n131 -> n130 ; +n25 -> n130 ; +n120 -> n119 ; +n79 -> n119 ; +n119 -> n118 ; +n25 -> n118 ; +n99 -> n98 ; +n79 -> n98 ; +n98 -> n97 ; +n25 -> n97 ; +n126 -> n125 ; +n79 -> n125 ; +n125 -> n124 ; +n25 -> n124 ; +n108 -> n107 ; +n79 -> n107 ; +n107 -> n106 ; +n25 -> n106 ; +n96 -> n95 ; +n79 -> n95 ; +n95 -> n94 ; +n25 -> n94 ; +n123 -> n122 ; +n79 -> n122 ; +n122 -> n121 ; +n25 -> n121 ; +n114 -> n113 ; +n79 -> n113 ; +n113 -> n112 ; +n25 -> n112 ; +n245 -> n244 ; +n246 -> n244 ; +n79 -> n244 ; +n244 -> n243 ; +n25 -> n243 ; +n34 -> n33 ; +n25 -> n33 ; +n34 -> n134 ; +n136 -> n134 ; +n137 -> n134 ; +n135 -> n134 ; +n79 -> n134 ; +n134 -> n133 ; +n25 -> n133 ; +n54 -> n53 ; +n25 -> n53 ; +n53 -> n175 ; +n176 -> n175 ; +n177 -> n175 ; +n79 -> n175 ; +n175 -> n174 ; +n25 -> n174 ; +n52 -> n51 ; +n25 -> n51 ; +n51 -> n166 ; +n167 -> n166 ; +n79 -> n166 ; +n166 -> n165 ; +n25 -> n165 ; +n235 -> n232 ; +n234 -> n232 ; +n233 -> n232 ; +n236 -> n232 ; +n237 -> n232 ; +n79 -> n232 ; +n232 -> n231 ; +n25 -> n231 ; +n282 -> n281 ; +n283 -> n281 ; +n79 -> n281 ; +n281 -> n280 ; +n25 -> n280 ; +n50 -> n49 ; +n25 -> n49 ; +n49 -> n163 ; +n164 -> n163 ; +n79 -> n163 ; +n163 -> n162 ; +n25 -> n162 ; +n24 -> n23 ; +n25 -> n23 ; +n70 -> n69 ; +n25 -> n69 ; +n69 -> n226 ; +n229 -> n226 ; +n227 -> n226 ; +n230 -> n226 ; +n228 -> n226 ; +n79 -> n226 ; +n226 -> n225 ; +n25 -> n225 ; +n183 -> n182 ; +n79 -> n182 ; +n182 -> n181 ; +n25 -> n181 ; +n60 -> n59 ; +n25 -> n59 ; +n58 -> n57 ; +n25 -> n57 ; +n59 -> n169 ; +n57 -> n169 ; +n173 -> n169 ; +n170 -> n169 ; +n171 -> n169 ; +n172 -> n169 ; +n79 -> n169 ; +n169 -> n168 ; +n25 -> n168 ; +n201 -> n200 ; +n203 -> n200 ; +n202 -> n200 ; +n204 -> n200 ; +n79 -> n200 ; +n200 -> n199 ; +n25 -> n199 ; +n66 -> n65 ; +n25 -> n65 ; +n65 -> n221 ; +n222 -> n221 ; +n223 -> n221 ; +n224 -> n221 ; +n79 -> n221 ; +n221 -> n220 ; +n25 -> n220 ; +n71 -> n254 ; +n255 -> n254 ; +n79 -> n254 ; +n254 -> n253 ; +n25 -> n253 ; +n72 -> n71 ; +n25 -> n71 ; +n212 -> n211 ; +n79 -> n211 ; +n211 -> n210 ; +n25 -> n210 ; +n263 -> n262 ; +n79 -> n262 ; +n262 -> n261 ; +n25 -> n261 ; +n9 -> n8 ; +n12 -> n8 ; +n10 -> n8 ; +n14 -> n13 ; +n12 -> n13 ; +n10 -> n13 ; +n16 -> n15 ; +n12 -> n15 ; +n10 -> n15 ; +n18 -> n17 ; +n12 -> n17 ; +n10 -> n17 ; +n12 -> n19 ; +n20 -> n19 ; +n10 -> n19 ; +n12 -> n21 ; +n22 -> n21 ; +n10 -> n21 ; +n140 -> n139 ; +n141 -> n139 ; +n142 -> n139 ; +n143 -> n139 ; +n79 -> n139 ; +n139 -> n138 ; +n25 -> n138 ; +n249 -> n248 ; +n250 -> n248 ; +n251 -> n248 ; +n252 -> n248 ; +n79 -> n248 ; +n248 -> n247 ; +n25 -> n247 ; +n38 -> n37 ; +n25 -> n37 ; +n37 -> n151 ; +n152 -> n151 ; +n79 -> n151 ; +n151 -> n150 ; +n25 -> n150 ; +n146 -> n145 ; +n79 -> n145 ; +n145 -> n144 ; +n25 -> n144 ; +n7 -> n4 ; +n5 -> n4 ; +n209 -> n206 ; +n208 -> n206 ; +n207 -> n206 ; +n79 -> n206 ; +n206 -> n205 ; +n25 -> n205 ; +n219 -> n214 ; +n216 -> n214 ; +n217 -> n214 ; +n215 -> n214 ; +n218 -> n214 ; +n79 -> n214 ; +n214 -> n213 ; +n25 -> n213 ; +n36 -> n35 ; +n25 -> n35 ; +n35 -> n148 ; +n149 -> n148 ; +n79 -> n148 ; +n148 -> n147 ; +n25 -> n147 ; +n76 -> n75 ; +n25 -> n75 ; +n75 -> n293 ; +n296 -> n293 ; +n295 -> n293 ; +n294 -> n293 ; +n79 -> n293 ; +n293 -> n292 ; +n25 -> n292 ; +n198 -> n197 ; +n79 -> n197 ; +n197 -> n196 ; +n25 -> n196 ; +n266 -> n265 ; +n269 -> n265 ; +n272 -> n265 ; +n270 -> n265 ; +n268 -> n265 ; +n271 -> n265 ; +n267 -> n265 ; +n79 -> n265 ; +n265 -> n264 ; +n25 -> n264 ; +n48 -> n47 ; +n25 -> n47 ; +n47 -> n185 ; +n187 -> n185 ; +n186 -> n185 ; +n189 -> n185 ; +n188 -> n185 ; +n79 -> n185 ; +n185 -> n184 ; +n25 -> n184 ; +n68 -> n67 ; +n25 -> n67 ; +n186 -> n239 ; +n67 -> n239 ; +n240 -> n239 ; +n242 -> n239 ; +n241 -> n239 ; +n79 -> n239 ; +n239 -> n238 ; +n25 -> n238 ; +n280 -> n285 ; +n286 -> n285 ; +n287 -> n285 ; +n79 -> n285 ; +n285 -> n284 ; +n25 -> n284 ; +n275 -> n274 ; +n278 -> n274 ; +n277 -> n274 ; +n279 -> n274 ; +n276 -> n274 ; +n79 -> n274 ; +n274 -> n273 ; +n25 -> n273 ; +n62 -> n61 ; +n25 -> n61 ; +n61 -> n191 ; +n192 -> n191 ; +n79 -> n191 ; +n191 -> n190 ; +n25 -> n190 ; +n180 -> n179 ; +n79 -> n179 ; +n179 -> n178 ; +n25 -> n178 ; +n44 -> n43 ; +n25 -> n43 ; +n46 -> n45 ; +n25 -> n45 ; +n42 -> n41 ; +n25 -> n41 ; +n55 -> n257 ; +n258 -> n257 ; +n259 -> n257 ; +n260 -> n257 ; +n79 -> n257 ; +n257 -> n256 ; +n25 -> n256 ; +n56 -> n55 ; +n25 -> n55 ; +n74 -> n73 ; +n25 -> n73 ; +n73 -> n289 ; +n290 -> n289 ; +n291 -> n289 ; +n79 -> n289 ; +n289 -> n288 ; +n25 -> n288 ; +n158 -> n157 ; +n161 -> n157 ; +n159 -> n157 ; +n160 -> n157 ; +n79 -> n157 ; +n157 -> n156 ; +n25 -> n156 ; +n40 -> n39 ; +n25 -> n39 ; +n39 -> n154 ; +n155 -> n154 ; +n79 -> n154 ; +n154 -> n153 ; +n25 -> n153 ; +n195 -> n194 ; +n79 -> n194 ; +n194 -> n193 ; +n25 -> n193 ; +n77 -> n3 ; +n87 -> n3 ; +n92 -> n3 ; +n85 -> n3 ; +n103 -> n3 ; +n82 -> n3 ; +n115 -> n3 ; +n109 -> n3 ; +n100 -> n3 ; +n89 -> n3 ; +n127 -> n3 ; +n63 -> n3 ; +n130 -> n3 ; +n118 -> n3 ; +n97 -> n3 ; +n124 -> n3 ; +n106 -> n3 ; +n94 -> n3 ; +n121 -> n3 ; +n112 -> n3 ; +n243 -> n3 ; +n33 -> n3 ; +n133 -> n3 ; +n53 -> n3 ; +n174 -> n3 ; +n51 -> n3 ; +n165 -> n3 ; +n231 -> n3 ; +n280 -> n3 ; +n49 -> n3 ; +n162 -> n3 ; +n23 -> n3 ; +n69 -> n3 ; +n225 -> n3 ; +n181 -> n3 ; +n59 -> n3 ; +n57 -> n3 ; +n168 -> n3 ; +n199 -> n3 ; +n65 -> n3 ; +n220 -> n3 ; +n253 -> n3 ; +n71 -> n3 ; +n210 -> n3 ; +n261 -> n3 ; +n8 -> n3 ; +n13 -> n3 ; +n15 -> n3 ; +n17 -> n3 ; +n19 -> n3 ; +n21 -> n3 ; +n138 -> n3 ; +n247 -> n3 ; +n37 -> n3 ; +n150 -> n3 ; +n144 -> n3 ; +n4 -> n3 ; +n205 -> n3 ; +n213 -> n3 ; +n35 -> n3 ; +n147 -> n3 ; +n75 -> n3 ; +n292 -> n3 ; +n196 -> n3 ; +n264 -> n3 ; +n47 -> n3 ; +n184 -> n3 ; +n67 -> n3 ; +n238 -> n3 ; +n284 -> n3 ; +n273 -> n3 ; +n61 -> n3 ; +n190 -> n3 ; +n178 -> n3 ; +n43 -> n3 ; +n45 -> n3 ; +n41 -> n3 ; +n256 -> n3 ; +n55 -> n3 ; +n73 -> n3 ; +n288 -> n3 ; +n156 -> n3 ; +n39 -> n3 ; +n153 -> n3 ; +n193 -> n3 ; +n26 -> n25 ; +n27 -> n25 ; +n28 -> n25 ; +n29 -> n25 ; +n30 -> n25 ; +n31 -> n25 ; +n32 -> n25 ; +n6 -> n5 ; +n11 -> n10 ; +n25 -> n79 ; +n26 -> n79 ; +n27 -> n79 ; +n28 -> n79 ; +n29 -> n79 ; +n30 -> n79 ; +n31 -> n79 ; +n32 -> n79 ; +n80 -> n79 ; +n81 -> n79 ; +} diff --git a/python/api/docs/Makefile b/python/api/docs/Makefile new file mode 100644 index 0000000000..d4bb2cbb9e --- /dev/null +++ b/python/api/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/python/api/docs/_templates/class.rst b/python/api/docs/_templates/class.rst new file mode 100644 index 0000000000..7027224ff1 --- /dev/null +++ b/python/api/docs/_templates/class.rst @@ -0,0 +1,6 @@ +{{name}} +============================= + +.. autoclass:: {{fullname}} + :members: + :undoc-members: diff --git a/python/api/docs/code-documentation.rst b/python/api/docs/code-documentation.rst new file mode 100644 index 0000000000..14df13b7a5 --- /dev/null +++ b/python/api/docs/code-documentation.rst @@ -0,0 +1,58 @@ +.. _code-documentation: + +Code documentation +================== + +This module contains several classes. + +.. .. automodule:: hets + :members: + :undoc-members: + :show-inheritance: + :separate: + +.. currentmodule:: hets + +Functions +--------- + +.. autosummary:: + :toctree: code + + load_library + +Classes +------- + +.. autosummary:: + :toctree: code + :template: class.rst + + BasicProof + Comorphism + ConsistencyChecker + ConservativityChecker + ConsistencyStatus + DevelopmentGraph + DevGraphEdge + DefinitionDevGraphEdge + TheoremDevGraphEdge + EdgeKind + DevGraphNode + ReferenceDevGraphNode + LocalDevGraphNode + GMorphism + GlobalAnnotations + Library + load_library + Logic + ProofState + ProofDetails + ProofKind + ConsistencyKind + Options + Option + Prover + Sentence + Signature + Theory diff --git a/python/api/docs/conf.py b/python/api/docs/conf.py new file mode 100644 index 0000000000..0cfee80097 --- /dev/null +++ b/python/api/docs/conf.py @@ -0,0 +1,48 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html +import os.path +import sys + + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information +sys.path.insert(0, os.path.abspath("../src")) +print(sys.path) + +project = 'Hets API' +copyright = '2023, Björn Gehrke' +author = 'Björn Gehrke' + +release = '00.00.01' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.viewcode', + 'sphinx.ext.napoleon', + 'sphinx.ext.autosummary' +] +autosummary_generate = True + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'sphinx_rtd_theme' +html_static_path = ['_static'] +html_show_sourcelink = False +html_copy_source = False + +autodoc_mock_imports = ["hyphen", "hs"] +autodoc_typehints_format = "short" +autoclass_content = "both" + +autodoc_default_options = { + 'exclude-members': 'hs_obj, hs_update' +} diff --git a/python/api/docs/index.rst b/python/api/docs/index.rst new file mode 100644 index 0000000000..aa77a31429 --- /dev/null +++ b/python/api/docs/index.rst @@ -0,0 +1,22 @@ +.. Hets API documentation master file, created by + sphinx-quickstart on Fri Jul 21 19:38:01 2023. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Hets API's documentation! +==================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + installation + usage + code-documentation + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`code-documentation` +* :ref:`search` diff --git a/python/api/docs/installation.rst b/python/api/docs/installation.rst new file mode 100644 index 0000000000..d92f2eb0e2 --- /dev/null +++ b/python/api/docs/installation.rst @@ -0,0 +1,124 @@ +Installation +============ + +.. contents:: + :local: + +Docker +------ +The simplest way to use the hets api is the docker image `spechub2/hets-api `_. Use one of the following commands to get an interactive session: + +.. code-block:: bash + + # Get an interactive python session + docker run -it --rm spechub2/hets-api python + + # Get an interactive ghci session + docker run -it --rm spechub2/hets-api ghci + + # Get an interactive bash session + docker run -it --rm spechub2/hets-api bash + +Optionally mount volumes to access your local files inside the docker container. The following command starts an interactive python session with the current working directory mounted inside the container at ``./files/`` + +.. code-block:: bash + + docker run -it --rm -v $PWD:/home/hets/files spechub2/hets-api python + + +Prerequisites +------------- + +Hyphen +"""""" + +The python hets API utilises `hyphen `_ to access the haskell interface from python. To install it, first install its dependencies:: + + sudo apt install git python3-dev ghc-dynamic ghc python3 python3-pip libghc-ghc-paths-dev libghc-parser-combinators-dev libghc-unordered-containers-dev + +Then, clone the source code and build the project:: + + git clone https://github.com/tbarnetlamb/hyphen.git + cd hyphen + python3 hyphen/build-extn.py + +Create a ``pyproject.toml`` in the root folder of the project with the following content + +.. code-block:: toml + + [project] + name = "hyphen" + version = "0.1.0.0" + + [tool.setuptools] + packages = [ "hyphen" ] + + [tool.setuptools.package-data] + "*" = [ "hslowlevel.*" ] + +Finally install hyphen with :: + + python3 -m pip install . + +.. hint:: + If you get errors during the installation try updating setuptools with + ``python3 -m pip install --upgrade pip setuptools`` + + +Haskell and python API +---------------------- + +To build the haskell API packages from the hets ppa are required. Add the ppa with :: + + sudo apt install -y software-properties-common apt-utils make + sudo apt-add-repository -y ppa:hets/hets + sudo apt update + +Then, install the dependencies for building the library :: + + sudo apt install openjdk-8-jdk-headless ant cabal-install ksh perl-base tar xz-utils zip libmysqlclient-dev ghc-haddock libghc-missingh-dev ghc>=7.10.3 happy libghc-mutable-containers-dev libghc-haxml-dev libghc-tar-dev libghc-random-dev libghc-parsec3-dev libghc-fgl-dev libghc-xml-dev libghc-http-dev libghc-warp-dev libghc-wai-extra-dev libghc-split-dev libghc-file-embed-dev libghc-monad-logger-dev libghc-yaml-dev libghc-esqueleto-dev>=2.5.3 libghc-persistent-dev>=2.7.0 libghc-persistent-template-dev>=2.5.2 libghc-persistent-postgresql-dev>=2.6.1 libghc-persistent-sqlite-dev>=2.6.2 libghc-utf8-string-dev libghc-relation-dev libghc-persistent-mysql-dev libghc-hexpat-dev libghc-aterm-dev libghc-xeno-dev libghc-heap-dev + +Clone the hets repository :: + + git clone https://github.com/spechub/Hets.git + cd Hets + +Build the library :: + + make derived + runhaskell Setup.hs configure + --ghc --prefix=/ + --disable-executable-stripping + --disable-benchmarks + --libdir=/lib/haskell-packages/ghc/lib/x86_64-linux-ghc-8.6.5 + --libsubdir=hets-api-0.100.0 + --datadir=share + --datasubdir=hets-api + --haddockdir=/lib/ghc-doc/haddock/hets-api-0.100.0 + --docdir=share/doc/hets-api-doc + --package-db=/var/lib/ghc/package.conf.d + --disable-profiling + lib:Hets + runhaskell Setup.hs build -j$(nproc) lib:Hets + sudo runhaskell Setup.hs install + +Finally, install the python API :: + + python3 -m pip install ./python/api + + +Provers and Library +------------------- + +It is recommend to install additional tools for automatic theorem proving as well as to download basic libraries and other examples + +.. code-block:: bash + + # Install provers. Choose any subset. + apt-get install -y cvc-47 darwin eprover fact++ maude minisat pellet spass vampire yices z3 zchaff + + # Download the hets library + git clone https://github.com/spechub/Hets-lib.git + export HETS_LIB=$(realpath Hets-lib) + + diff --git a/python/api/docs/make.bat b/python/api/docs/make.bat new file mode 100644 index 0000000000..954237b9b9 --- /dev/null +++ b/python/api/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/python/api/docs/requirements.txt b/python/api/docs/requirements.txt new file mode 100644 index 0000000000..4170c03efd --- /dev/null +++ b/python/api/docs/requirements.txt @@ -0,0 +1 @@ +sphinx-rtd-theme \ No newline at end of file diff --git a/python/api/docs/usage.rst b/python/api/docs/usage.rst new file mode 100644 index 0000000000..e9f6199ee1 --- /dev/null +++ b/python/api/docs/usage.rst @@ -0,0 +1,70 @@ +Usage +===== + +.. contents:: + :local: + +Most of the classes in the library depend on haskell structures and are therefore not ment to be instantiated manually. Rather, the library handles the instantiation. One exception is the :py:class:`~hets.Options` class. + +The main entry point is :py:meth:`hets.load_library`. It loads a library from a file path. Once a library is loaded you can perform several actions on the library itself, inspect the development graph with :py:meth:`~hets.Library.development_graph`, and prove or check the consistency of nodes or check the conservativity of an edge. + +.. code-block:: python + + import hets + + lib = hets.load_library("Family.dol") + dg = lib.development_graph() + + nodes = dg.nodes() + edges = dg.edges() + + print(nodes) + print(edges) + + [family1, family2] = nodes + + theory1 = family1.global_theory() + sentences1 = theory1.sentences() + goals1 = family1.goals() + axioms1 = family1.axioms() + + goal = goals1[0] + print(str(goal)) + print(goal.is_proven()) + + family1.prove(goals=[goal.name()]) + + print(goal.is_proven()) + + +.. code-block:: dol + :caption: Family.dol + + logic OWL + + ontology Family1 = + Class: Person + Class: Woman SubClassOf: Person + ObjectProperty: hasChild + Class: Mother + EquivalentTo: Woman and hasChild some Person + Individual: mary Types: Woman Facts: hasChild john + Individual: john + Individual: mary + Types: Annotations: Implied "true"^^xsd:boolean + Mother + end + + ontology Family2 = + Class: Person + Class: Woman SubClassOf: Person + ObjectProperty: hasChild + Class: Mother + EquivalentTo: Woman and hasChild some Person + Individual: mary Types: Woman Facts: hasChild john + Individual: john Types: Person + Individual: mary + Types: Annotations: Implied "true"^^xsd:boolean Mother + Types: Annotations: Implied "true"^^xsd:boolean Person + Types: Annotations: Implied "true"^^xsd:boolean Woman + end diff --git a/python/api/pyproject.toml b/python/api/pyproject.toml new file mode 100644 index 0000000000..5a07db4456 --- /dev/null +++ b/python/api/pyproject.toml @@ -0,0 +1,13 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "hets-api" +version = "0.1.0.0" +description = "Python interface for hets (http://hets.eu)" +readme = "README.md" +requires-python = ">=3.8" +keywords = ["hets", "heterogeneous specifications"] +license = {text = "GPLv3"} +dependencies = ["hyphen"] diff --git a/python/api/src/hets/BasicProof.py b/python/api/src/hets/BasicProof.py new file mode 100644 index 0000000000..eec50f6d6f --- /dev/null +++ b/python/api/src/hets/BasicProof.py @@ -0,0 +1,81 @@ +from typing import Optional + +from .ProofDetails import ProofDetails +from .ProofKind import ProofKind +from .haskell import PyBasicProof, PyBasicProofGuessed, PyBasicProofConjectured, PyBasicProofHandwritten, \ + pyProofStatusOfPyBasicProof, Just, fromJust + + +class BasicProof: + """ + Result of proving a theory. + + Represents `Static.GTheory.BasicProof` via `HetsAPI.Python.PyBasicProof`. + """ + + def __init__(self, hs_basic_proof: PyBasicProof): + """ + Result of proving a theory. + + :warning: This class should not be instantiated manually. + + :param hs_basic_proof: Haskell object of ``HetsAPI.Internal.BasicProof`` + """ + self._hs_basic_proof = hs_basic_proof + + def is_guessed(self) -> bool: + """ + Returns whether the proof is guessed. + """ + + return isinstance(self._hs_basic_proof, PyBasicProofGuessed) + + def is_conjectured(self) -> bool: + """ + Returns whether the proof is conjectured. + """ + + return isinstance(self._hs_basic_proof, PyBasicProofConjectured) + + def is_handwritten(self) -> bool: + """ + Returns whether the proof is handwritten. + """ + + return isinstance(self._hs_basic_proof, PyBasicProofHandwritten) + + def details(self) -> Optional[ProofDetails]: + """ + Get the details of the proof if available. + + Proof details are not available if the proof is guessed, conjectured, or handwritten. + + :return: Details of the proof + """ + + maybe = pyProofStatusOfPyBasicProof(self._hs_basic_proof) + if isinstance(maybe, Just): + return ProofDetails(fromJust(maybe), self._kind()) + + return None + + def _kind(self) -> Optional[ProofKind]: + if self.is_guessed(): + return ProofKind.GUESSED + elif self.is_conjectured(): + return ProofKind.CONJECTURED + elif self.is_handwritten(): + return ProofKind.HANDWRITTEN + else: + return None + + def kind(self) -> ProofKind: + """ + Get the kind of the proof. + """ + + details = self.details() + if details is None: + return self._kind() + else: + return details.kind() diff --git a/python/api/src/hets/Comorphism.py b/python/api/src/hets/Comorphism.py new file mode 100644 index 0000000000..85dd81a39c --- /dev/null +++ b/python/api/src/hets/Comorphism.py @@ -0,0 +1,54 @@ +from .haskell import comorphismName, PyComorphism, targetLogicName, sourceLogicName + + +class Comorphism: + """ + A comorphism from one logic to another. + + Represents `Logic.Comorphism` via `HetsAPI.Python.PyComorphism`. + """ + + def __init__(self, hs_comorphism: PyComorphism) -> None: + """ + A comorphism from one logic to another. + + :warning: This class should not be instantiated manually. + + :param hs_comorphism: Haskell object of ``HetsAPI.Python.PyComorphism`` + """ + + self._hs_comorphism = hs_comorphism + + def name(self) -> str: + """ + Get the name of the comorphism. + """ + + return comorphismName(self._hs_comorphism) + + def __eq__(self, other): + return isinstance(other, Comorphism) and self.name() == other.name() + + def __hash__(self): + return self.name().__hash__() + + def path_length(self) -> int: + """ + Calculates the length of the comorphism path. + """ + + return len(self.name().split(";")) + + def source(self) -> str: + """ + Get the source logic of the comorphism. + :return: + """ + return sourceLogicName(self._hs_comorphism) + + def target(self) -> str: + """ + Get the target logic of the comorphism. + :return: + """ + return targetLogicName(self._hs_comorphism) diff --git a/python/api/src/hets/ConservativityChecker.py b/python/api/src/hets/ConservativityChecker.py new file mode 100644 index 0000000000..5fb5bda5e9 --- /dev/null +++ b/python/api/src/hets/ConservativityChecker.py @@ -0,0 +1,29 @@ +from .haskell.Python import conservativityCheckerName, conservativityCheckerUsable + +from .maybe import maybe_to_optional + + +class ConservativityChecker: + """ + A tool to check the conservativity of a theory. + + Represents `Proofs.AbstractState.G_cons_checker` via `HetsAPI.Python.PyConsChecker`. + """ + + def __init__(self, hs_conservativity_checker): + self._hs_cons_checker = hs_conservativity_checker + + def name(self) -> str: + """ + Returns the name of the conservativity checker. + :return: + """ + return conservativityCheckerName(self._hs_cons_checker) + + def get_usable(self) -> bool: + """ + Returns whether the conservativity checker is usable. + :return: + """ + res = conservativityCheckerUsable(self._hs_cons_checker).act() + return maybe_to_optional(res) is None diff --git a/python/api/src/hets/ConsistencyChecker.py b/python/api/src/hets/ConsistencyChecker.py new file mode 100644 index 0000000000..702222b6bf --- /dev/null +++ b/python/api/src/hets/ConsistencyChecker.py @@ -0,0 +1,33 @@ +from .haskell import consCheckerName, PyConsChecker + + +class ConsistencyChecker: + """ + A tool to check the consistency of a theory. + + Represents `Proofs.AbstractState.G_cons_checker` via `HetsAPI.Python.PyConsChecker`. + """ + + def __init__(self, hs_cons_checker: PyConsChecker) -> None: + """ + A tool to check the consistency of a theory. + + :warning: This class should not be instantiated manually. + + :param hs_cons_checker: Haskell object of ``HetsAPI.Python.PyConsChecker`` + """ + + self._hs_cons_checker = hs_cons_checker + + def name(self) -> str: + """ + Get the name of the consistency checker + """ + + return consCheckerName(self._hs_cons_checker) + + def __eq__(self, other): + return isinstance(other, ConsistencyChecker) and self.name() == other.name() + + def __hash__(self): + return self.name().__hash__() diff --git a/python/api/src/hets/ConsistencyKind.py b/python/api/src/hets/ConsistencyKind.py new file mode 100644 index 0000000000..e6acfe612b --- /dev/null +++ b/python/api/src/hets/ConsistencyKind.py @@ -0,0 +1,86 @@ +from enum import Enum + + +class ConsistencyKind(Enum): + """ + A type of consistency of a theory. + """ + + INCONSISTENT = 0 + """ + The theory was proven to be inconsistent + """ + + UNKNOWN = 1 + """ + The consistency of the theory is not known + """ + + PROOF_THEORETICALLY_CONSERVATIVE = 2 + """ + The theory is proof-theoretically conservative + """ + + CONSERVATIVE = 3 + """ + The theory is conservative + """ + + MONOMORPHIC = 4 + """ + The theory is monomorphic + """ + + DEFINITIONAL = 5 + """ + The theory is definitional + """ + + ERROR = 6 + """ + An error occurred + """ + + TIMED_OUT = 7 + """ + The consistency checker timed out + """ + + NONE = 8 + """ + There is no consistency + """ + + def to_str(self) -> str: + """ + Converts a consistency kind to human friendly string + """ + + return { + ConsistencyKind.INCONSISTENT: "Inconsistent", + ConsistencyKind.UNKNOWN: "Unknown", + ConsistencyKind.PROOF_THEORETICALLY_CONSERVATIVE: "Proof-theoretically conservative", + ConsistencyKind.CONSERVATIVE: "Conservative", + ConsistencyKind.MONOMORPHIC: "Monomorphic", + ConsistencyKind.DEFINITIONAL: "Definitional", + ConsistencyKind.ERROR: "Errored", + ConsistencyKind.TIMED_OUT: "Timed out", + ConsistencyKind.NONE: "None" + }[self] + + def short_name(self) -> str: + """ + Returns an abbreviated form of the kinds name + """ + + return { + ConsistencyKind.INCONSISTENT: "NotCons", + ConsistencyKind.UNKNOWN: "Unknown", + ConsistencyKind.PROOF_THEORETICALLY_CONSERVATIVE: "PCons", + ConsistencyKind.CONSERVATIVE: "Cons", + ConsistencyKind.MONOMORPHIC: "Mono", + ConsistencyKind.DEFINITIONAL: "Def", + ConsistencyKind.ERROR: "Err", + ConsistencyKind.TIMED_OUT: "TimeOut", + ConsistencyKind.NONE: "" + }[self] diff --git a/python/api/src/hets/ConsistencyStatus.py b/python/api/src/hets/ConsistencyStatus.py new file mode 100644 index 0000000000..19bbf49606 --- /dev/null +++ b/python/api/src/hets/ConsistencyStatus.py @@ -0,0 +1,49 @@ +from .ConsistencyKind import ConsistencyKind +from .conversions import hs_conservativity_to_consistency_kind +from .haskell import ConsStatus + + +class ConsistencyStatus: + """ + Consistency status of a devgraph node. + + Represents `Static.DgUtils.ConsStatus` via `HetsAPI.Internal.ConsStatus`. + """ + + def __init__(self, hs_cons_status: ConsStatus): + """ + Consistency status of a node. + + The status contains the required consistency as well as the result of a proven consistency (or :py:const:`~hets.ConsistencyKind.UNKNOWN` by default) + + :warning: This class should not be instantiated manually. + + :param hs_cons_status: Haskell object of ``HetsAPI.Internal.ConsStatus`` + """ + + self._hs_cons_status = hs_cons_status + + def required(self) -> ConsistencyKind: + """ + Returns the required consistency of the node. + + :return: + """ + hsCons = self._hs_cons_status.requiredConservativity() + return hs_conservativity_to_consistency_kind(hsCons) + + def proven(self) -> ConsistencyKind: + """ + Returns the proven consistency of the node. + :return: + """ + + hsCons = self._hs_cons_status.provenConservativity() + return hs_conservativity_to_consistency_kind(hsCons) + + def is_proven_link(self) -> bool: + """ + Returns whether the conservativity is open + :return: False if the conservativity is open, True otherwise + """ + return self._hs_cons_status.isProvenConsStatusLink() diff --git a/python/api/src/hets/DevGraphEdge.py b/python/api/src/hets/DevGraphEdge.py new file mode 100644 index 0000000000..0e377cf534 --- /dev/null +++ b/python/api/src/hets/DevGraphEdge.py @@ -0,0 +1,269 @@ +""" +Description : Represents `Static.DevGraph.DGLinkLab` +Copyright : (c) Otto-von-Guericke University of Magdeburg +License : GPLv2 or higher, see LICENSE.txt +""" +import typing +from typing import Tuple, Optional, List + +from .Theory import Theory +from .ConsistencyStatus import ConsistencyStatus +from .ConsistencyKind import ConsistencyKind +from .haskell import DGLinkLab, fstOf3, sndOf3, thd, gmorphismOfEdge, developmentGraphEdgeLabelName, \ + developmentGraphEdgeLabelId, getDevGraphLinkType, DevGraphLinkType, LinkKindGlobal, LinkKindLocal, LinkKindHiding, \ + LinkKindFree, LinkKindCofree, TheoremLink, showDoc, getUsableConservativityCheckers, \ + checkConservativityEdgeAndRecord, fst, snd, getEdgeConsStatus, diags, show, linkTypeKind, PyTheory, Conservativity, \ + LibEnv +from .ConservativityChecker import ConservativityChecker +from .Sentence import Sentence +from .HsWrapper import HsHierarchyElement +from .GMorphism import GMorphism +from .result import result_or_raise, result_to_optional +from .conversions import hs_conservativity_to_consistency_kind +from enum import Enum + + +class EdgeKind(Enum): + """ + Enumeration of edge kinds. + + Abstraction over `Static.DevGraph.DGLinkLab` + """ + + # TODO: Add comments describing the different edge kinds + + UNKNOWN = -1 + """ Unknown edge kind """ + GLOBAL = 0 + LOCAL = 1 + HIDING = 2 + FREE = 3 + COFREE = 4 + + +class DevGraphEdge(HsHierarchyElement): + """ + Represents a development graph edge. + + Represents `Static.DevGraph.DGLinkLab` via `HetsAPI.Internal.DGLinkLab` + """ + + def __init__(self, hs_edge: Tuple[int, int, DGLinkLab], parent: Optional[HsHierarchyElement]) -> None: + super().__init__(parent) + + self._hs_edge = hs_edge + + def _type(self) -> DevGraphLinkType: + return getDevGraphLinkType(self._label()) + + def hs_obj(self): + return self._hs_edge + + def _pointer(self): + """ + Returns a tuple to identify the edge in the DevelopmentGraph. + :return: + """ + name, env = self.root().hs_obj() + + return name, env, self._hs_edge + + def origin(self) -> int: + """ + Returns the id of the origin node of the edge. + :return: Id of the origin node + """ + return fstOf3(self._hs_edge) + + def target(self) -> int: + """ + Returns the id of the target node of the edge. + :return: Id of the target node + """ + return sndOf3(self._hs_edge) + + def _label(self) -> DGLinkLab: + """ + Helper function to extract the label from the Haskell tuple. + :return: + """ + return thd(self._hs_edge) + + def morphism(self) -> GMorphism: + """ + Returns the morphism of the edge. + :return: + """ + return GMorphism(gmorphismOfEdge(self._label())) + + def name(self) -> str: + """ + Returns the name of the edge. + :return: + """ + return developmentGraphEdgeLabelName(self._label()) + + def title(self) -> str: + """ + Calculates a title of the edge. + :return: + """ + return f"{self.id()} {self.name()}({self.origin()} --> {self.target()})" + + def id(self) -> int: + """ + Returns the id of the edge. + :return: + """ + return developmentGraphEdgeLabelId(self._label()) + + def kind(self) -> EdgeKind: + """ + Calculates and returns the kind of the edge. + :return: Kind of the edge + """ + t = self._type() + k = linkTypeKind(t) + + if isinstance(k, LinkKindGlobal): + return EdgeKind.GLOBAL + elif isinstance(k, LinkKindLocal): + return EdgeKind.LOCAL + elif isinstance(k, LinkKindHiding): + return EdgeKind.HIDING + elif isinstance(k, LinkKindFree): + return EdgeKind.FREE + elif isinstance(k, LinkKindCofree): + return EdgeKind.COFREE + else: + return EdgeKind.UNKNOWN + + def is_homogeneous(self) -> bool: + """ + Checks whether the edge is homogeneous. + :return: True if the edge is homogeneous, False otherwise + """ + return self._type().linkTypeIsHomogenoeous() + + def is_inclusion(self) -> bool: + """ + Checks whether the edge is an inclusion. + :return: True if the edge is an inclusion, False otherwise + """ + return self._type().linkTypeIsInclusion() + + def info(self) -> str: + """ + Calculates a textual representation of the edge. + :return: + """ + return showDoc(self._label(), "") + + def get_usable_conservativity_checkers(self) -> typing.List[ConservativityChecker]: + """ + Returns a list of usable conservativity checkers for this edge. + :return: List of conservativity checkers + """ + name, env = self.root().hs_obj() + ccs = getUsableConservativityCheckers(self._hs_edge, env, name).act() + + return [ConservativityChecker(cc) for cc in ccs] + + def get_conservativity_checker_by_name(self, name: str) -> Optional[ConservativityChecker]: + """ + Returns a (usable) conservativity checker by its name. + :param name: Name of the conservativity checker + :return: Conservativity checker if found, otherwise None + """ + return next((cc for cc in self.get_usable_conservativity_checkers() if cc.name() == name), None) + + def conservativity(self) -> ConsistencyKind: + """ + Returns the conservativity of the edge. + + Overridden in subclasses. + :return: + """ + return ConsistencyKind.UNKNOWN + + def conservativity_status(self) -> ConsistencyStatus: + """ + Returns the consistency status of the edge. + :return: + """ + edge_lab = self._label() + hs_cons_status = getEdgeConsStatus(edge_lab) + return ConsistencyStatus(hs_cons_status) + + def check_conservativity(self, checker: ConservativityChecker) -> Tuple[Optional[ConsistencyKind], Optional[List[str]], Optional[List[str]], List[str]]: + """ + Checks the conservativity of this edge + + :param checker: Checker to use for checking the conservativity + :return: calculated conservativity together with obligations for the conservativity holding in the source theory and obligations required to hold in an imported theory + """ + check_result = checkConservativityEdgeAndRecord(checker._hs_cons_checker, self._pointer()).act() + + result: Optional[Tuple[Tuple[Conservativity, PyTheory, PyTheory], LibEnv]] = result_to_optional(check_result) + diagnosis = [show(d) for d in diags(check_result)] + + if result is None: + return None, None, None, diagnosis + + conservativity_result = fst(result) + new_env = snd(result) + + conservativity = fstOf3(conservativity_result) + explanationsTheory = Theory(sndOf3(conservativity_result), None) + obligationsTheory = Theory(thd(conservativity_result), None) + + explanations = [str(s) for s in explanationsTheory.sentences()] + obligations = [str(s) for s in obligationsTheory.sentences()] + + self.root().hs_update(new_env) + + return hs_conservativity_to_consistency_kind(conservativity), explanations, obligations, diagnosis + + +class DefinitionDevGraphEdge(DevGraphEdge): + """ + Represents a definitional edge in the development graph. + """ + def conservativity(self) -> ConsistencyKind: + return ConsistencyKind.DEFINITIONAL + + +class TheoremDevGraphEdge(DevGraphEdge): + """ + Represents a theorem edge in the development graph. + """ + + def _type(self) -> TheoremLink: + return super()._type() + + def is_proven(self) -> bool: + """ + Returns whether the edge is proven. + :return: True if the edge has been proven, False otherwise + """ + return self._type().linkTypeIsProven() + + def is_conservativ(self) -> bool: + """ + Returns whether the edge is conservativ. + :return: True if the edge was checked to be conservativ, False otherwise + """ + return self._type().linkTypeIsConservativ() + + def is_pending(self) -> bool: + """ + Returns whether the edge has pending proofs. + :return: True if the edge has pending proofs, False otherwise + """ + return self._type().linkTypeIsPending() + + def conservativity(self) -> ConsistencyKind: + if self.is_conservativ(): + return ConsistencyKind.PROOF_THEORETICALLY_CONSERVATIVE + + return ConsistencyKind.UNKNOWN diff --git a/python/api/src/hets/DevGraphNode.py b/python/api/src/hets/DevGraphNode.py new file mode 100644 index 0000000000..11b578cdad --- /dev/null +++ b/python/api/src/hets/DevGraphNode.py @@ -0,0 +1,289 @@ +import threading +from typing import Tuple, Optional, List + +from .ConsistencyStatus import ConsistencyStatus +from .ConsistencyKind import ConsistencyKind +from .Comorphism import Comorphism +from .result import result_or_raise +from .ConsistencyChecker import ConsistencyChecker +from .LibName import LibName +from .ProofDetails import ProofDetails +from .Prover import Prover +from .HsWrapper import HsHierarchyElement +from .haskell import snd, theoryOfNode, DGNodeLab, fst, Just, Nothing, PyProver, PyComorphism, defaultProofOptions, \ + mkPyProofOptions, proveNode, recordProofResult, ConsistencyStatus as HsConsistencyStatus, PyConsChecker, \ + defaultConsCheckingOptions, \ + PyConsCheckingOptions, checkConsistencyAndRecord, TheoryPointer, globalTheory, recomputeNode, fromJust, \ + developmentGraphNodeLabelName, getDevelopmentGraphNodeType, nodeTypeIsReference, nodeTypeIsProven, \ + nodeTypeIsProvenConsistent, isInternalNode, showGlobalDoc, consistencyStatusType, CSUnchecked, CSTimeout, CSError, \ + CSInconsistent, CSConsistent, consistencyStatusMessage, isNodeReferenceNode, referencedNodeLibName + +from .Theory import Theory + + +class DevGraphNode(HsHierarchyElement): + """ + Represents a development graph node. + + Represents `Static.DevGraph.DGNodeLab` via `HetsAPI.Internal.DGNodeLab`. + """ + + _prove_lock: threading.Lock + """ Lock for acquired by the proof thread when updating the environment """ + + def __init__(self, hs_node: Tuple[int, DGNodeLab], parent: Optional[HsHierarchyElement]) -> None: + super().__init__(parent) + + self._prove_lock = threading.Lock() + + self._hs_node = hs_node + + self._theory: Optional[Theory] = None + + def hs_obj(self): + return self._hs_node + + def id(self) -> int: + """ + Returns the id of the node. + :return: + """ + return fst(self._hs_node) + + def _label(self) -> DGNodeLab: + """ + Helper function to get the label from the Haskell tuple. + :return: + """ + return snd(self._hs_node) + + def name(self) -> str: + """ + Returns the name of the node. + :return: + """ + return developmentGraphNodeLabelName(self._label()) + + def is_internal(self) -> bool: + """ + Returns whether the node is internal. + :return: + """ + return isInternalNode(self._label()) + + def _theory_pointer(self) -> TheoryPointer: + """ + Returns a tuple to identify the node in the DevelopmentGraph. + :return: + """ + node = self.hs_obj() + graph = self.parent().hs_obj() + env_name = self.parent().parent().hs_obj() + + name = fst(env_name) + env = snd(env_name) + + return name, env, graph, node + + def prove(self, + prover: Optional[Prover] = None, + comorphism: Optional[Comorphism] = None, + use_theorems: Optional[bool] = None, + goals_to_prove: Optional[List[str]] = None, + axioms_to_include: Optional[List[str]] = None, + timeout: Optional[int] = None + ) -> List[ProofDetails]: + """ + Proves selected goals in the theory of the node. + + If possible, proofs are done in parallel. + + :param prover: The prover to use or None to use the default prover + :param comorphism: The comorphism to use or None to use the default comorphism + :param use_theorems: Whether to include previously proven goals as theorems in subsequent proofs or None to use the default value + :param goals_to_prove: List of goals to prove (must be names of goals of the global theory of the node) or None to prove all goals + :param axioms_to_include: List of axioms to include (must be names of axioms of the global theory of the node) or None to include all axioms + :param timeout: Maximum time in seconds to spend on proving or None to use the default timeout + :return: Proof result for each goal + """ + prover_maybe = Just(prover._hs_prover) if prover else Nothing().subst(a=PyProver()) + comorphism_maybe = Just(comorphism._hs_comorphism) if comorphism else Nothing().subst(a=PyComorphism()) + + default_opts = defaultProofOptions + + opts = mkPyProofOptions( + prover_maybe, + comorphism_maybe)( + use_theorems if use_theorems is not None else default_opts.proofOptsUseTheorems(), + goals_to_prove if goals_to_prove is not None else default_opts.proofOptsGoalsToProve(), + axioms_to_include if axioms_to_include is not None else default_opts.proofOptsAxiomsToInclude(), + timeout if timeout is not None else default_opts.proofOptsTimeout(), + ) + + prove_result = proveNode(self._theory_pointer(), opts).act() + result = result_or_raise(prove_result) + + self._prove_lock.acquire() + new_env = recordProofResult(self._theory_pointer(), result) + + self.root().hs_update(new_env) + self._prove_lock.release() + + goal_statuses = snd(result) + + return list(ProofDetails(x) for x in goal_statuses) + + def check_consistency(self, + cons_checker: Optional[ConsistencyChecker] = None, + comorphism: Optional[Comorphism] = None, + include_theorems: Optional[bool] = None, + timeout: Optional[int] = None + ) -> Tuple[ConsistencyKind, str]: + """ + Checks the consistency of the theory of the node. + + :param cons_checker: The consistency checker to use or None to use the default consistency checker + :param comorphism: The comorphism to use or None to use the default comorphism + :param include_theorems: Whether to include previously proven goals as theorems in the consistency check or None to use the default value + :param timeout: Maximum time in seconds to spend on checking the consistency or None to use the default timeout + :return: Consistency status and message + """ + + cc_maybe = Just(cons_checker._hs_cons_checker) if cons_checker else Nothing().subst(a=PyConsChecker()) + comorphism_maybe = Just(comorphism._hs_comorphism) if comorphism else Nothing().subst(a=PyComorphism()) + + default_opts = defaultConsCheckingOptions + + opts = PyConsCheckingOptions( + cc_maybe, + comorphism_maybe, + include_theorems if include_theorems is not None else default_opts.consOptsIncludeTheorems(), + timeout if timeout is not None else default_opts.consOptsTimeout(), + ) + + result = checkConsistencyAndRecord(self._theory_pointer(), opts).act() + cc_result, new_env = fst(result), snd(result) + + self.root().hs_update(new_env) + + status_type = consistencyStatusType(cc_result) + status_message = consistencyStatusMessage(cc_result) + + if isinstance(status_type, CSUnchecked): + return ConsistencyKind.UNKNOWN, status_message + elif isinstance(status_type, CSTimeout): + return ConsistencyKind.TIMED_OUT, status_message + elif isinstance(status_type, CSError): + return ConsistencyKind.ERROR, status_message + elif isinstance(status_type, CSInconsistent): + return ConsistencyKind.INCONSISTENT, status_message + elif isinstance(status_type, CSConsistent): + return ConsistencyKind.PROOF_THEORETICALLY_CONSERVATIVE, status_message + else: + return ConsistencyKind.UNKNOWN, status_message + + def global_theory(self) -> Optional[Theory]: + """ + Returns the global theory of the node if applicable. + :return: Global theory or None if not applicable + """ + "" + node_lab = snd(self._hs_node) + + py_theory_maybe = globalTheory(node_lab) + + if isinstance(py_theory_maybe, Just): + py_theory = fromJust(py_theory_maybe) + return Theory(py_theory, self) + + return None + + def recompute(self) -> None: + """ + Recomputes the node and forces an update of the development graph. + :return: + """ + new_lib_env = recomputeNode(self._theory_pointer()) + + root = self.parent().parent() + root.hs_update(new_lib_env) + + def hs_update(self, new_hs_obj) -> None: + self._hs_node = new_hs_obj + + if self._theory: + node_lab = snd(self._hs_node) + hs_theory = theoryOfNode(node_lab) + self._theory.hs_update(hs_theory) + + def theory(self) -> Theory: + """ + Returns the local theory of the node. + :return: + """ + if self._theory is None: + self._theory = Theory(theoryOfNode(snd(self._hs_node)), self) + + return self._theory + + def is_reference_node(self) -> bool: + """ + Returns whether the node is a reference node. + :return: + """ + return nodeTypeIsReference(getDevelopmentGraphNodeType(self._label())) + + def is_proven_node(self) -> bool: + """ + Returns whether the node has been proven. + :return: + """ + return nodeTypeIsProven(getDevelopmentGraphNodeType(self._label())) + + def is_consistency_proven(self) -> bool: + """ + Returns whether the node has been proven consistent. + :return: + """ + return nodeTypeIsProvenConsistent(getDevelopmentGraphNodeType(self._label())) + + def info(self) -> str: + """ + Calculates a textual representation of the node and its theory. + :return: + """ + dev_graph = self.parent() + return showGlobalDoc(dev_graph.global_annotations()._hs_global_annos, self._label(), "") + + +class LocalDevGraphNode(DevGraphNode): + """ + Represents a local development graph node. + """ + def consistency_status(self) -> ConsistencyStatus: + node_lab = snd(self._hs_node) + hs_cons_status = node_lab.getNodeConsStatus() + return ConsistencyStatus(hs_cons_status) + + +class ReferenceDevGraphNode(DevGraphNode): + """ + Represents a reference development graph node. + """ + def referenced_libname(self) -> LibName: + return LibName(referencedNodeLibName(self._label())) + + +def dev_graph_node_from_hs(hs_node: Tuple[int, DGNodeLab], parent: Optional[HsHierarchyElement]) -> DevGraphNode: + """ + Factory function to create a development graph node from a Haskell node. + + :param hs_node: Haskell node + :param parent: Parent element + :return: Python development graph node + """ + label = snd(hs_node) + if isNodeReferenceNode(label): + return ReferenceDevGraphNode(hs_node, parent) + else: + return LocalDevGraphNode(hs_node, parent) diff --git a/python/api/src/hets/DevelopmentGraph.py b/python/api/src/hets/DevelopmentGraph.py new file mode 100644 index 0000000000..34be854fa5 --- /dev/null +++ b/python/api/src/hets/DevelopmentGraph.py @@ -0,0 +1,97 @@ +import logging +import typing +from typing import List, Optional, Dict + +from .DevGraphNode import DevGraphNode, dev_graph_node_from_hs +from .DevGraphEdge import DevGraphEdge, DefinitionDevGraphEdge, TheoremDevGraphEdge +from .GlobalAnnotations import GlobalAnnotations +from .HsWrapper import HsHierarchyElement + +from .haskell import getLNodesFromDevelopmentGraph, DGraph, Nothing, Just, fromJust, getDGNodeById, \ + getLEdgesFromDevelopmentGraph, globalAnnotations, getDevGraphLinkType, thd, DefinitionLink, fst + + +class DevelopmentGraph(HsHierarchyElement): + """ + Represents a development graph. + + Represents `Static.DevGraph.DGraph` + """ + + _logger = logging.getLogger(__name__) + + def __init__(self, hs_development_graph: DGraph, parent: HsHierarchyElement) -> None: + super().__init__(parent) + + self._hs_development_graph = hs_development_graph + + self._nodes: Optional[Dict[int, DevGraphNode]] = None + self._edges: Optional[List[DevGraphEdge]] = None + + def hs_obj(self): + """ + Returns the underlying Haskell object. + :return: + """ + + return self._hs_development_graph + + def hs_update(self, new_hs_obj: DGraph): + self._hs_development_graph = new_hs_obj + self._logger.debug("Updating hs object of development graph") + + if self._nodes: + for node_id, node in self._nodes.items(): + hs_node_maybe = getDGNodeById(self._hs_development_graph)(node_id) + if isinstance(hs_node_maybe, Nothing): + self._logger.warning(f"Node {node_id} could not be found. Probably, it has been deleted") + else: + hsNode = fromJust(hs_node_maybe) + node.hs_update((node_id, hsNode)) + + def nodes(self) -> List[DevGraphNode]: + """ + Get all nodes in the development graph. + :return: List of nodes + """ + if self._nodes is None: + hs_nodes = getLNodesFromDevelopmentGraph(self._hs_development_graph) + self._nodes = dict((fst(x), dev_graph_node_from_hs(x, self)) for x in hs_nodes) + + return list(self._nodes.values()) + + def node_by_id(self, node_id: int) -> Optional[DevGraphNode]: + """ + Get a node by its id. + :param node_id: Id of the node + :return: Node if found, otherwise None + """ + if self._nodes is None: + self._nodes = {} + + self._logger.debug("Get node %s in %s", node_id, self._nodes) + + if node_id not in self._nodes: + hs_node_maybe = getDGNodeById(self._hs_development_graph)(node_id) + if isinstance(hs_node_maybe, Just): + hs_node = fromJust(hs_node_maybe) + node = dev_graph_node_from_hs(hs_node, self) + self._nodes[node_id] = node + + return self._nodes.get(node_id, None) + + def edges(self) -> List[DevGraphEdge]: + """ + Get all edges in the development graph. + :return: List of edges + """ + hs_edges = getLEdgesFromDevelopmentGraph(self._hs_development_graph) + + return [DefinitionDevGraphEdge(x, self) if isinstance(getDevGraphLinkType(thd(x)), DefinitionLink) else TheoremDevGraphEdge(x, self) for x in hs_edges] + + def global_annotations(self) -> GlobalAnnotations: + """ + Get the global annotations of the development graph. + :return: + """ + return GlobalAnnotations(globalAnnotations(self._hs_development_graph)) diff --git a/python/hets/GMorphism.py b/python/api/src/hets/GMorphism.py similarity index 61% rename from python/hets/GMorphism.py rename to python/api/src/hets/GMorphism.py index 89897f5db8..07a8c898aa 100644 --- a/python/hets/GMorphism.py +++ b/python/api/src/hets/GMorphism.py @@ -10,32 +10,69 @@ class GMorphism: + """ + Represents a Grothendieck signature morphisms with indices. + + Represents `Logic.Grothendieck.GMorphism` via `HetsAPI.Python.PyGMorphism`. + """ def __init__(self, hs_g_morphism) -> None: self._hs_g_morphism = hs_g_morphism def name(self) -> str: + """ + Returns the name of the morphism. + :return: + """ return comorphismNameOfGMorphism(self._hs_g_morphism) def description(self) -> str: + """ + Returns the description of the morphism. + :return: + """ return comorphismDescriptionOfGMorphism(self._hs_g_morphism) def signature(self) -> Signature: + """ + Returns the signature of the morphism. + :return: + """ return Signature(signatureOfGMorphism(self._hs_g_morphism)) def comorphism(self) -> Comorphism: + """ + Returns the comorphism of the morphism. + :return: + """ return Comorphism(comorphismOfGMorphism(self._hs_g_morphism)) def domain(self) -> dict: + """ + Returns the domain of the morphism. + :return: + """ return as_json(domainOfGMorphism(self._hs_g_morphism)) def codomain(self) -> dict: + """ + Returns the codomain of the morphism. + :return: + """ return as_json(codomainOfGMorphism(self._hs_g_morphism)) def is_inclusion(self) -> bool: + """ + Returns whether the morphism is an inclusion. + :return: + """ return isGMorphismInclusion(self._hs_g_morphism) def as_json(self) -> dict: + """ + Returns the morphism as a json object. + :return: + """ return as_json(gMorphismToTransportType(self._hs_g_morphism)) def symbol_map(self) -> Dict[object, object]: diff --git a/python/hets/GlobalAnnotations.py b/python/api/src/hets/GlobalAnnotations.py similarity index 52% rename from python/hets/GlobalAnnotations.py rename to python/api/src/hets/GlobalAnnotations.py index 0e88d3b66c..d0184be83d 100644 --- a/python/hets/GlobalAnnotations.py +++ b/python/api/src/hets/GlobalAnnotations.py @@ -2,51 +2,61 @@ class GlobalAnnotations: + """ + Represents the global annotations of a theory. + + Represents `Common.GlobalAnnotations` via `HetsAPI.Internal.GlobalAnnotations`. + """ def __init__(self, hs_global_annos): self._hs_global_annos = hs_global_annos def precedence_annotations(self) -> object: """ Returns the precedence annotations. - WARNING! This functions returns a plain hyphen (haskell) object. Interaction might be difficult. - @return: Plain hyphen object of the precedence annotations + :warning: This functions returns a plain hyphen (haskell) object. Interaction might be difficult. + + :return: Plain hyphen object of the precedence annotations """ return precedenceAnnotations(self._hs_global_annos) def associativity_annotations(self) -> dict: """ Returns the associativity annotations. - WARNING! This functions returns a plain hyphen (haskell) object. Interaction might be difficult. - @return: Plain hyphen object of the associativity annotations + :warning: This functions returns a plain hyphen (haskell) object. Interaction might be difficult. + + :return: Plain hyphen object of the associativity annotations """ return associativityAnnotations(self._hs_global_annos) def display_annos(self) -> dict: """ Returns a map on how to display ids according to a format. - WARNING! This functions returns a plain hyphen (haskell) object. Interaction might be difficult. - @return: Plain hyphen object of the display map + :warning: This functions returns a plain hyphen (haskell) object. Interaction might be difficult. + + :return: Plain hyphen object of the display map """ return displayAnnos(self._hs_global_annos) def literal_annos(self) -> object: """ Returns the literal map. - WARNING! This functions returns a plain hyphen (haskell) object. Interaction might be difficult. - @return: Plain hyphen object of the literal map + :warning: This functions returns a plain hyphen (haskell) object. Interaction might be difficult. + + :return: Plain hyphen object of the literal map """ return literalAnnos(self._hs_global_annos) def prefix_map(self) -> dict: """ Returns a prefix definitions as map from prefix to definition. - WARNING! This functions returns a plain hyphen (haskell) object. Interaction might be difficult. - @return: Plain hyphen object of the prefix map + :warning: This functions returns a plain hyphen (haskell) object. Interaction might be difficult. + + :return: Plain hyphen object of the prefix map """ return prefixMap(self._hs_global_annos) diff --git a/python/api/src/hets/HsWrapper.py b/python/api/src/hets/HsWrapper.py new file mode 100644 index 0000000000..784b445c88 --- /dev/null +++ b/python/api/src/hets/HsWrapper.py @@ -0,0 +1,58 @@ +from typing import Optional + + +class HsWrapper: + """ + Base class for wrapped Haskell elements. + + Offers a common base class for treating immutable Haskell objects as mutable Python objects. + """ + + def hs_obj(self): + """ + Derived classes should implement this function to return the underlying Haskell object. + :return: The underlying Haskell object + """ + pass + + def hs_update(self, new_hs_obj): + """ + Derived classes should implement this function to facilitate a Python mutable object by updating the underlying + Haskell object. + + :param new_hs_obj: New Haskell object + :return: + """ + pass + + +class HsHierarchyElement(HsWrapper): + """ + Base class for wrapped Haskell elements with a hierarchical structure. + + Provides a common base class for treating immutable Haskell objects as mutable Python objects with a hierarchical structure. + """ + def __init__(self, parent: Optional): + super().__init__() + + self._parent = parent + + def parent(self) -> Optional: + """ + Get the parent of the element if the element is not the root. + :return: The parent element + """ + return self._parent + + def root(self): + """ + Get the root element of the hierarchy. + :return: Root element + """ + if self.parent() is None: + return self + + return self.parent().root() + + + diff --git a/python/api/src/hets/LibName.py b/python/api/src/hets/LibName.py new file mode 100644 index 0000000000..37c5a1c93f --- /dev/null +++ b/python/api/src/hets/LibName.py @@ -0,0 +1,63 @@ +from typing import Optional + +from .haskell import LibName as HsLibName, libVersion, getLibId, mimeType, getFilePath, show + +from .maybe import maybe_to_optional +from .Pretty import Pretty + + +class LibName: + """ + Represents information about a library. + + Represents `Common.LibName` via `HetsAPI.Internal.LibName`. + """ + + def __init__(self, hs_libname: HsLibName): + self._hs_libname = hs_libname + + def version(self) -> Optional[str]: + """ + Returns the version of the library, if available + + :return: version of the library, if available + """ + + return maybe_to_optional(libVersion(self._hs_libname)) + + def id(self) -> str: + """ + Returns a unique identifier of the library. + + :return: Unique identifier of the library + """ + + return Pretty(getLibId(self._hs_libname)).to_str() + + def location(self) -> Optional[str]: + """ + Returns the physical location i.e. file path of the library, if available + + :return: Physical location, if available + """ + path = getFilePath(self._hs_libname) + + return None if path == "" else path + + def mime_type(self) -> Optional[str]: + """ + Returns the mime type of the library, if available + + :return: mime type of the library, if available + """ + + return maybe_to_optional(mimeType(self._hs_libname)) + + def __eq__(self, other): + return isinstance(other, LibName) and other.id() == self.id() + + def __hash__(self): + return hash(self.id()) + + def __str__(self): + return show(self._hs_libname) diff --git a/python/api/src/hets/Library.py b/python/api/src/hets/Library.py new file mode 100644 index 0000000000..0a70a70f57 --- /dev/null +++ b/python/api/src/hets/Library.py @@ -0,0 +1,226 @@ +from typing import Optional, Any, Tuple, List + +from .DevelopmentGraph import DevelopmentGraph +from .HsWrapper import HsHierarchyElement +from .Options import Options +from .RefinementTree import RefinementTree +from .haskell import loadLibrary as loadHsLibrary, fst, snd, getGraphForLibrary, Result, resultToMaybe, Just, fromJust, \ + automatic as automaticHs, globalSubsume as globalSubsumeHs, globalDecomposition as globalDecompositionHs, \ + localInference as localInferenceHs, localDecomposition as localDecompositionHs, \ + compositionProveEdges as compositionProveEdgesHs, conservativity as conservativityHs, \ + automaticHideTheoremShift as automaticHideTheoremShiftHs, theoremHideShift as theoremHideShiftHs, \ + computeColimit as computeColimitHs, normalForm as normalFormHs, triangleCons as triangleConsHs, \ + freeness as freenessHs, libFlatImports as libFlatImportsHs, libFlatDUnions as libFlatDUnionsHs, \ + libFlatRenamings as libFlatRenamingsHs, libFlatHiding as libFlatHidingHs, libFlatHeterogen as libFlatHeterogenHs, \ + qualifyLibEnv as qualifyLibEnvHs, getLibraryDependencies, getAvailableSpecificationsForRefinement, getRefinementTree +from .maybe import maybe_to_optional +from .result import result_or_raise +from .LibName import LibName + + +class Library(HsHierarchyElement): + """ + Represents a loaded library. + + Represents the tuple `(Common.LibName.LibName, Static.DevGraph.LibEnv)` + + Library is the root in the hierarchy of library, development graph, nodes, and edges + """ + + def __init__(self, hs_library) -> None: + super().__init__(None) + self._name = fst(hs_library) + self._env = snd(hs_library) + + self._dgraph: Optional[DevelopmentGraph] = None + self._referenced_libraries = {} + + def hs_obj(self): + return self._name, self._env + + def hs_update(self, new_env): + self._env = new_env + + if self._dgraph: + hs_graph = getGraphForLibrary(self._name, self._env) + self._dgraph.hs_update(hs_graph) + + for lib in self._referenced_libraries.values(): + lib.hs_update(new_env) + + def referenced_library(self, name: LibName): + """ + Returns a referenced library. + + :param name: Name of the referenced library + :return: + """ + if name not in self._referenced_libraries: + self._referenced_libraries[name] = Library((name._hs_libname, self._env)) + + return self._referenced_libraries[name] + + def name(self) -> LibName: + """ + Returns the name of the library. + :return: + """ + return LibName(self._name) + + def development_graph(self) -> DevelopmentGraph: + """ + Returns the development graph of the library. + :return: + """ + + if self._dgraph is None: + self._dgraph = DevelopmentGraph(getGraphForLibrary(self._name, self._env), self) + + return self._dgraph + + def environment(self) -> List[Tuple[LibName, DevelopmentGraph]]: + """ + Returns the environment of the library. + :return: + """ + hs_dict: List[Tuple[Any, Any]] = [(fst(x), snd(x)) for x in self._env.toList()] + + return [(LibName(n), DevelopmentGraph(g, self)) for n, g in hs_dict] + + def dependencies(self) -> List[Tuple[LibName, LibName]]: + """ + Returns the dependencies of the library. + :return: + """ + hs_dep = getLibraryDependencies(self._env) + + return [(LibName(fst(x)), LibName(snd(x))) for x in hs_dep] + + def automatic(self): + """ + Applies automatic proof rules. + :return: + """ + + new_env = automaticHs(self._name, self._env) + self.hs_update(new_env) + + def global_subsume(self): + """ + + :return: + """ + new_env = globalSubsumeHs(self._name, self._env) + self.hs_update(new_env) + + def global_decomposition(self): + new_env = globalDecompositionHs(self._name, self._env) + self.hs_update(new_env) + + def local_inference(self): + new_env = localInferenceHs(self._name, self._env) + self.hs_update(new_env) + + def local_decomposition(self): + new_env = localDecompositionHs(self._name, self._env) + self.hs_update(new_env) + + def composition_prove_edges(self): + new_env = compositionProveEdgesHs(self._name, self._env) + self.hs_update(new_env) + + def conservativity(self): + new_env = conservativityHs(self._name, self._env) + self.hs_update(new_env) + + def automatic_hide_theorem_shift(self): + new_env = automaticHideTheoremShiftHs(self._name, self._env) + self.hs_update(new_env) + + def theorem_hide_shift(self): + new_env_r = theoremHideShiftHs(self._name, self._env) + self.hs_update_result(new_env_r) + + def compute_colimit(self): + new_env_r = computeColimitHs(self._name, self._env) + self.hs_update_result(new_env_r) + + def normal_form(self): + new_env_r = normalFormHs(self._name, self._env) + self.hs_update_result(new_env_r) + + def triangle_cons(self): + new_env_r = triangleConsHs(self._name, self._env) + self.hs_update_result(new_env_r) + + def freeness(self): + new_env_r = freenessHs(self._name, self._env) + self.hs_update_result(new_env_r) + + def lib_flat_imports(self): + new_env_r = libFlatImportsHs(self._name, self._env) + self.hs_update_result(new_env_r) + + def lib_flat_d_unions(self): + new_env_r = libFlatDUnionsHs(self._name, self._env) + self.hs_update_result(new_env_r) + + def lib_flat_renamings(self): + new_env_r = libFlatRenamingsHs(self._name, self._env) + self.hs_update_result(new_env_r) + + def lib_flat_hiding(self): + new_env_r = libFlatHidingHs(self._name, self._env) + self.hs_update_result(new_env_r) + + def lib_flat_heterogen(self): + new_env_r = libFlatHeterogenHs(self._name, self._env) + self.hs_update_result(new_env_r) + + def qualify_lib_env(self): + new_env_r = qualifyLibEnvHs(self._name, self._env) + self.hs_update_result(new_env_r) + + def hs_update_result(self, new_env_r: Result): + new_env_m = resultToMaybe(new_env_r) + if isinstance(new_env_m, Just): + self.hs_update(fromJust(new_env_m)) + + def specifications(self) -> List[str]: + """ + Returns the available specifications for refinement. + :return: + """ + return getAvailableSpecificationsForRefinement(self.development_graph()._hs_development_graph) + + def get_refinement_tree(self, spec_name: str) -> Optional[RefinementTree]: + """ + Returns the refinement tree for a specification. + :param spec_name: Name of the specification + :return: Refinement tree if available, otherwise None + """ + hs_ref_tree_m = getRefinementTree(spec_name, self.development_graph()._hs_development_graph) + + hs_ref_tree = maybe_to_optional(hs_ref_tree_m) + if hs_ref_tree is None: + return None + + return RefinementTree(hs_ref_tree) + + +def load_library(path: str, options: Optional[Options] = None) -> Library: + """ + Loads a library from a file path. + + :param path: Path to the library + :param options: Options for loading and interacting with the library + :return: + """ + if options is None: + options = Options() + + result = loadHsLibrary(path, options._hs_options).act() + + name_and_env = result_or_raise(result, "Failed to load library") + + return Library(name_and_env) diff --git a/python/hets/Logic.py b/python/api/src/hets/Logic.py similarity index 51% rename from python/hets/Logic.py rename to python/api/src/hets/Logic.py index 5abc3ceed5..f58bb5f93f 100644 --- a/python/hets/Logic.py +++ b/python/api/src/hets/Logic.py @@ -1,10 +1,21 @@ class Logic: + """ + Represents a simplified logic by name and description. + """ def __init__(self, name: str, description: str): self._name = name self._description = description def name(self) -> str: + """ + Returns the name of the logic. + :return: + """ return self._name def description(self) -> str: + """ + Returns the description of the logic. + :return: + """ return self._description diff --git a/python/api/src/hets/Options.py b/python/api/src/hets/Options.py new file mode 100644 index 0000000000..5569f45645 --- /dev/null +++ b/python/api/src/hets/Options.py @@ -0,0 +1,131 @@ +import typing + +import hets.haskell.Internal as Internal + + +class Option: + """ + A generic wrapper around fields of `Driver.Options.HetcatsOpts`. + """ + name: str + description: str + typ: typing.Any + _hs_name: str + + def __init__(self, name, description, typ, hs_name: str): + self.name = name + self.description = description + self.typ = typ + self._hs_name = hs_name + + def _hs_setter(self) -> typing.Callable[[Internal.HetcatsOpts, typing.Any], Internal.HetcatsOpts]: + setter_name = "optsWith" + self._hs_name[0].upper() + self._hs_name[1:] + return Internal.__dict__[setter_name] + + def _hs_getter(self) -> typing.Callable[[Internal.HetcatsOpts], typing.Any]: + return Internal.HetcatsOpts.__dict__[self._hs_name] + + +_ALL_OPTIONS = [ + Option("url_catalog", "", [(str, str)], "urlCatalog"), + Option("infiles", "", [str], "infiles"), + Option("spec_names", "", [typing.Any], "specNames"), + Option("trans_names", "", [typing.Any], "transNames"), + Option("lossy_trans", "", bool, "lossyTrans"), + Option("view_names", "", [typing.Any], "viewNames"), + Option("libdirs", "", [str], "libdirs"), + Option("model_spar_q", "", str, "modelSparQ"), + Option("counter_spar_q", "", int, "counterSparQ"), + Option("outdir", "", str, "outdir"), + Option("database_do_migrate", "", bool, "databaseDoMigrate"), + Option("database_output_file", "", str, "databaseOutputFile"), + Option("database_config_file", "", str, "databaseConfigFile"), + Option("database_sub_config_key", "", str, "databaseSubConfigKey"), + Option("database_file_version_id", "", str, "databaseFileVersionId"), + Option("database_reanalyze", "", bool, "databaseReanalyze"), + Option("xupdate", "", str, "xupdate"), + Option("recurse", "", bool, "recurse"), + Option("verbose", "", int, "verbose"), + Option("def_logic", "", str, "defLogic"), + Option("def_syntax", "", str, "defSyntax"), + Option("output_to_stdout", "", bool, "outputToStdout"), + Option("interactive", "", bool, "interactive"), + Option("connect_p", "", int, "connectP"), + Option("connect_h", "", str, "connectH"), + Option("uncolored", "", bool, "uncolored"), + Option("xml_flag", "", bool, "xmlFlag"), + Option("apply_automatic", "", bool, "applyAutomatic"), + Option("compute_normal_form", "", bool, "computeNormalForm"), + Option("dump_opts", "", [str], "dumpOpts"), + Option("disable_certificate_verification", "", bool, "disableCertificateVerification"), + Option("use_lib_pos", "", bool, "useLibPos"), + Option("unlit", "", bool, "unlit"), + Option("serve", "", bool, "serve"), + Option("listen", "", int, "listen"), + Option("pid_file", "", str, "pidFile"), + Option("whitelist", "", [[str]], "whitelist"), + Option("blacklist", "", [[str]], "blacklist"), + Option("run_mmt", "", bool, "runMMT"), + Option("full_theories", "", bool, "fullTheories"), + Option("output_logic_list", "", bool, "outputLogicList"), + Option("output_logic_graph", "", bool, "outputLogicGraph"), + Option("file_type", "", bool, "fileType"), + Option("access_token", "", str, "accessToken"), + Option("http_request_headers", "", [str], "httpRequestHeaders"), + Option("full_sign", "", bool, "fullSign"), + Option("print_ast", "", bool, "printAST"), +] + +_ALL_OPTIONS_BY_NAME = dict([(x.name, x) for x in _ALL_OPTIONS]) + + +class Options: + """ + Options for loading and interaction with the library to be passed to the Hets API. See `Driver.Options.HetcatsOpts` + for details about available options. + + Wrapper arround `Driver.Options.HetcatsOpts`. + """ + _hs_options: Internal.HetcatsOpts = None + + def __init__(self, **kwargs): + self._hs_options = Internal.defaultHetcatsOpts + + self.patch(**kwargs) + + def __getitem__(self, key: typing.Union[str, Option]): + if isinstance(key, Option): + key = key.name + + if key not in _ALL_OPTIONS_BY_NAME: + raise AttributeError(f"Unknown key '{key}'") + + option = _ALL_OPTIONS_BY_NAME[key] + return option._hs_getter()(self._hs_options) + + def __setitem__(self, key: typing.Union[str, Option], value): + if isinstance(key, Option): + key = key.name + + if key not in _ALL_OPTIONS_BY_NAME: + raise AttributeError(f"Unknown key '{key}'") + + option = _ALL_OPTIONS_BY_NAME[key] + self._hs_options = option._hs_setter()(self._hs_options, value) + + def __len__(self): + return len(_ALL_OPTIONS) + + def __iter__(self): + return iter(_ALL_OPTIONS) + + def patch(self, **kwargs): + for key, value in kwargs.items(): + if key in _ALL_OPTIONS_BY_NAME and value is not None: + hs_fn = _ALL_OPTIONS_BY_NAME[key]._hs_setter() + self._hs_options = hs_fn(self._hs_options, value) + + def to_dict(self): + return dict((option.name, option._hs_getter()(self._hs_options)) for option in _ALL_OPTIONS) + + diff --git a/python/api/src/hets/Pretty.py b/python/api/src/hets/Pretty.py new file mode 100644 index 0000000000..d80746201a --- /dev/null +++ b/python/api/src/hets/Pretty.py @@ -0,0 +1,28 @@ +from typing import TypeVar, Generic + +from .haskell import showDoc + +A = TypeVar("A") + + +class Pretty(Generic[A]): + """ + Represents Haskell objects that can be pretty-printed. + + I.e. objects that implement the `Pretty` typeclass in Haskell. + """ + def __init__(self, hs_obj): + self._hs_obj = hs_obj + + def to_str(self) -> str: + """ + Returns the pretty-printed string representation of the Haskell object. + :return: + """ + return showDoc(self._hs_obj)("") + + def __eq__(self, other): + return isinstance(other, Pretty) and other.to_str() == self.to_str() + + def __hash__(self): + return hash(self.to_str()) diff --git a/python/api/src/hets/ProofDetails.py b/python/api/src/hets/ProofDetails.py new file mode 100644 index 0000000000..b4760f2430 --- /dev/null +++ b/python/api/src/hets/ProofDetails.py @@ -0,0 +1,105 @@ +from datetime import datetime, timedelta + +from .haskell import ProofStatus as ProofStatusHs, GoalStatus, tacticScriptContent, Open, Proved, Disproved, show + +from .ProofKind import ProofKind + +from typing import List, Optional + + +class ProofDetails: + """ + Represents the details of a proof. + + Represents `Logic.Prover.ProofStatus` via `HetsAPI.Internal.ProofStatus`. + """ + def __init__(self, hs_proof_status: ProofStatusHs, kind: Optional[ProofKind] = None): + self._hs_proof_status = hs_proof_status + + self._kind = kind + + def goal_name(self) -> str: + """ + Returns the name of the goal that was attempted to be proven. + :return: + """ + return self._hs_proof_status.goalName() + + def goal_status(self) -> GoalStatus: + """ + Returns the status of the goal as a result of the proof. + :return: + """ + return self._hs_proof_status.goalStatus() + + def used_axioms(self) -> List[str]: + """ + Returns the axioms that were used in the proof. + :return: List of names of axioms in the theory + """ + return list(self._hs_proof_status.usedAxioms()) + + def used_prover(self) -> str: + """ + Returns the prover that was used in the proof. + :return: Name of the prover + """ + return self._hs_proof_status.usedProver() + + def used_time(self) -> timedelta: + """ + Returns the elapsed time of the proof. + :return: + """ + used_time_str = show(self._hs_proof_status.usedTime()) + if used_time_str.startswith("-"): + # Sometimes the prover returns -1 as time. Return 0 instead. + return timedelta(seconds=-1) + + if "." in used_time_str: + used_time_str = used_time_str.split(".")[0] + + used_time = datetime.strptime(used_time_str, "%H:%M:%S") + + return used_time - datetime.strptime("", "") + + def tactic_script(self) -> str: + """ + Returns the tactic script that was used in the proof. + :return: + """ + return tacticScriptContent(self._hs_proof_status.tacticScript()) + + def proof_tree(self) -> str: + """ + Returns the proof tree of the proof if available. + :return: String representation of the proof tree or an empty string if not available + """ + return show(self._hs_proof_status.proofTree()) + + def proof_lines(self) -> List[str]: + """ + Returns the proof lines of the proof or an empty list if not available. + :return: + """ + return list(self._hs_proof_status.proofLines()) + + def kind(self) -> ProofKind: + """ + Returns the result kind of the proof. + :return: + """ + if self._kind is not None: + return self._kind + + status = self.goal_status() + + if isinstance(status, Open): + if any("timeout" in reason.lower() for reason in status.goalStatusOpenReason()): + return ProofKind.TIMED_OUT + + return ProofKind.OPEN + if isinstance(status, Disproved): + return ProofKind.DISPROVEN + if isinstance(status, Proved): + return ProofKind.PROVEN diff --git a/python/api/src/hets/ProofKind.py b/python/api/src/hets/ProofKind.py new file mode 100644 index 0000000000..a1df28b3c2 --- /dev/null +++ b/python/api/src/hets/ProofKind.py @@ -0,0 +1,38 @@ +from enum import Enum + + +class ProofKind(Enum): + """ + Enum for the different kinds of proof results. + """ + UNKNOWN = -1 + """ The proof result is unknown. """ + OPEN = 1 + """ The proof is still open. """ + PROVEN = 2 + """ The goal has been proven successfully. """ + PROVEN_BY_INCONSISTENCY = 3 + """ The goal has been proven by inconsistency. """ + DISPROVEN = 4 + """ The goal has been disproven. """ + TIMED_OUT = 5 + """ The proof timed out. """ + GUESSED = 6 + """ The proof result is guessed. """ + CONJECTURED = 7 + """ The goal is assumed to hold as a conjecture. """ + HANDWRITTEN = 8 + """ The goal is proven by a handwritten proof. """ + + def to_str(self) -> str: + return { + ProofKind.UNKNOWN: "Unknown", + ProofKind.OPEN: "Open", + ProofKind.PROVEN: "Proven", + ProofKind.PROVEN_BY_INCONSISTENCY: "Proven by inconsistency", + ProofKind.DISPROVEN: "Disproven", + ProofKind.TIMED_OUT: "Timed out", + ProofKind.GUESSED: "Guessed", + ProofKind.CONJECTURED: "Conjectured", + ProofKind.HANDWRITTEN: "Handwritten" + }[self] diff --git a/python/hets/ProofState.py b/python/api/src/hets/ProofState.py similarity index 59% rename from python/hets/ProofState.py rename to python/api/src/hets/ProofState.py index 951869eede..47599d3ed3 100644 --- a/python/hets/ProofState.py +++ b/python/api/src/hets/ProofState.py @@ -1,9 +1,3 @@ -""" -Description : Represents `Proofs.AbstractState.ProofState` -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" - from typing import List from .Sentence import Sentence @@ -11,27 +5,56 @@ class ProofState: + """ + Represents the result of a proof attempt. + + Represents `Proofs.AbstractState.ProofState` via `HetsAPI.Internal.ProofState`. + """ def __init__(self, hs_proof_state: ProofStateHs, theory): self._hs_proof_state = hs_proof_state self._theory = theory def selected_goals(self) -> List[Sentence]: + """ + Returns the selected goals of the proof state. + :return: List selected goals + """ goal_names = self._hs_proof_state.selectedGoals() return [x for x in self._theory.goals() if x.name() in goal_names] def included_axioms(self) -> List[Sentence]: + """ + Returns the included axioms of the proof state. + :return: List of included axioms + """ goal_names = self._hs_proof_state.includedAxioms() return [x for x in self._theory.axioms() if x.name() in goal_names] def included_theorems(self) -> List[Sentence]: + """ + Returns the theorems included in the proof. + :return: + """ goal_names = self._hs_proof_state.includedTheorems() return [x for x in self._theory.sentences() if x.name() in goal_names] def acc_diags(self) -> List[Diagnosis]: + """ + Returns the accumulated diagnoses occured during the proof. + :return: + """ return self._hs_proof_state.accDiags() def selected_prover_name(self) -> str: + """ + Returns the name of the selected prover. + :return: + """ return self._hs_proof_state.selectedProver() def selected_cons_checker_name(self) -> str: + """ + Returns the name of the selected consistency checker. + :return: + """ return self._hs_proof_state.selectedConsChecker() diff --git a/python/hets/Prover.py b/python/api/src/hets/Prover.py similarity index 65% rename from python/hets/Prover.py rename to python/api/src/hets/Prover.py index 629d92e9f8..0804be7f5a 100644 --- a/python/hets/Prover.py +++ b/python/api/src/hets/Prover.py @@ -1,19 +1,23 @@ -""" -Description : Represents `Logic.Prover.Prover` -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" - from typing import Optional, List from .haskell import proverName, PyProver, PyComorphism class Prover: + """ + Represents a prover. + + Represents `Logic.Prover.Prover` + """ + def __init__(self, hs_prover: PyProver) -> None: self._hs_prover = hs_prover def name(self) -> str: + """ + Returns the name of the prover. + :return: + """ return proverName(self._hs_prover) def __eq__(self, other): @@ -22,3 +26,6 @@ def __eq__(self, other): def __hash__(self): return self.name().__hash__() + def __repr__(self): + return f"<{__name__} '{self.name()}'>" + diff --git a/python/api/src/hets/RefinementTree.py b/python/api/src/hets/RefinementTree.py new file mode 100644 index 0000000000..9ef81fa43d --- /dev/null +++ b/python/api/src/hets/RefinementTree.py @@ -0,0 +1,34 @@ +from typing import List + +from .RefinementTreeLink import RefinementTreeLink +from .RefinementTreeNode import RefinementTreeNode + +from .haskell import labNodes, labEdges + + +class RefinementTree: + """ + Represents a refinement tree. + + Represents `Graph.Gr RTNodeLab RTLinkLab`. + """ + _links: List[RefinementTreeLink] + _nodes: List[RefinementTreeNode] + + def __init__(self, hs_refinement_tree): + self._nodes = [RefinementTreeNode(n) for n in labNodes(hs_refinement_tree)] + self._links = [RefinementTreeLink(e) for e in labEdges(hs_refinement_tree)] + + def nodes(self) -> List[RefinementTreeNode]: + """ + Returns the nodes of the refinement tree. + :return: + """ + return self._nodes + + def edges(self) -> List[RefinementTreeLink]: + """ + Returns the edges of the refinement tree. + :return: + """ + return self._links diff --git a/python/api/src/hets/RefinementTreeLink.py b/python/api/src/hets/RefinementTreeLink.py new file mode 100644 index 0000000000..675d6c08b3 --- /dev/null +++ b/python/api/src/hets/RefinementTreeLink.py @@ -0,0 +1,59 @@ +import enum +import typing + +from hets.haskell import RefinementTreeLink as HsRefinementTreeLink, thd, fstOf3, sndOf3, rtl_type, RTRefine, RTComp + + +class RefinementTreeLinkKind(enum.Enum): + UNKNOWN = -1 + COMPONENT = 1 + SIMPLE = 2 + + +class RefinementTreeLink: + """ + Represents a link in a refinement tree with source and target node IDs and label data for the edge. + + Represents `(Int, Int, RTLinkLab)`. + """ + + def __init__(self, hs_edge: typing.Tuple[int, int, HsRefinementTreeLink]): + self._hs_edge = hs_edge + + def _label(self) -> HsRefinementTreeLink: + return thd(self._hs_edge) + + def id(self) -> typing.Tuple[int, int]: + """ + Returns a tuple identifying the edge. + :return: + """ + return self.source_id(), self.target_id() + + def source_id(self) -> int: + """ + Returns the ID of the source node. + :return: + """ + + return fstOf3(self._hs_edge) + + def target_id(self) -> int: + """ + Returns the ID of the target node. + :return: + """ + return sndOf3(self._hs_edge) + + def kind(self) -> RefinementTreeLinkKind: + """ + Returns the kind of the link. + :return: + """ + typ = rtl_type(self._label()) + if isinstance(typ, RTRefine): + return RefinementTreeLinkKind.SIMPLE + elif isinstance(typ, RTComp): + return RefinementTreeLinkKind.COMPONENT + else: + return RefinementTreeLinkKind.UNKNOWN diff --git a/python/api/src/hets/RefinementTreeNode.py b/python/api/src/hets/RefinementTreeNode.py new file mode 100644 index 0000000000..c92b532adc --- /dev/null +++ b/python/api/src/hets/RefinementTreeNode.py @@ -0,0 +1,37 @@ +import typing + +from hets.haskell import RefinementTreeNode as HsRefinementTreeNode, snd, isRootNode, rtn_name, rtNodeLab, fst + + +class RefinementTreeNode: + """ + Represents a refinement tree node with their ID and label data. + + Represents `(Int, RTNodeLab)`. + """ + def __init__(self, hs_node: typing.Tuple[int, HsRefinementTreeNode]): + self._hs_node = hs_node + + def _label(self) -> HsRefinementTreeNode: + return snd(self._hs_node) + + def is_root(self) -> bool: + """ + Returns whether the node is a root node. + :return: True if the node is a root node, False otherwise + """ + return isRootNode(self._label()) + + def name(self) -> str: + """ + Returns the name of the node. + :return: + """ + return rtn_name(rtNodeLab(self._label())) + + def id(self) -> int: + """ + Returns the ID of the node. + :return: + """ + return fst(self._hs_node) diff --git a/python/api/src/hets/Sentence.py b/python/api/src/hets/Sentence.py new file mode 100644 index 0000000000..67aaacd9a4 --- /dev/null +++ b/python/api/src/hets/Sentence.py @@ -0,0 +1,114 @@ +import json +from typing import Tuple, Callable, List, Optional + +from .maybe import maybe_to_optional +from .haskell import fst, snd, PyTheorySentence, Sentence as PySentence, PyBasicProof, theorySentenceIsAxiom, \ + theorySentenceWasTheorem, theorySentenceIsDefined, theorySentenceGetTheoremStatus, theorySentencePriority, \ + theorySentenceContent, Just, fromJust, theorySentenceBestProof +from .json_conversion import as_json + +from .Comorphism import Comorphism +from .BasicProof import BasicProof +from .ProofDetails import ProofDetails +from hets import ProofKind + +from .HsWrapper import HsHierarchyElement + + +class Sentence(HsHierarchyElement): + """ + Represents a sentence in a theory. + + Represents `Logic.Logic.Sentence` via `HetsAPI.Python.PyTheorySentence`. + """ + def __init__(self, hs_sentence_with_name: Tuple[str, PyTheorySentence], + hs_pretty_fn: Callable[[PySentence], str], + parent: Optional[HsHierarchyElement] = None) -> None: + super().__init__(parent) + self._hs_obj = hs_sentence_with_name + self._name = fst(hs_sentence_with_name) + self._hs_sentence = snd(hs_sentence_with_name) + self._hs_pretty_fn = hs_pretty_fn + + def hs_obj(self): + return self._hs_obj + + def hs_update(self, new_hs_obj): + self._hs_obj = new_hs_obj + self._name = fst(new_hs_obj) + self._hs_sentence = snd(new_hs_obj) + + def name(self) -> str: + """ + Returns the name of the sentence. + :return: + """ + return self._name + + def as_json(self) -> dict: + """ + Converts the sentence to a JSON object. + :return: + """ + return as_json(theorySentenceContent(self._hs_sentence)) + + def is_axiom(self) -> bool: + """ + Returns whether the sentence is an axiom. + :return: True if the sentence is an axiom, False otherwise + """ + return theorySentenceIsAxiom(self._hs_sentence) + + def was_theorem(self) -> bool: + """ + Returns whether the sentence was a goal but was proven and included as axiom in a proof. + :return: True if the sentence originally was a goal + """ + return theorySentenceWasTheorem(self._hs_sentence) + + def is_defined(self) -> bool: + """ + Returns whether the sentence is defined. TODO: Reallly? + :return: + """ + return theorySentenceIsDefined(self._hs_sentence) + + def is_proven(self) -> bool: + """ + Returns whether the sentence is proven. + :return: + """ + return any(b.kind() == ProofKind.PROVEN for _, b in self.theorem_status()) + + def theorem_status(self) -> List[Tuple[Comorphism, BasicProof]]: + """ + Returns the theorem status of the sentence. + :return: + """ + return list((Comorphism(fst(x)), BasicProof(snd(x))) for x in theorySentenceGetTheoremStatus(self._hs_sentence)) + + def best_proof(self) -> Optional[BasicProof]: + """ + Returns the best proof of the sentence. + :return: + """ + proof = maybe_to_optional(theorySentenceBestProof(self._hs_sentence)) + return BasicProof(proof) if proof is not None else None + + def priority(self) -> Optional[str]: + """ + Returns the priority of the sentence if it has one. + :return: + """ + + priority = theorySentencePriority(self._hs_sentence) + if isinstance(priority, Just): + return fromJust(priority) + + return None + + def __str__(self) -> str: + return self._hs_pretty_fn(theorySentenceContent(self._hs_sentence)) + + def __repr__(self): + return f"<{__name__} object representing sentence {self.name()} = '{str(self)}'>" diff --git a/python/hets/Signature.py b/python/api/src/hets/Signature.py similarity index 56% rename from python/hets/Signature.py rename to python/api/src/hets/Signature.py index b78a31769a..d7224246dc 100644 --- a/python/hets/Signature.py +++ b/python/api/src/hets/Signature.py @@ -5,11 +5,24 @@ class Signature: + """ + Represents a signature. + + Represents `Logic.Signature.Signature` via `HetsAPI.Python.ExtSign`. + """ def __init__(self, hs_signature: ExtSign): self._hs_signature = hs_signature def non_imported_symbols(self) -> List[dict]: + """ + Returns the non-imported symbols of the signature as JSON objects. + :return: + """ return [as_json(symbol) for symbol in self._hs_signature.nonImportedSymbols()] def plain(self) -> dict: + """ + Returns the plain signature as a JSON object. + :return: + """ return as_json(self._hs_signature.plainSign()) diff --git a/python/api/src/hets/Theory.py b/python/api/src/hets/Theory.py new file mode 100644 index 0000000000..2de6ee12ae --- /dev/null +++ b/python/api/src/hets/Theory.py @@ -0,0 +1,299 @@ +from __future__ import annotations + +from typing import List, Optional, Tuple, Dict + +from .Comorphism import Comorphism +from .ConsistencyChecker import ConsistencyChecker +from .HsWrapper import HsHierarchyElement +from .Logic import Logic +from .Prover import Prover +from .Sentence import Sentence +from .Signature import Signature +from .haskell import (fst, PyTheory, getUsableProvers, getUsableConsistencyCheckers, + getAvailableComorphisms, getAllSentences, getAllGoals, getAllAxioms, getProvenGoals, + prettySentence, showTheory, + getUnprovenGoals, OMap, snd, + logicNameOfTheory, + logicDescriptionOfTheory, signatureOfTheory, sublogicOfPyTheory, getTheoryForSelection, + translateTheory) +from .result import result_or_raise + + +class Theory(HsHierarchyElement): + """ + Represents a logical theory. + + Represents `Static.GTheory.G_theory` via `HetsAPI.Python.PyTheory`. + """ + + def __init__(self, hs_theory: PyTheory, parent: Optional[HsHierarchyElement]) -> None: + super().__init__(parent) + self._sentences: Optional[List[Sentence]] = None + self._axioms: Optional[List[Sentence]] = None + self._goals: Optional[List[Sentence]] = None + self._proven_goals: Optional[List[Sentence]] = None + self._unproven_goals: Optional[List[Sentence]] = None + self._hs_theory = hs_theory + self._hs_pretty_sentence = prettySentence(hs_theory) + + def hs_obj(self): + return self._hs_theory + + def hs_update(self, new_hs_obj): + self._hs_theory = new_hs_obj + + new_sentences = OMap.toList(getAllSentences(self._hs_theory)) + + for name_and_sentence in new_sentences: + name = fst(name_and_sentence) + + if self._sentences: + for sentence in self._sentences: + if sentence.name() == name: + sentence.hs_update(name_and_sentence) + + if self._axioms: + for axiom in self._axioms: + if axiom.name() == name: + axiom.hs_update(name_and_sentence) + + if self._goals: + for goal in self._goals: + if goal.name() == name: + goal.hs_update(name_and_sentence) + + if self._proven_goals: + for proven_goal in self._proven_goals: + if proven_goal.name() == name: + proven_goal.hs_update(name_and_sentence) + + if self._unproven_goals: + for unproven_goal in self._unproven_goals: + if unproven_goal.name() == name: + unproven_goal.hs_update(name_and_sentence) + + # Reset cached proven and unproven goals as they might have changed. + # Previously queried goals are updated and a previously unproven goal is now proven, the instance reflects the + # change. However, the lists should be re-queried to not list proven goals in the unproven list. + self._proven_goals = None + self._unproven_goals = None + + def get_usable_provers(self) -> List[Prover]: + """ + Get all usable provers for the theory. + :return: List of usable provers + """ + provers = getUsableProvers(self._hs_theory).act() + return list({Prover(fst(p)) for p in provers}) + + def get_usable_provers_with_comorphisms(self) -> Dict[Prover, List[Comorphism]]: + """ + Get all usable provers for the theory with their comorphisms. + :return: Dictionary with provers as keys and lists of suitable comorphisms as values + """ + provers = getUsableProvers(self._hs_theory).act() + result = dict() + for prover_and_comorphism in provers: + prover = fst(prover_and_comorphism) + comorphism = snd(prover_and_comorphism) + result.setdefault(Prover(prover), []).append(Comorphism(comorphism)) + + return result + + def get_usable_provers_and_comorphisms(self) -> List[Tuple[Prover, Comorphism]]: + """ + Get all usable provers for the theory with their comorphisms. + :return: List of tuples of a prover and a suitable comorphisms + """ + provers = getUsableProvers(self._hs_theory).act() + return list((Prover(fst(p)), Comorphism(snd(p))) for p in provers) + + def get_prover_by_name(self, name: str) -> Optional[Prover]: + """ + Get a prover by its name. + :param name: Name of the prover + :return: The Prover if found, otherwise None + """ + matches = list(p for p in self.get_usable_provers() if p.name() == name) + if len(matches) == 1: + return matches[0] + return None + + def get_usable_consistency_checkers_with_comorphisms(self) -> Dict[ConsistencyChecker, List[Comorphism]]: + """ + Get all usable consistency checkers for the theory with their comorphisms. + :return: Dictionary with consistency checkers as keys and lists of suitable comorphisms as values + """ + consistency_checkers = getUsableConsistencyCheckers(self._hs_theory).act() + result = dict() + for consistency_checker_and_comorphism in consistency_checkers: + cc = fst(consistency_checker_and_comorphism) + comorphism = snd(consistency_checker_and_comorphism) + result.setdefault(ConsistencyChecker(cc), []).append(Comorphism(comorphism)) + + return result + + def get_usable_consistency_checkers(self) -> List[ConsistencyChecker]: + """ + Get all usable consistency checkers for the theory. + :return: + """ + ccs = getUsableConsistencyCheckers(self._hs_theory).act() + return list({ConsistencyChecker(fst(cc)) for cc in ccs}) + + def get_usable_consistency_checkers_and_comorphisms(self) -> List[Tuple[ConsistencyChecker, Comorphism]]: + """ + Get all usable consistency checkers for the theory with their comorphisms. + :return: List of tuples of a consistency checker and a suitable comorphisms + """ + consistency_checkers = getUsableConsistencyCheckers(self._hs_theory).act() + return list((ConsistencyChecker(fst(cc)), Comorphism(snd(cc))) for cc in consistency_checkers) + + def get_consistency_checker_by_name(self, name: str) -> Optional[ConsistencyChecker]: + """ + Get a consistency checker by its name. + :param name: Name of the consistency checker + :return: The ConsistencyChecker if found, otherwise None + """ + checkers = self.get_usable_consistency_checkers() + return next((cc for cc in checkers if cc.name() == name), None) + + def get_available_comorphisms(self) -> List[Comorphism]: + """ + Get all available comorphisms for the theory. + :return: + """ + comorphisms = getAvailableComorphisms(self._hs_theory) + return [Comorphism(x) for x in comorphisms] + + def sentence_by_name(self, name: str) -> Optional[Sentence]: + """ + Get a sentence by its name. + + :param name: Name of the sentence + :return: The Sentence if found, otherwise None + """ + return next((s for s in self.sentences() if s.name() == name), None) + + def sentences(self) -> List[Sentence]: + """ + Get all sentences in the theory. + :return: + """ + if self._sentences is None: + sentences = getAllSentences(self._hs_theory) + self._sentences = [Sentence(x, self._hs_pretty_sentence, self) for x in OMap.toList(sentences)] + + return self._sentences + + def axioms(self) -> List[Sentence]: + """ + Get all axioms in the theory. + :return: + """ + if self._axioms is None: + axioms = getAllAxioms(self._hs_theory) + self._axioms = [Sentence(x, self._hs_pretty_sentence, self) for x in OMap.toList(axioms)] + + return self._axioms + + def goals(self) -> List[Sentence]: + """ + Get all goals in the theory. + :return: + """ + if self._goals is None: + self._goals = [Sentence(x, self._hs_pretty_sentence, self) for x in + OMap.toList(getAllGoals(self._hs_theory))] + + return self._goals + + def proven_goals(self) -> List[Sentence]: + """ + Get all proven goals in the theory. + :return: + """ + if self._proven_goals is None: + self._proven_goals = [Sentence(x, self._hs_pretty_sentence, self) for x in + OMap.toList(getProvenGoals(self._hs_theory))] + + return self._proven_goals + + def unproven_goals(self) -> List[Sentence]: + """ + Get all unproven goals in the theory. + :return: + """ + if self._unproven_goals is None: + self._unproven_goals = [Sentence(x, self._hs_pretty_sentence, self) for x in + OMap.toList(getUnprovenGoals(self._hs_theory))] + + return self._unproven_goals + + def logic(self) -> Logic: + """ + Get the logic of the theory. + :return: + """ + return Logic(logicNameOfTheory(self._hs_theory), logicDescriptionOfTheory(self._hs_theory)) + + def signature(self) -> Signature: + """ + Get the signature of the theory. + :return: + """ + return Signature(signatureOfTheory(self._hs_theory)) + + def sentence_by_name(self, name: str) -> Optional[Sentence]: + """ + Get a sentence by its name. + :param name: Name of the sentence + :return: The Sentence if found, otherwise None + """ + return next(iter(s for s in self.sentences() if s.name() == name), None) + + def get_sublogic(self) -> str: + """ + Calculate and return the sublogic of the theory. + :return: + """ + return sublogicOfPyTheory(self._hs_theory) + + def with_selection(self, + axioms: Optional[List[str]] = None, + goals: Optional[List[str]] = None, + theorems: Optional[List[str]] = None): + """ + Create a new theory with a subset of axioms, goals and theorems. + + :param axioms: Selection of axioms for the new theory + :param goals: Selection of goals for the new theory + :param theorems: Selection of theorems for the new theory + :return: New theory with the selected sentences + """ + if axioms is None: + axioms = self.axioms() + if goals is None: + goals = self.goals() + if theorems is None: + theorems = [] + + theory = getTheoryForSelection(axioms, goals, theorems, self._hs_theory) + + return Theory(theory, self.parent()) + + def translate(self, comorphism: Comorphism) -> Theory: + """ + Translate the theory using a comorphism. + + :param comorphism: Comorphism to use for the translation + :return: The translated theory + """ + translated_result = translateTheory(comorphism._hs_comorphism, self._hs_theory) + translatedHs = result_or_raise(translated_result, f"Translation with '{comorphism.name()}' failed") + translated = Theory(translatedHs, None) + + return translated + + def __str__(self) -> str: + return showTheory(self._hs_theory) diff --git a/python/api/src/hets/__init__.py b/python/api/src/hets/__init__.py new file mode 100644 index 0000000000..fcba204f3b --- /dev/null +++ b/python/api/src/hets/__init__.py @@ -0,0 +1,64 @@ +""" +Description : Reexports all modules of the API +Copyright : (c) Otto-von-Guericke University of Magdeburg +License : GPLv2 or higher, see LICENSE.txt +""" + +from .BasicProof import BasicProof +from .Comorphism import Comorphism +from .ConsistencyChecker import ConsistencyChecker +from .ConservativityChecker import ConservativityChecker +from .ConsistencyKind import ConsistencyKind +from .ConsistencyStatus import ConsistencyStatus +from .DevelopmentGraph import DevelopmentGraph +from .DevGraphEdge import DevGraphEdge, DefinitionDevGraphEdge, TheoremDevGraphEdge, EdgeKind +from .DevGraphNode import DevGraphNode, ReferenceDevGraphNode, LocalDevGraphNode +from .GlobalAnnotations import GlobalAnnotations +from .GMorphism import GMorphism +from .Library import Library, load_library +from .Logic import Logic +from .Options import Options, Option +from .ProofDetails import ProofDetails +from .ProofKind import ProofKind +from .ProofState import ProofState +from .Prover import Prover +from .RefinementTree import RefinementTree +from .RefinementTreeNode import RefinementTreeNode +from .RefinementTreeLink import RefinementTreeLink +from .Sentence import Sentence +from .Signature import Signature +from .Theory import Theory + +__all__ = [ + "BasicProof", + "Comorphism", + "ConsistencyChecker", + "ConservativityChecker", + "ConsistencyStatus", + "DevelopmentGraph", + "DevGraphEdge", + "DefinitionDevGraphEdge", + "TheoremDevGraphEdge", + "EdgeKind", + "DevGraphNode", + "ReferenceDevGraphNode", + "LocalDevGraphNode", + "GMorphism", + "GlobalAnnotations", + "Library", + "load_library", + "Logic", + "ProofState", + "ProofDetails", + "ProofKind", + "ConsistencyKind", + "Options", + "Option", + "Prover", + "RefinementTree", + "RefinementTreeNode", + "RefinementTreeLink", + "Sentence", + "Signature", + "Theory" +] diff --git a/python/api/src/hets/conversions.py b/python/api/src/hets/conversions.py new file mode 100644 index 0000000000..850c8581ad --- /dev/null +++ b/python/api/src/hets/conversions.py @@ -0,0 +1,24 @@ +from hets import ConsistencyKind +from hets.haskell import Conservativity, Inconsistent, Unknown, PCons, Cons, Mono, Def + + +def hs_conservativity_to_consistency_kind(hs_cons: Conservativity) -> ConsistencyKind: + """ + Converts a Haskell conservativity to a Python ConsistencyKind. + :param hs_cons: Haskell conservativity + :return: Python ConsistencyKind + """ + if isinstance(hs_cons, Inconsistent): + return ConsistencyKind.INCONSISTENT + elif isinstance(hs_cons, Unknown): + return ConsistencyKind.UNKNOWN + elif isinstance(hs_cons, PCons): + return ConsistencyKind.PROOF_THEORETICALLY_CONSERVATIVE + elif isinstance(hs_cons, Cons): + return ConsistencyKind.CONSERVATIVE + elif isinstance(hs_cons, Mono): + return ConsistencyKind.MONOMORPHIC + elif isinstance(hs_cons, Def): + return ConsistencyKind.DEFINITIONAL + else: + return ConsistencyKind.NONE diff --git a/python/hets/haskell/ByteString.py b/python/api/src/hets/haskell/ByteString.py similarity index 100% rename from python/hets/haskell/ByteString.py rename to python/api/src/hets/haskell/ByteString.py diff --git a/python/hets/haskell/ByteString.pyi b/python/api/src/hets/haskell/ByteString.pyi similarity index 100% rename from python/hets/haskell/ByteString.pyi rename to python/api/src/hets/haskell/ByteString.pyi diff --git a/python/api/src/hets/haskell/Internal.py b/python/api/src/hets/haskell/Internal.py new file mode 100644 index 0000000000..d375b48d89 --- /dev/null +++ b/python/api/src/hets/haskell/Internal.py @@ -0,0 +1,233 @@ +""" Auto generated python imports for haskell module HetsAPI.Internal""" + +from .base import * + +from hs.HetsAPI.Internal import ( + # type imports + LEdge, + LNode, + LibEnv, + + # class imports + DefinitionLink, + DevGraphLinkKind, + DevGraphLinkType, + LinkKindCofree, + LinkKindFree, + LinkKindGlobal, + LinkKindHiding, + LinkKindLocal, + TheoremLink, + CSConsistent, + CSError, + CSInconsistent, + CSTimeout, + CSUnchecked, + Cons, + ConsStatus, + Conservativity, + ConsistencyStatus, + DGLinkLab, + DGNodeLab, + DGNodeType, + DGraph, + Def, + Diagnosis, + Disproved, + ExtSign, + GlobalAnnos, + GoalStatus, + Gr, + HcOpt, + HetcatsOpts, + IRI, + Id, + Inconsistent, + LibName, + LiteralType, + Mono, + Open, + PCons, + ProofState, + ProofStatus, + Proved, + RTComp, + RTLink, + RTLinkLab, + RTLinkType, + RTNodeLab, + RTRefine, + Result, + SType, + TacticScript, + TimeOfDay, + Token, + Unknown, + + # function imports + associativityAnnotations, + consistencyStatusMessage, + consistencyStatusType, + developmentGraphEdgeLabelId, + developmentGraphEdgeLabelName, + developmentGraphNodeLabelName, + displayAnnos, + getDevGraphLinkType, + globalAnnotations, + isNodeReferenceNode, + linkTypeIsConservativ, + linkTypeIsHomogenoeous, + linkTypeIsInclusion, + linkTypeIsPending, + linkTypeIsProven, + linkTypeKind, + literalAnnos, + nodeTypeIsProven, + nodeTypeIsProvenConsistent, + nodeTypeIsReference, + optsWithAccessToken, + optsWithApplyAutomatic, + optsWithBlacklist, + optsWithComputeNormalForm, + optsWithConnectH, + optsWithConnectP, + optsWithCounterSparQ, + optsWithDatabaseConfigFile, + optsWithDatabaseDoMigrate, + optsWithDatabaseFileVersionId, + optsWithDatabaseOutputFile, + optsWithDatabaseReanalyze, + optsWithDatabaseSubConfigKey, + optsWithDefLogic, + optsWithDefSyntax, + optsWithDisableCertificateVerification, + optsWithDumpOpts, + optsWithFileType, + optsWithFullSign, + optsWithFullTheories, + optsWithHttpRequestHeaders, + optsWithInfiles, + optsWithInteractive, + optsWithLibdirs, + optsWithListen, + optsWithLossyTrans, + optsWithModelSparQ, + optsWithOutdir, + optsWithOutputLogicGraph, + optsWithOutputLogicList, + optsWithOutputToStdout, + optsWithPidFile, + optsWithPrintAST, + optsWithRecurse, + optsWithRunMMT, + optsWithServe, + optsWithSpecNames, + optsWithTransNames, + optsWithUncolored, + optsWithUnlit, + optsWithUrlCatalog, + optsWithUseLibPos, + optsWithVerbose, + optsWithViewNames, + optsWithWhitelist, + optsWithXmlFlag, + optsWithXupdate, + precedenceAnnotations, + prefixMap, + referencedNodeLibName, + tacticScriptContent, + accessToken, + analysis, + applyAutomatic, + blacklist, + caslAmalg, + computeNormalForm, + connectH, + connectP, + conservativityUnknownReason, + counterSparQ, + databaseConfig, + databaseConfigFile, + databaseContext, + databaseDoMigrate, + databaseFileVersionId, + databaseOutputFile, + databaseReanalyze, + databaseSubConfigKey, + defLogic, + defSyntax, + defaultHetcatsOpts, + diags, + disableCertificateVerification, + dumpOpts, + fileType, + fromJust, + fullSign, + fullTheories, + getConsOfStatus, + getEdgeConsStatus, + getFilePath, + getLibId, + getNodeConsStatus, + goalName, + goalStatus, + goalStatusOpenReason, + guiType, + httpRequestHeaders, + infiles, + interactive, + intype, + ioEncoding, + isInternalNode, + isProvenConsStatusLink, + labEdges, + labNodes, + libVersion, + libdirs, + linkStatus, + listen, + locIRI, + lossyTrans, + maybeResult, + mimeType, + modelSparQ, + nonImportedSymbols, + outdir, + outputLogicGraph, + outputLogicList, + outputToStdout, + outtypes, + pidFile, + plainSign, + printAST, + proofLines, + proofTree, + provenConservativity, + recurse, + requiredConservativity, + resultToMaybe, + rtl_type, + rtn_diag, + rtn_name, + rtn_type, + runMMT, + serve, + showConsistencyStatus, + showDoc, + showGlobalDoc, + specNames, + tacticScript, + transNames, + uncolored, + unlit, + urlCatalog, + useLibPos, + usedAxioms, + usedProver, + usedTime, + verbose, + viewNames, + whitelist, + xmlFlag, + xupdate, +) diff --git a/python/api/src/hets/haskell/Internal.pyi b/python/api/src/hets/haskell/Internal.pyi new file mode 100644 index 0000000000..dd61107694 --- /dev/null +++ b/python/api/src/hets/haskell/Internal.pyi @@ -0,0 +1,245 @@ +""" Auto generated python stubs for haskell module HetsAPI.Internal""" + +import typing + +from .custom import * +from .Prelude import * + +G_0 = typing.TypeVar("G_0") # sign +G_1 = typing.TypeVar("G_1") # symbol +G_2 = typing.TypeVar("G_2") # a +G_3 = typing.TypeVar("G_3") # b +G_4 = typing.TypeVar("G_4") # proof_tree +G_5 = typing.TypeVar("G_5") # gr + +LEdge = typing.Tuple[Node, Node, G_3] +LNode = typing.Tuple[Node, G_2] +LibEnv = Map[LibName,DGraph] + +class DefinitionLink(DevGraphLinkType): + def __init__(self, x0: DevGraphLinkKind, x1: bool, x2: bool): ... +class DevGraphLinkKind: ... +class DevGraphLinkType: ... +class LinkKindCofree(DevGraphLinkKind): ... +class LinkKindFree(DevGraphLinkKind): ... +class LinkKindGlobal(DevGraphLinkKind): ... +class LinkKindHiding(DevGraphLinkKind): ... +class LinkKindLocal(DevGraphLinkKind): ... +class TheoremLink(DevGraphLinkType): + def __init__(self, x0: DevGraphLinkKind, x1: bool, x2: bool, x3: bool, x4: bool, x5: bool): ... +class CSConsistent(SType): ... +class CSError(SType): ... +class CSInconsistent(SType): ... +class CSTimeout(SType): ... +class CSUnchecked(SType): ... +class Cons(Conservativity): ... +class ConsStatus: ... +class Conservativity: ... +class ConsistencyStatus: ... +class DGLinkLab: ... +class DGNodeLab: ... +class DGNodeType: ... +class DGraph: ... +class Def(Conservativity): ... +class Diagnosis: ... +class Disproved(GoalStatus): ... +class ExtSign(typing.Generic[G_0, G_1]): ... +class GlobalAnnos: ... +class GoalStatus: ... +class Gr(typing.Generic[G_2, G_3]): ... +class HcOpt(HetcatsOpts): + def __init__(self, x0: AnaType, x1: GuiType, x2: typing.List[typing.Tuple[str, str]], x3: typing.List[FilePath], x4: typing.List[SIMPLE_ID], x5: typing.List[SIMPLE_ID], x6: bool, x7: typing.List[SIMPLE_ID], x8: InType, x9: typing.List[FilePath], x10: FilePath, x11: int, x12: FilePath, x13: typing.List[OutType], x14: bool, x15: FilePath, x16: FilePath, x17: str, x18: str, x19: bool, x20: DBConfig, x21: DBContext, x22: FilePath, x23: bool, x24: int, x25: str, x26: str, x27: bool, x28: typing.List[CASLAmalgOpt], x29: bool, x30: int, x31: str, x32: bool, x33: bool, x34: bool, x35: bool, x36: typing.List[str], x37: bool, x38: Enc, x39: bool, x40: bool, x41: bool, x42: int, x43: FilePath, x44: typing.List[typing.List[str]], x45: typing.List[typing.List[str]], x46: bool, x47: bool, x48: bool, x49: bool, x50: bool, x51: str, x52: typing.List[str], x53: bool, x54: bool): ... +class HetcatsOpts: ... +class IRI: ... +class Id: ... +class Inconsistent(Conservativity): ... +class LibName: ... +class LiteralType: ... +class Mono(Conservativity): ... +class Open(GoalStatus): + def __init__(self, x0: Reason): ... +class PCons(Conservativity): ... +class ProofState: ... +class ProofStatus(typing.Generic[G_4]): ... +class Proved(GoalStatus): + def __init__(self, x0: bool): ... +class RTComp(RTLinkType): ... +class RTLink(RTLinkLab): + def __init__(self, x0: RTLinkType): ... +class RTLinkLab: ... +class RTLinkType: ... +class RTNodeLab: ... +class RTRefine(RTLinkType): ... +class Result(typing.Generic[G_2]): ... +class SType: ... +class TacticScript: ... +class TimeOfDay: ... +class Token: ... +class Unknown(Conservativity): + def __init__(self, x0: str): ... + +def associativityAnnotations(x0: GlobalAnnos) -> AssocMap: ... +def consistencyStatusMessage(x0: ConsistencyStatus) -> str: ... +def consistencyStatusType(x0: ConsistencyStatus) -> SType: ... +def developmentGraphEdgeLabelId(x0: DGLinkLab) -> int: ... +def developmentGraphEdgeLabelName(x0: DGLinkLab) -> str: ... +def developmentGraphNodeLabelName(x0: DGNodeLab) -> str: ... +def displayAnnos(x0: GlobalAnnos) -> DisplayMap: ... +def getDevGraphLinkType(x0: DGLinkLab) -> DevGraphLinkType: ... +def globalAnnotations(x0: DGraph) -> GlobalAnnos: ... +def isNodeReferenceNode(x0: DGNodeLab) -> bool: ... +def linkTypeIsConservativ(x0: DevGraphLinkType) -> bool: ... +def linkTypeIsHomogenoeous(x0: DevGraphLinkType) -> bool: ... +def linkTypeIsInclusion(x0: DevGraphLinkType) -> bool: ... +def linkTypeIsPending(x0: DevGraphLinkType) -> bool: ... +def linkTypeIsProven(x0: DevGraphLinkType) -> bool: ... +def linkTypeKind(x0: DevGraphLinkType) -> DevGraphLinkKind: ... +def literalAnnos(x0: GlobalAnnos) -> LiteralAnnos: ... +def nodeTypeIsProven(x0: DGNodeType) -> bool: ... +def nodeTypeIsProvenConsistent(x0: DGNodeType) -> bool: ... +def nodeTypeIsReference(x0: DGNodeType) -> bool: ... +def optsWithAccessToken(x0: HetcatsOpts, x1: str) -> HetcatsOpts: ... +def optsWithApplyAutomatic(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithBlacklist(x0: HetcatsOpts, x1: typing.List[typing.List[str]]) -> HetcatsOpts: ... +def optsWithComputeNormalForm(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithConnectH(x0: HetcatsOpts, x1: str) -> HetcatsOpts: ... +def optsWithConnectP(x0: HetcatsOpts, x1: int) -> HetcatsOpts: ... +def optsWithCounterSparQ(x0: HetcatsOpts, x1: int) -> HetcatsOpts: ... +def optsWithDatabaseConfigFile(x0: HetcatsOpts, x1: FilePath) -> HetcatsOpts: ... +def optsWithDatabaseDoMigrate(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithDatabaseFileVersionId(x0: HetcatsOpts, x1: str) -> HetcatsOpts: ... +def optsWithDatabaseOutputFile(x0: HetcatsOpts, x1: FilePath) -> HetcatsOpts: ... +def optsWithDatabaseReanalyze(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithDatabaseSubConfigKey(x0: HetcatsOpts, x1: str) -> HetcatsOpts: ... +def optsWithDefLogic(x0: HetcatsOpts, x1: str) -> HetcatsOpts: ... +def optsWithDefSyntax(x0: HetcatsOpts, x1: str) -> HetcatsOpts: ... +def optsWithDisableCertificateVerification(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithDumpOpts(x0: HetcatsOpts, x1: typing.List[str]) -> HetcatsOpts: ... +def optsWithFileType(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithFullSign(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithFullTheories(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithHttpRequestHeaders(x0: HetcatsOpts, x1: typing.List[str]) -> HetcatsOpts: ... +def optsWithInfiles(x0: HetcatsOpts, x1: typing.List[FilePath]) -> HetcatsOpts: ... +def optsWithInteractive(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithLibdirs(x0: HetcatsOpts, x1: typing.List[FilePath]) -> HetcatsOpts: ... +def optsWithListen(x0: HetcatsOpts, x1: int) -> HetcatsOpts: ... +def optsWithLossyTrans(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithModelSparQ(x0: HetcatsOpts, x1: FilePath) -> HetcatsOpts: ... +def optsWithOutdir(x0: HetcatsOpts, x1: FilePath) -> HetcatsOpts: ... +def optsWithOutputLogicGraph(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithOutputLogicList(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithOutputToStdout(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithPidFile(x0: HetcatsOpts, x1: FilePath) -> HetcatsOpts: ... +def optsWithPrintAST(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithRecurse(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithRunMMT(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithServe(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithSpecNames(x0: HetcatsOpts, x1: typing.List[SIMPLE_ID]) -> HetcatsOpts: ... +def optsWithTransNames(x0: HetcatsOpts, x1: typing.List[SIMPLE_ID]) -> HetcatsOpts: ... +def optsWithUncolored(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithUnlit(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithUrlCatalog(x0: HetcatsOpts, x1: typing.List[typing.Tuple[str, str]]) -> HetcatsOpts: ... +def optsWithUseLibPos(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithVerbose(x0: HetcatsOpts, x1: int) -> HetcatsOpts: ... +def optsWithViewNames(x0: HetcatsOpts, x1: typing.List[SIMPLE_ID]) -> HetcatsOpts: ... +def optsWithWhitelist(x0: HetcatsOpts, x1: typing.List[typing.List[str]]) -> HetcatsOpts: ... +def optsWithXmlFlag(x0: HetcatsOpts, x1: bool) -> HetcatsOpts: ... +def optsWithXupdate(x0: HetcatsOpts, x1: FilePath) -> HetcatsOpts: ... +def precedenceAnnotations(x0: GlobalAnnos) -> PrecedenceGraph: ... +def prefixMap(x0: GlobalAnnos) -> PrefixMap: ... +def referencedNodeLibName(x0: DGNodeLab) -> LibName: ... +def tacticScriptContent(x0: TacticScript) -> str: ... +def accessToken(x0: HetcatsOpts) -> str: ... +def analysis(x0: HetcatsOpts) -> AnaType: ... +def applyAutomatic(x0: HetcatsOpts) -> bool: ... +def blacklist(x0: HetcatsOpts) -> typing.List[typing.List[str]]: ... +def caslAmalg(x0: HetcatsOpts) -> typing.List[CASLAmalgOpt]: ... +def computeNormalForm(x0: HetcatsOpts) -> bool: ... +def connectH(x0: HetcatsOpts) -> str: ... +def connectP(x0: HetcatsOpts) -> int: ... +def conservativityUnknownReason(x0: Conservativity) -> str: ... +def counterSparQ(x0: HetcatsOpts) -> int: ... +def databaseConfig(x0: HetcatsOpts) -> DBConfig: ... +def databaseConfigFile(x0: HetcatsOpts) -> FilePath: ... +def databaseContext(x0: HetcatsOpts) -> DBContext: ... +def databaseDoMigrate(x0: HetcatsOpts) -> bool: ... +def databaseFileVersionId(x0: HetcatsOpts) -> str: ... +def databaseOutputFile(x0: HetcatsOpts) -> FilePath: ... +def databaseReanalyze(x0: HetcatsOpts) -> bool: ... +def databaseSubConfigKey(x0: HetcatsOpts) -> str: ... +def defLogic(x0: HetcatsOpts) -> str: ... +def defSyntax(x0: HetcatsOpts) -> str: ... +def defaultHetcatsOpts() -> HetcatsOpts: ... +def diags(x0: Result[G_2]) -> typing.List[Diagnosis]: ... +def disableCertificateVerification(x0: HetcatsOpts) -> bool: ... +def dumpOpts(x0: HetcatsOpts) -> typing.List[str]: ... +def fileType(x0: HetcatsOpts) -> bool: ... +def fromJust(x0: Maybe[G_2]) -> G_2: ... +def fullSign(x0: HetcatsOpts) -> bool: ... +def fullTheories(x0: HetcatsOpts) -> bool: ... +def getConsOfStatus(x0: ConsStatus) -> Conservativity: ... +def getEdgeConsStatus(x0: DGLinkLab) -> ConsStatus: ... +def getFilePath(x0: LibName) -> FilePath: ... +def getLibId(x0: LibName) -> IRI: ... +def getNodeConsStatus(x0: DGNodeLab) -> ConsStatus: ... +def goalName(x0: ProofStatus[G_4]) -> str: ... +def goalStatus(x0: ProofStatus[G_4]) -> GoalStatus: ... +def goalStatusOpenReason(x0: GoalStatus) -> Reason: ... +def guiType(x0: HetcatsOpts) -> GuiType: ... +def httpRequestHeaders(x0: HetcatsOpts) -> typing.List[str]: ... +def infiles(x0: HetcatsOpts) -> typing.List[FilePath]: ... +def interactive(x0: HetcatsOpts) -> bool: ... +def intype(x0: HetcatsOpts) -> InType: ... +def ioEncoding(x0: HetcatsOpts) -> Enc: ... +def isInternalNode(x0: DGNodeLab) -> bool: ... +def isProvenConsStatusLink(x0: ConsStatus) -> bool: ... +def labEdges(x0: G_5[G_2,G_3]) -> typing.List[LEdge[G_3]]: ... +def labNodes(x0: G_5[G_2,G_3]) -> typing.List[LNode[G_2]]: ... +def libVersion(x0: LibName) -> Maybe[VersionNumber]: ... +def libdirs(x0: HetcatsOpts) -> typing.List[FilePath]: ... +def linkStatus(x0: ConsStatus) -> ThmLinkStatus: ... +def listen(x0: HetcatsOpts) -> int: ... +def locIRI(x0: LibName) -> Maybe[IRI]: ... +def lossyTrans(x0: HetcatsOpts) -> bool: ... +def maybeResult(x0: Result[G_2]) -> Maybe[G_2]: ... +def mimeType(x0: LibName) -> Maybe[str]: ... +def modelSparQ(x0: HetcatsOpts) -> FilePath: ... +def nonImportedSymbols(x0: ExtSign[G_0,G_1]) -> Set[G_1]: ... +def outdir(x0: HetcatsOpts) -> FilePath: ... +def outputLogicGraph(x0: HetcatsOpts) -> bool: ... +def outputLogicList(x0: HetcatsOpts) -> bool: ... +def outputToStdout(x0: HetcatsOpts) -> bool: ... +def outtypes(x0: HetcatsOpts) -> typing.List[OutType]: ... +def pidFile(x0: HetcatsOpts) -> FilePath: ... +def plainSign(x0: ExtSign[G_0,G_1]) -> G_0: ... +def printAST(x0: HetcatsOpts) -> bool: ... +def proofLines(x0: ProofStatus[G_4]) -> typing.List[str]: ... +def proofTree(x0: ProofStatus[G_4]) -> G_4: ... +def provenConservativity(x0: ConsStatus) -> Conservativity: ... +def recurse(x0: HetcatsOpts) -> bool: ... +def requiredConservativity(x0: ConsStatus) -> Conservativity: ... +def resultToMaybe(x0: Result[G_2]) -> Maybe[G_2]: ... +def rtl_type(x0: RTLinkLab) -> RTLinkType: ... +def rtn_diag(x0: RTNodeLab) -> str: ... +def rtn_name(x0: RTNodeLab) -> str: ... +def rtn_type(x0: RTNodeLab) -> RTNodeType: ... +def runMMT(x0: HetcatsOpts) -> bool: ... +def serve(x0: HetcatsOpts) -> bool: ... +def showConsistencyStatus(x0: Conservativity) -> str: ... +def showDoc(x0: G_2) -> ShowS: ... +def showGlobalDoc(x0: GlobalAnnos, x1: G_2) -> ShowS: ... +def specNames(x0: HetcatsOpts) -> typing.List[SIMPLE_ID]: ... +def tacticScript(x0: ProofStatus[G_4]) -> TacticScript: ... +def transNames(x0: HetcatsOpts) -> typing.List[SIMPLE_ID]: ... +def uncolored(x0: HetcatsOpts) -> bool: ... +def unlit(x0: HetcatsOpts) -> bool: ... +def urlCatalog(x0: HetcatsOpts) -> typing.List[typing.Tuple[str, str]]: ... +def useLibPos(x0: HetcatsOpts) -> bool: ... +def usedAxioms(x0: ProofStatus[G_4]) -> typing.List[str]: ... +def usedProver(x0: ProofStatus[G_4]) -> str: ... +def usedTime(x0: ProofStatus[G_4]) -> TimeOfDay: ... +def verbose(x0: HetcatsOpts) -> int: ... +def viewNames(x0: HetcatsOpts) -> typing.List[SIMPLE_ID]: ... +def whitelist(x0: HetcatsOpts) -> typing.List[typing.List[str]]: ... +def xmlFlag(x0: HetcatsOpts) -> bool: ... +def xupdate(x0: HetcatsOpts) -> FilePath: ... diff --git a/python/hets/haskell/OMap.py b/python/api/src/hets/haskell/OMap.py similarity index 100% rename from python/hets/haskell/OMap.py rename to python/api/src/hets/haskell/OMap.py diff --git a/python/hets/haskell/OMap.pyi b/python/api/src/hets/haskell/OMap.pyi similarity index 100% rename from python/hets/haskell/OMap.pyi rename to python/api/src/hets/haskell/OMap.pyi diff --git a/python/api/src/hets/haskell/Prelude.py b/python/api/src/hets/haskell/Prelude.py new file mode 100644 index 0000000000..50d0ab2939 --- /dev/null +++ b/python/api/src/hets/haskell/Prelude.py @@ -0,0 +1,2 @@ +from .base import * +from hs.Prelude import Just, Nothing, fst, snd, show, String, Maybe diff --git a/python/hets/haskell/Prelude.pyi b/python/api/src/hets/haskell/Prelude.pyi similarity index 100% rename from python/hets/haskell/Prelude.pyi rename to python/api/src/hets/haskell/Prelude.pyi diff --git a/python/api/src/hets/haskell/Python.py b/python/api/src/hets/haskell/Python.py new file mode 100644 index 0000000000..1a72c6aaba --- /dev/null +++ b/python/api/src/hets/haskell/Python.py @@ -0,0 +1,138 @@ +""" Auto generated python imports for haskell module HetsAPI.Python""" + +from .base import * + +from hs.HetsAPI.Python import ( + # type imports + PyTheorySentence, + PyTheorySentenceByName, + LinkPointer, + RefinementTreeLink, + Sentence, + SignatureJSON, + SymbolJSON, + TheoryPointer, + TheorySentence, + TheorySentenceByName, + + # class imports + PyBasicProof, + PyBasicProofConjectured, + PyBasicProofGuessed, + PyBasicProofHandwritten, + PyComorphism, + PyConsChecker, + PyConsCheckingOptions, + PyConservativityChecker, + PyGMorphism, + PyProofOptions, + PyProofTree, + PyProver, + PyTheory, + RefinementTreeNode, + + # function imports + checkConservativityEdge, + checkConservativityEdgeAndRecord, + checkConsistency, + checkConsistencyAndRecord, + codomainOfGMorphism, + comorphismDescriptionOfGMorphism, + comorphismName, + comorphismNameOfGMorphism, + comorphismOfGMorphism, + consCheckerName, + consOptsComorphism, + consOptsConsChecker, + consOptsIncludeTheorems, + consOptsTimeout, + conservativityCheckerName, + conservativityCheckerUsable, + defaultConsCheckingOptions, + defaultProofOptions, + domainOfGMorphism, + fstOf3, + gMorphismToTransportType, + getAllAxioms, + getAllGoals, + getAllSentences, + getAvailableComorphisms, + getDGNodeById, + getProvenGoals, + getTheoryForSelection, + getUnprovenGoals, + getUsableConservativityCheckers, + getUsableConsistencyCheckers, + getUsableProvers, + globalTheory, + gmorphismOfEdge, + isGMorphismInclusion, + logicDescriptionOfTheory, + logicNameOfTheory, + mkPyProofOptions, + prettySentence, + prettyTheory, + proofOptsAxiomsToInclude, + proofOptsComorphism, + proofOptsGoalsToProve, + proofOptsProver, + proofOptsTimeout, + proofOptsUseTheorems, + proveNode, + proveNodeAndRecord, + proverName, + pyProofStatusOfPyBasicProof, + recordProofResult, + signatureOfGMorphism, + signatureOfTheory, + sndOf3, + sourceLogicDescriptionName, + sourceLogicName, + sublogicOfPyTheory, + targetLogicDescriptionName, + targetLogicName, + thd, + theoryOfNode, + theorySentenceBestProof, + translateTheory, + automatic, + automaticHideTheoremShift, + compositionProveEdges, + computeColimit, + conservativity, + freeness, + getAvailableSpecificationsForRefinement, + getDevelopmentGraphNodeType, + getEdgesFromDevelopmentGraph, + getGraphForLibrary, + getLEdgesFromDevelopmentGraph, + getLNodesFromDevelopmentGraph, + getLibraryDependencies, + getNodesFromDevelopmentGraph, + getRefinementTree, + globalDecomposition, + globalSubsume, + isRootNode, + libFlatDUnions, + libFlatHeterogen, + libFlatHiding, + libFlatImports, + libFlatRenamings, + loadLibrary, + localDecomposition, + localInference, + normalForm, + qualifyLibEnv, + recomputeNode, + recordConservativityResult, + rtNodeLab, + showTheory, + theoremHideShift, + theorySentenceContent, + theorySentenceGetTheoremStatus, + theorySentenceIsAxiom, + theorySentenceIsDefined, + theorySentencePriority, + theorySentenceWasTheorem, + triangleCons, +) diff --git a/python/api/src/hets/haskell/Python.pyi b/python/api/src/hets/haskell/Python.pyi new file mode 100644 index 0000000000..1404a09f94 --- /dev/null +++ b/python/api/src/hets/haskell/Python.pyi @@ -0,0 +1,143 @@ +""" Auto generated python stubs for haskell module HetsAPI.Python""" + +import typing + +from .custom import * +from .Prelude import * +from .Internal import * +from .OMap import * + +G_0 = typing.TypeVar("G_0") # a +G_1 = typing.TypeVar("G_1") # b +G_2 = typing.TypeVar("G_2") # c +G_3 = typing.TypeVar("G_3") # tStatus + +PyTheorySentence = SenAttr[Sentence,ThmStatus[typing.Tuple[PyComorphism, PyBasicProof]]] +PyTheorySentenceByName = OMap[str,PyTheorySentence] +LinkPointer = typing.Tuple[LibName, LibEnv, LEdge[DGLinkLab]] +RefinementTreeLink = RTLinkLab +Sentence = GenericTransportType +SignatureJSON = GenericTransportType +SymbolJSON = GenericTransportType +TheoryPointer = typing.Tuple[LibName, LibEnv, DGraph, LNode[DGNodeLab]] +TheorySentence = SenAttr[Sentence,ThmStatus[typing.Tuple[AnyComorphism, BasicProof]]] +TheorySentenceByName = OMap[str,TheorySentence] + +class PyBasicProof: ... +class PyBasicProofConjectured(PyBasicProof): ... +class PyBasicProofGuessed(PyBasicProof): ... +class PyBasicProofHandwritten(PyBasicProof): ... +class PyComorphism: ... +class PyConsChecker: ... +class PyConsCheckingOptions: ... +class PyConservativityChecker: ... +class PyGMorphism: ... +class PyProofOptions: ... +class PyProofTree: ... +class PyProver: ... +class PyTheory: ... +class RefinementTreeNode: ... + +def checkConservativityEdge(x0: PyConservativityChecker, x1: LinkPointer) -> IO[Result[typing.Tuple[Conservativity, PyTheory, PyTheory]]]: ... +def checkConservativityEdgeAndRecord(x0: PyConservativityChecker, x1: LinkPointer) -> IO[Result[typing.Tuple[typing.Tuple[Conservativity, PyTheory, PyTheory], LibEnv]]]: ... +def checkConsistency(x0: TheoryPointer, x1: PyConsCheckingOptions) -> IO[ConsistencyStatus]: ... +def checkConsistencyAndRecord(x0: TheoryPointer, x1: PyConsCheckingOptions) -> IO[typing.Tuple[ConsistencyStatus, LibEnv]]: ... +def codomainOfGMorphism(x0: PyGMorphism) -> GenericTransportType: ... +def comorphismDescriptionOfGMorphism(x0: PyGMorphism) -> str: ... +def comorphismName(x0: PyComorphism) -> str: ... +def comorphismNameOfGMorphism(x0: PyGMorphism) -> str: ... +def comorphismOfGMorphism(x0: PyGMorphism) -> PyComorphism: ... +def consCheckerName(x0: PyConsChecker) -> str: ... +def consOptsComorphism(x0: PyConsCheckingOptions) -> Maybe[PyComorphism]: ... +def consOptsConsChecker(x0: PyConsCheckingOptions) -> Maybe[PyConsChecker]: ... +def consOptsIncludeTheorems(x0: PyConsCheckingOptions) -> bool: ... +def consOptsTimeout(x0: PyConsCheckingOptions) -> int: ... +def conservativityCheckerName(x0: PyConservativityChecker) -> str: ... +def conservativityCheckerUsable(x0: PyConservativityChecker) -> IO[Maybe[str]]: ... +def defaultConsCheckingOptions() -> PyConsCheckingOptions: ... +def defaultProofOptions() -> PyProofOptions: ... +def domainOfGMorphism(x0: PyGMorphism) -> GenericTransportType: ... +def fstOf3(x0: typing.Tuple[G_0, G_1, G_2]) -> G_0: ... +def gMorphismToTransportType(x0: PyGMorphism) -> GenericTransportType: ... +def getAllAxioms(x0: PyTheory) -> PyTheorySentenceByName: ... +def getAllGoals(x0: PyTheory) -> PyTheorySentenceByName: ... +def getAllSentences(x0: PyTheory) -> PyTheorySentenceByName: ... +def getAvailableComorphisms(x0: PyTheory) -> typing.List[PyComorphism]: ... +def getDGNodeById(x0: DGraph, x1: int) -> Maybe[DGNodeLab]: ... +def getProvenGoals(x0: PyTheory) -> PyTheorySentenceByName: ... +def getTheoryForSelection(x0: typing.List[str], x1: typing.List[str], x2: typing.List[str], x3: PyTheory) -> PyTheory: ... +def getUnprovenGoals(x0: PyTheory) -> PyTheorySentenceByName: ... +def getUsableConservativityCheckers(x0: LEdge[DGLinkLab], x1: LibEnv, x2: LibName) -> IO[typing.List[PyConservativityChecker]]: ... +def getUsableConsistencyCheckers(x0: PyTheory) -> IO[typing.List[typing.Tuple[PyConsChecker, PyComorphism]]]: ... +def getUsableProvers(x0: PyTheory) -> IO[typing.List[typing.Tuple[PyProver, PyComorphism]]]: ... +def globalTheory(x0: DGNodeLab) -> Maybe[PyTheory]: ... +def gmorphismOfEdge(x0: DGLinkLab) -> PyGMorphism: ... +def isGMorphismInclusion(x0: PyGMorphism) -> bool: ... +def logicDescriptionOfTheory(x0: PyTheory) -> str: ... +def logicNameOfTheory(x0: PyTheory) -> str: ... +def mkPyProofOptions(x0: Maybe[PyProver], x1: Maybe[PyComorphism], x2: bool, x3: typing.List[str], x4: typing.List[str], x5: int) -> PyProofOptions: ... +def prettySentence(x0: PyTheory, x1: Sentence) -> str: ... +def prettyTheory(x0: PyTheory) -> str: ... +def proofOptsAxiomsToInclude(x0: PyProofOptions) -> typing.List[str]: ... +def proofOptsComorphism(x0: PyProofOptions) -> Maybe[PyComorphism]: ... +def proofOptsGoalsToProve(x0: PyProofOptions) -> typing.List[str]: ... +def proofOptsProver(x0: PyProofOptions) -> Maybe[PyProver]: ... +def proofOptsTimeout(x0: PyProofOptions) -> int: ... +def proofOptsUseTheorems(x0: PyProofOptions) -> bool: ... +def proveNode(x0: TheoryPointer, x1: PyProofOptions) -> IO[Result[typing.Tuple[PyTheory, typing.List[ProofStatus[PyProofTree]]]]]: ... +def proveNodeAndRecord(x0: TheoryPointer, x1: PyProofOptions) -> IO[Result[typing.Tuple[typing.Tuple[PyTheory, typing.List[ProofStatus[PyProofTree]]], LibEnv]]]: ... +def proverName(x0: PyProver) -> str: ... +def pyProofStatusOfPyBasicProof(x0: PyBasicProof) -> Maybe[ProofStatus[PyProofTree]]: ... +def recordProofResult(x0: TheoryPointer, x1: typing.Tuple[PyTheory, typing.List[ProofStatus[PyProofTree]]]) -> LibEnv: ... +def signatureOfGMorphism(x0: PyGMorphism) -> ExtSign[SignatureJSON,SymbolJSON]: ... +def signatureOfTheory(x0: PyTheory) -> ExtSign[SignatureJSON,SymbolJSON]: ... +def sndOf3(x0: typing.Tuple[G_0, G_1, G_2]) -> G_1: ... +def sourceLogicDescriptionName(x0: PyComorphism) -> str: ... +def sourceLogicName(x0: PyComorphism) -> str: ... +def sublogicOfPyTheory(x0: PyTheory) -> str: ... +def targetLogicDescriptionName(x0: PyComorphism) -> str: ... +def targetLogicName(x0: PyComorphism) -> str: ... +def thd(x0: typing.Tuple[G_0, G_1, G_2]) -> G_2: ... +def theoryOfNode(x0: DGNodeLab) -> PyTheory: ... +def theorySentenceBestProof(x0: PyTheorySentence) -> Maybe[PyBasicProof]: ... +def translateTheory(x0: PyComorphism, x1: PyTheory) -> Result[PyTheory]: ... +def automatic(x0: LibName, x1: LibEnv) -> LibEnv: ... +def automaticHideTheoremShift(x0: LibName, x1: LibEnv) -> LibEnv: ... +def compositionProveEdges(x0: LibName, x1: LibEnv) -> LibEnv: ... +def computeColimit(x0: LibName, x1: LibEnv) -> Result[LibEnv]: ... +def conservativity(x0: LibName, x1: LibEnv) -> LibEnv: ... +def freeness(x0: LibName, x1: LibEnv) -> Result[LibEnv]: ... +def getAvailableSpecificationsForRefinement(x0: DGraph) -> typing.List[str]: ... +def getDevelopmentGraphNodeType(x0: DGNodeLab) -> DGNodeType: ... +def getEdgesFromDevelopmentGraph(x0: DGraph) -> typing.List[DGLinkLab]: ... +def getGraphForLibrary(x0: LibName, x1: LibEnv) -> DGraph: ... +def getLEdgesFromDevelopmentGraph(x0: DGraph) -> typing.List[LEdge[DGLinkLab]]: ... +def getLNodesFromDevelopmentGraph(x0: DGraph) -> typing.List[LNode[DGNodeLab]]: ... +def getLibraryDependencies(x0: LibEnv) -> typing.List[typing.Tuple[LibName, LibName]]: ... +def getNodesFromDevelopmentGraph(x0: DGraph) -> typing.List[DGNodeLab]: ... +def getRefinementTree(x0: str, x1: DGraph) -> Maybe[Gr[RefinementTreeNode,RefinementTreeLink]]: ... +def globalDecomposition(x0: LibName, x1: LibEnv) -> LibEnv: ... +def globalSubsume(x0: LibName, x1: LibEnv) -> LibEnv: ... +def isRootNode(x0: RefinementTreeNode) -> bool: ... +def libFlatDUnions(x0: LibName, x1: LibEnv) -> Result[LibEnv]: ... +def libFlatHeterogen(x0: LibName, x1: LibEnv) -> Result[LibEnv]: ... +def libFlatHiding(x0: LibName, x1: LibEnv) -> Result[LibEnv]: ... +def libFlatImports(x0: LibName, x1: LibEnv) -> Result[LibEnv]: ... +def libFlatRenamings(x0: LibName, x1: LibEnv) -> Result[LibEnv]: ... +def loadLibrary(x0: FilePath, x1: HetcatsOpts) -> IO[Result[typing.Tuple[LibName, LibEnv]]]: ... +def localDecomposition(x0: LibName, x1: LibEnv) -> LibEnv: ... +def localInference(x0: LibName, x1: LibEnv) -> LibEnv: ... +def normalForm(x0: LibName, x1: LibEnv) -> Result[LibEnv]: ... +def qualifyLibEnv(x0: LibName, x1: LibEnv) -> Result[LibEnv]: ... +def recomputeNode(x0: TheoryPointer) -> LibEnv: ... +def recordConservativityResult(x0: LinkPointer, x1: ConservativityResult) -> LibEnv: ... +def rtNodeLab(x0: RefinementTreeNode) -> RTNodeLab: ... +def showTheory(x0: G_theory) -> str: ... +def theoremHideShift(x0: LibName, x1: LibEnv) -> Result[LibEnv]: ... +def theorySentenceContent(x0: SenAttr[G_0,ThmStatus[G_3]]) -> G_0: ... +def theorySentenceGetTheoremStatus(x0: SenAttr[G_0,ThmStatus[G_3]]) -> typing.List[G_3]: ... +def theorySentenceIsAxiom(x0: SenAttr[G_0,ThmStatus[G_3]]) -> bool: ... +def theorySentenceIsDefined(x0: SenAttr[G_0,ThmStatus[G_3]]) -> bool: ... +def theorySentencePriority(x0: SenAttr[G_0,ThmStatus[G_3]]) -> Maybe[str]: ... +def theorySentenceWasTheorem(x0: SenAttr[G_0,ThmStatus[G_3]]) -> bool: ... +def triangleCons(x0: LibName, x1: LibEnv) -> Result[LibEnv]: ... diff --git a/python/api/src/hets/haskell/__init__.py b/python/api/src/hets/haskell/__init__.py new file mode 100644 index 0000000000..50c655814b --- /dev/null +++ b/python/api/src/hets/haskell/__init__.py @@ -0,0 +1,26 @@ +import logging as _logging +_logger = _logging.getLogger(__name__) + +_logger.debug("Loading hyphen") +from .base import * +_logger.debug("Loading hyphen done") + +_logger.debug("Loading ByteString") +from .ByteString import * +_logger.debug("Loading ByteString done") + +_logger.debug("Loading Internal") +from .Internal import * +_logger.debug("Loading Internal done") + +_logger.debug("Loading OMap") +from .OMap import * +_logger.debug("Loading OMap done") + +_logger.debug("Loading Prelude") +from .Prelude import * +_logger.debug("Loading Prelude done") + +_logger.debug("Loading Python") +from .Python import * +_logger.debug("Loading Python done") diff --git a/python/hets/haskell/__init__.pyi b/python/api/src/hets/haskell/__init__.pyi similarity index 100% rename from python/hets/haskell/__init__.pyi rename to python/api/src/hets/haskell/__init__.pyi diff --git a/python/api/src/hets/haskell/base.py b/python/api/src/hets/haskell/base.py new file mode 100644 index 0000000000..72f9d16eaa --- /dev/null +++ b/python/api/src/hets/haskell/base.py @@ -0,0 +1,6 @@ +import hyphen + +if hyphen.importing and hyphen.importing.FORCED_EMPTY and hyphen.importing.EXPECTED_EMPTY: + # Some moules in a module hierarchy do not exist in haskell. E.g. the module `Driver.Options` in python implies the existence of a module `Driver` which does not have to exist in python. Hence, these modules need to be marked explicitly empty + hyphen.importing.FORCED_EMPTY += ["Driver", "Common", "Static", "Logic", "Proofs", "HetsAPI"] + hyphen.importing.EXPECTED_EMPTY += ["Driver", "Common", "Static", "Logic", "Proofs", "HetsAPI"] diff --git a/python/api/src/hets/haskell/custom.pyi b/python/api/src/hets/haskell/custom.pyi new file mode 100644 index 0000000000..c7890f0d70 --- /dev/null +++ b/python/api/src/hets/haskell/custom.pyi @@ -0,0 +1,47 @@ +import typing + +A = typing.TypeVar("A") +B = typing.TypeVar("B") + +Map = typing.Dict[A, B] + + +AnaType = typing.Any +GuiType = typing.Any +FilePath = str +SIMPLE_ID = typing.Any +InType = typing.Any +OutType = typing.Any +DBConfig = typing.Any +DBContext = typing.Any +CASLAmalgOpt = typing.Any +Enc = typing.Any +Reason = str + +PrecedenceGraph = typing.Any +AssocMap = typing.Dict +DisplayMap = typing.Dict +LiteralAnnos = typing.Dict +PrefixMap = typing.Dict + +ShowS = typing.Callable[[str], str] + +VersionNumber = typing.Any + +Set = typing.Set + +SenAttr = typing.Any +ThmStatus = typing.Any +ThmLinkStatus = typing.Any + +GenericTransportType = typing.Any +LNode = typing.Any +LEdge = typing.Any +AnyComorphism = typing.Any +BasicProof = typing.Any + +G_theory = typing.Any + +ProofHistory = typing.Any + +Node = int diff --git a/python/hets/json_conversion.py b/python/api/src/hets/json_conversion.py similarity index 76% rename from python/hets/json_conversion.py rename to python/api/src/hets/json_conversion.py index a4a33fed70..6c8af6276f 100644 --- a/python/hets/json_conversion.py +++ b/python/api/src/hets/json_conversion.py @@ -1,9 +1,7 @@ -import ast +import json from typing import Union -from .haskell import show, ByteString, toStrict - -import json +from .haskell import ByteString, toStrict def as_json(hs_byte_string: Union[ByteString, bytes]) -> dict: @@ -16,8 +14,8 @@ def as_json(hs_byte_string: Union[ByteString, bytes]) -> dict: The JSON representation is generated automatically by the haskell package Data.Aeson. See https://hackage.haskell.org/package/aeson/docs/Data-Aeson.html for further details. - @param hs_byte_string A haskell `Data.ByteString.ByteString` or `Data.ByteString.Lazy.ByteString` - @return The byte string parsed as json object to a dictionary + :param: hs_byte_string A haskell `Data.ByteString.ByteString` or `Data.ByteString.Lazy.ByteString` + :return: The byte string parsed as json object to a dictionary """ if isinstance(hs_byte_string, bytes): diff --git a/python/api/src/hets/maybe.py b/python/api/src/hets/maybe.py new file mode 100644 index 0000000000..0e4d6d59ae --- /dev/null +++ b/python/api/src/hets/maybe.py @@ -0,0 +1,17 @@ +import typing + +from .haskell import fromJust, Maybe, Just + +T = typing.TypeVar("T") + + +def maybe_to_optional(maybe: typing.Generic[T]) -> typing.Optional[T]: + """ + Converts a Haskell Maybe object to an Optional. + :param maybe: Haskell Maybe object + :return: Value if Just, None if Nothing + """ + if isinstance(maybe, Just): + return fromJust(maybe) + + return None diff --git a/python/api/src/hets/result.py b/python/api/src/hets/result.py new file mode 100644 index 0000000000..4a72075524 --- /dev/null +++ b/python/api/src/hets/result.py @@ -0,0 +1,41 @@ +""" +Description : Provides utility functions to interact with `Common.Result.Result` instances +Copyright : (c) Otto-von-Guericke University of Magdeburg +License : GPLv2 or higher, see LICENSE.txt +""" + +from typing import Any, TypeVar, Generic, Optional, List +from .haskell import resultToMaybe, fromJust, Just, show, Result, Diagnosis, diags +from .maybe import maybe_to_optional + +T = TypeVar("T") + + +class ResultException(BaseException): + """ + Exception raised when trying to extract a value from a result but the result contained an error. + """ + pass + + +def result_to_optional(result: Generic[T]) -> Optional[T]: + """ + Converts a Haskell Result object to an Optional Python object. + :param result: Haskell Result object + :return: Value of the result or None if the result contains an error + """ + return maybe_to_optional(resultToMaybe(result)) + + +def result_or_raise(result: Generic[T], msg: str = "An error occurred") -> T: + """ + Extracts the value from a result or raises a `ResultException` if the result contains an error. + :param result: Haskell Result object + :param msg: Message to include in the exception + :return: + """ + result_maybe = resultToMaybe(result) + if isinstance(result_maybe, Just): + return fromJust(result_maybe) + else: + raise ResultException(f"{msg}: {show(diags(result))}") diff --git a/python/gen_interfaces.json b/python/gen_interfaces.json new file mode 100644 index 0000000000..07517d2bcb --- /dev/null +++ b/python/gen_interfaces.json @@ -0,0 +1,21 @@ +[ + { + "name": "HetsAPI.Python", + "imports": [ + ".custom", + ".Prelude", + ".Internal", + ".OMap" + ], + "ignore": [ + "genericProveBatch" + ] + }, + { + "name": "HetsAPI.Internal", + "imports": [ + ".custom", + ".Prelude" + ] + } +] \ No newline at end of file diff --git a/python/gen_interfaces.py b/python/gen_interfaces.py new file mode 100644 index 0000000000..6ec0850eb6 --- /dev/null +++ b/python/gen_interfaces.py @@ -0,0 +1,413 @@ +import argparse +from io import BufferedReader, BufferedWriter +import keyword +import os +import subprocess +import re +import sys +import typing + + +class GHCI(object): + def __init__(self) -> None: + self._ghci = None + + def __enter__(self): + self._ghci = subprocess.Popen(["make", "ghci"], stdout=subprocess.PIPE, + stdin=subprocess.PIPE, stderr=subprocess.PIPE) + + # Consume initial output + out = "" + while not out.endswith("Prelude> "): + buf = self._out().peek() + self._out().read(len(buf)) + out += buf.decode("utf8") + + return self + + def __exit__(self, *args): + self.session().terminate() + self.session().wait() + self._ghci = None + + def session(self) -> subprocess.Popen: + assert self._ghci + return self._ghci + + def _in(self) -> BufferedWriter: + return self.session().stdin + + def _out(self) -> BufferedReader: + return self.session().stdout + + def command(self, command: str) -> str: + self._in().write(f"{command}\n".encode("utf8")) + self._in().flush() + + out = "" + while not re.search(r"^[a-zA-Z*_. ]+> $", out, re.MULTILINE): + buf = self._out().peek() + self._out().read(len(buf)) + out += buf.decode() + + # out = buf.decode("utf8") + + out = out[:out.rfind("\n")] + return out + + +RE_TYPE = re.compile(r"^type (?:\w+\.)*(\w+) (\w+ )*= (.*)$") +RE_CONSTRUCTOR = re.compile( + r"^(?:[A-Z]\w+\.)*([A-Z]\w+) :: (?:.* => )?((?:.*? -> )*.*?)$") +RE_FUNCTION = re.compile( + r"^(?:[A-Z]\w+\.)*([a-z]\w+) :: (?:.* => )?((?:.*? -> )*.*?)$") +RE_CLASS = re.compile(r"^(?:data|newtype) (\w+)\s+((?:\w+ )*)= ...$") +RE_NEW_TYPE = re.compile(r"^ (\w+)\s+((?:\w+ )*)= ...$") + +HS_TO_PY_TYPE_MAP = {"Bool": "bool", "Int": "int", "String": "str"} + + +class PythonStub: + def __init__(self, hs_module_name: str): + self._registry = [ + (self._add_class, RE_CLASS), + (self._add_type_alias, RE_TYPE), + (self._add_function, RE_FUNCTION), + (self._add_subclass, RE_CONSTRUCTOR) + ] + + self._classes = {} + self._types = {} + self._functions = {} + self._generics = {} + self._generic_id = 0 + self._imports = [] + self._members_to_ignore = [] + + self._hs_module_name = hs_module_name + + def add_haskell_declaration(self, line: str) -> None: + found = False + for fn, regex in self._registry: + if regex.match(line): + fn(line) + found = True + + if not found: + print(f"[E] Unknown pattern: {line}") + + def add_import(self, module: str) -> None: + self._imports += [module] + + def ignore_members(self, members_to_ignore: typing.List[str]): + self._members_to_ignore = members_to_ignore + + def to_pyi(self) -> str: + return "\n".join([ + f'""" Auto generated python stubs for haskell module {self._hs_module_name or ""}"""', + "", + "import typing", + "", + *[f'from {module} import *' for module in self._imports], + "", + *[f'{n} = typing.TypeVar("{n}") # {orig}' for orig, + n in self._generics.items()], + "", + *self._types.values(), + "", + *self._classes.values(), + "", + *self._functions.values(), + "" + ]) + + def to_py(self) -> str: + return "\n".join([ + f'""" Auto generated python imports for haskell module {self._hs_module_name}"""', + "", + "from .base import *", + "", + f"from hs.{self._hs_module_name} import (", + " # type imports", + *[f" {n}," for n in self._types.keys()], + "", + " # class imports", + *[f" {n}," for n in self._classes.keys()], + "", + " # function imports", + *[f" {n}," for n in self._functions.keys()], + ")", + "" + ]) + + def _member_ok(self, name: str, context: str) -> bool: + if keyword.iskeyword(name): + print( + f"[E] {context} name is a reservered keyword in python '{name}'") + return False + + if name in self._members_to_ignore: + return False + + return True + + def _generic_var(self, orig: str | None = None) -> str: + if orig in self._generics: + return self._generics[orig] + + name = f"G_{self._generic_id}" + + if orig is None: + orig = name.lower() + + self._generics[orig] = name + self._generic_id += 1 + return name + + def _parse_parens(self, term: str, delimiter: str) -> typing.List[str]: + parts = [] + parens = 0 + part = "" + i = 0 + while i < len(term): + c = term[i] + if c == '(': + parens += 1 + elif c == ')': + parens -= 1 + + if i < len(term) - len(delimiter) and term[i:i+len(delimiter)] == delimiter and parens == 0: + parts += [part.strip()] + part = "" + i += len(delimiter) + continue + else: + part += c + + i += 1 + + parts += [part.strip()] + return parts + + def _parse_tuple(self, term: str) -> typing.List[str] | None: + if len(term) < 2 or term[0] != "(" or term[-1] != ")": + return None + + return self._parse_parens(term[1:-1], ",") + + def _parse_function(self, term: str) -> typing.List[str]: + return self._parse_parens(term, "->") + + def _add_class(self, line: str): + match = RE_CLASS.match(line) + + class_name = match.group(1) + params = match.group(2) + + if not self._member_ok(class_name, "class"): + return + + if params is not None and params != "": + params = params.strip().split(" ") + + gen_params = [self._generic_var(p.strip()) for p in params] + + self._classes[class_name] = f"class {class_name}(typing.Generic[{', '.join(gen_params)}]): ..." + else: + self._classes[class_name] = f"class {class_name}: ..." + + return self._classes[class_name] + + def _add_subclass(self, line: str): + def sub_fn(match: re.Match[str]) -> str: + groups = match.groups() + out = f"class {groups[0]}" + + params = self._parse_function(groups[1]) + + assert params is not None + + params = [self._to_type(p) for p in params] + + if params[-1] != groups[0]: + out += f"({params[-1]})" + + out += ":" + + if len(params) > 1: + out += "\n def __init__(self, " + + for i, param in enumerate(params[:-1]): + if i > 0: + out += ", " + out += f"x{i}: {param}" + + out += "): ..." + else: + out += " ..." + + return out + + class_name = RE_CONSTRUCTOR.match(line).group(1) + + if not self._member_ok(class_name, "class"): + return + + self._classes[class_name] = RE_CONSTRUCTOR.sub(sub_fn, line) + + def _add_type_alias(self, line: str): + + match = RE_TYPE.match(line) + type_name = match.group(1) + definition = match.group(3) + + if not self._member_ok(type_name, "type alias"): + return + + definition_type = self._to_type(definition) + + self._types[type_name] = f"{type_name} = {definition_type}" + + def _add_function(self, line: str): + def sub_fn(match: re.Match[str]) -> str: + groups = match.groups() + out = f"def {groups[0]}(" + + params = self._parse_function(groups[1]) + + assert params is not None + + params = [self._to_type(p) for p in params] + + for i, param in enumerate(params[:-1]): + if i > 0: + out += ", " + out += f"x{i}: {param}" + + out += f") -> {params[-1]}: ..." + + return out + + fn_name = RE_FUNCTION.match(line).group(1) + + if not self._member_ok(fn_name, "function"): + return + + self._functions[fn_name] = RE_FUNCTION.sub(sub_fn, line) + + def _to_type(self, term: str) -> str: + assert term != "" + + if parts := self._parse_tuple(term): + if len(parts) == 1: + return self._to_type(parts[0]) + + nested = [self._to_type(p) for p in parts] + return f"typing.Tuple[{', '.join(nested)}]" + + if term[0] == "[" and term[-1] == "]": + nested = self._to_type(term[1:-1].strip()) + return f"typing.List[{nested}]" + + parts = self._parse_function(term) + if len(parts) >= 2: + param_types = [self._to_type(p.strip()) for p in parts[1:]] + ret_type = self._to_type(parts[0]) + + return f"typing.Callable[[{', '.join(param_types)}], {ret_type}]" + + if " " in term: + parts = self._parse_parens(term, " ") + + assert len(parts) >= 2 + + typ = self._to_type(parts[0]) + type_args = [self._to_type(p) for p in parts[1:]] + return f"{typ}[{','.join(type_args)}]" + + if term in HS_TO_PY_TYPE_MAP: + return HS_TO_PY_TYPE_MAP[term] + + if term[0].islower(): + name = self._generic_var(term) + return name + + return term.split(".")[-1] + + +def main(): + parser = argparse.ArgumentParser() + config_group = parser.add_mutually_exclusive_group(required=True) + + manual_config = config_group.add_argument_group() + manual_config.add_argument("modulenames", nargs="*") + manual_config.add_argument( + "-i", "--import", dest="modules_to_import", nargs="*") + + config_group.add_argument( + "-c", "--config", type=argparse.FileType("r"), nargs="?") + + parser.add_argument("-o", "--output-directory", + default=os.getcwd(), nargs="?") + + args = parser.parse_args() + + if args.config: + import json + config = json.load(args.config) + else: + config = [{ + "name": m, + "imports": args.modules_to_import} + for m in args.modulenames] + + print("Generating stubs for modules: " + + ", ".join(c["name"] for c in config)) + + with GHCI() as ghci: + for i, module in enumerate(config): + print(f"Module {module['name']}:") + print(" loading ...", end="", flush=True) + try: + ghci.command(f":l {module['name']}") + except Exception as e: + print(" failed") + raise e + print(" done") + + print(" generating stubs ...", end="", flush=True) + try: + content = ghci.command(f":browse! {module['name']}") + + text = re.sub(r"\n\s+", " ", content, flags=re.MULTILINE) + lines = text.splitlines() + lines = [line.strip() + for line in lines if not line.strip().startswith("--")] + + stub = PythonStub(module["name"]) + stub.ignore_members(module.get("ignore", [])) + for line in lines: + if line.strip() == "": + continue + stub.add_haskell_declaration(line) + + for import_module in module.get("imports", []): + stub.add_import(import_module) + + filename = os.path.join( + args.output_directory, module['name'].split(".")[-1]) + + with open(f"{filename}.pyi", "w") as f: + f.write(stub.to_pyi()) + + with open(f"{filename}.py", "w") as f: + f.write(stub.to_py()) + + except Exception as e: + print(" failed") + raise e + print(" done") + + +if __name__ == "__main__": + main() diff --git a/python/gui/pyproject.toml b/python/gui/pyproject.toml new file mode 100644 index 0000000000..6e4bd7e0ea --- /dev/null +++ b/python/gui/pyproject.toml @@ -0,0 +1,19 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "hets-gui" +version = "0.1.0.0" +description = "Graphical User Interface for hets (http://hets.eu)" +readme = "README.md" +requires-python = ">=3.8" +keywords = ["hets", "heterogeneous specifications"] +license = {text = "GPLv3"} +dependencies = ["xdot", "numpy", "graphviz", "hets-api"] + +[project.gui-scripts] +hets = "hetsgui:run" + +[tool.setuptools.package-data] +"*" = [ "*.gresource", "*.png" ] diff --git a/python/gui/src/hetsgui/ApplicationSettings.py b/python/gui/src/hetsgui/ApplicationSettings.py new file mode 100644 index 0000000000..7070200ccf --- /dev/null +++ b/python/gui/src/hetsgui/ApplicationSettings.py @@ -0,0 +1,45 @@ +from collections.abc import Callable +from dataclasses import dataclass +from typing import Optional, Any + +from gi.repository.GLib import KeyFile + + +class ApplicationSettings: + """ + Class to store application settings with a KeyFile (.ini). + """ + + keyfile: KeyFile + + def __init__(self, keyfile: KeyFile): + self.keyfile = keyfile + + def _get(self, m: Callable[[str, str], Any], group_name: str, key: str) -> Optional[Any]: + """ + Helper function to get an optional value from the keyfile. + + :param m: function on `KeyFile` + :param group_name: passed to m + :param key: passed to m + :return: + """ + try: + return m(group_name, key) + except: + return None + + + @property + def apply_proof_rules_automatically(self) -> Optional[bool]: + """ + Whether automatic proof rules should be applied automatically when loading a library. + :return: None if not set, otherwise the value + """ + + return self._get(self.keyfile.get_boolean, "hets", "apply_proof_rules_automatically") + + @apply_proof_rules_automatically.setter + def apply_proof_rules_automatically(self, value: bool) -> None: + self.keyfile.set_boolean("hets", "apply_proof_rules_automatically", value) + diff --git a/python/gui/src/hetsgui/GtkSmartTemplate.py b/python/gui/src/hetsgui/GtkSmartTemplate.py new file mode 100644 index 0000000000..d47fef52ca --- /dev/null +++ b/python/gui/src/hetsgui/GtkSmartTemplate.py @@ -0,0 +1,40 @@ +import logging + +from gi.repository import Gtk + +from .utils import resource_exist + + +def GtkSmartTemplate(original_class): + """ + An extension to the Gtk.Template decorator that also loads a css file from the resources. + + Uses the name of the module of the class to determine the path to the resources. + + :param original_class: The class to decorate. + :return: The decorated class. + """ + + _logger = logging.getLogger(__name__) + + # The module name follows the format `hetsgui.path.to.file`. + # The resources are organized under `/eu/hets/gui/path/to/file`. + resource_name = "/".join(original_class.__module__.split(".")[1:]) + style_resource_name = f"/eu/hets/gui/{resource_name}.css" + + original_init = original_class.__init__ + + def new_init(self, *args, **kwargs): + original_init(self, *args, **kwargs) + + if resource_exist(style_resource_name): + _logger.debug("Loading css resource %s", style_resource_name) + self._provider = Gtk.CssProvider() + self._provider.load_from_resource(style_resource_name) + self.get_style_context().add_provider_for_screen(self.get_screen(), self._provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) + + self.connect("destroy", lambda w: self.get_style_context().remove_provider_for_screen(self.get_screen(), self._provider)) + + original_class.__init__ = new_init + + return Gtk.Template(resource_path=f"/eu/hets/gui/{resource_name}.ui")(original_class) diff --git a/python/gui/src/hetsgui/LibraryManager.py b/python/gui/src/hetsgui/LibraryManager.py new file mode 100644 index 0000000000..bd328a3ca4 --- /dev/null +++ b/python/gui/src/hetsgui/LibraryManager.py @@ -0,0 +1,252 @@ +import logging +import os.path +import typing +from typing import Dict, Optional + +from gi.repository import Gtk, Gio, GObject + +from hets import Library, load_library, Options +from .windows.LibraryWindow import LibraryWindow +from .windows.MainWindow import MainWindow +from .windows.RefinementTreeWindow import RefinementTreeWindow + + +class LibraryManager(GObject.GObject): + """ + Class to manage the loaded libraries and windows. + """ + + # Register signals + __gsignals__ = { + # Signal emitted when a new library is loaded + "new-library": (GObject.SIGNAL_RUN_FIRST, None, (str,)), + + # Signal emitted when a new refinement tree specification is loaded + "new-refinement-tree-spec": (GObject.SIGNAL_RUN_FIRST, None, (str, str)) + } + + _logger = logging.getLogger(__name__) + + _loaded_library_paths: Dict[str, str] + """ Mapping of loaded library paths to library ids. """ + + _loaded_libraries: Dict[str, Library] + """ Mapping of ids of to loaded libraries. """ + + _open_windows: Dict[str, MainWindow] + """ Mapping of library ids to their window. """ + + _open_refinement_tree_windows: Dict[str, RefinementTreeWindow] + """ Mapping of library ids and spec names to their refinement tree window. """ + + _default_window: Optional[MainWindow] + """ Default window to be used when no other window is open. """ + + _library_graph_window: Optional[LibraryWindow] + """ Window showing the library graph. """ + + def __init__(self, application: Gio.Application): + super().__init__() + self._library_graph_window = None + self._loaded_libraries = {} + self._known_libnames = {} + self._open_windows = {} + self._open_refinement_tree_windows = {} + self._loaded_library_paths = {} + self._default_window = None + + self._application = application + + def _new_window(self) -> MainWindow: + """ + Helper function to create a new main window connecting its signals to this instance. + :return: Initialized main window instance. + """ + + window = MainWindow(application=self._application) + window.connect("load-file", self.on_load_file) + window.connect("show-library", self.on_show_library) + window.connect("show-library-graph", self.on_show_library_graph) + window.connect("destroy", self._on_window_destroyed) + + return window + + def _on_window_destroyed(self, window: MainWindow): + """ + Handler for the destroy signal of a window. + + Removes the window from the list of open windows. + + :param window: The destroyed window. + :return: + """ + + key = next((k for k, v in self._open_windows.items() if v == window), None) + + if key is None: + self._logger.warning( + "Trying to clean up a destroyed window but the window was not found in the list of open windows") + else: + del self._open_windows[key] + + def show_default_window(self): + """ + Shows the default window. + + If no default window is set, a new one is created. + :return: + """ + + if self._default_window is None: + self._default_window = self._new_window() + + self._default_window.show_all() + self._default_window.present() + + def on_show_library(self, sender, library_id: str): + """ + Handler for the show-library signal from library windows. + + :param sender: ignored + :param library_id: id of the library to show + :return: + """ + + self.show_library(library_id) + + def on_load_file(self, sender, file: str, settings: Options): + """ + Handler for the load-file signal from library windows. + + :param sender: ignored + :param file: File to load + :param settings: HetsCats object with library settings + :return: + """ + + self.load_file(file, settings) + + def load_file(self, file: str, settings: Options): + """ + Load a library from a file. + + :param file: File to load + :param settings: HetsCats object with library settings + :return: + """ + + file = os.path.abspath(file) + + # Only load the library if it is not already loaded + if file in self._loaded_library_paths: + self.show_library(self._loaded_library_paths[file]) + return + + try: + # Load and register the library, referenced libraries, and refinement tree specifications + library = load_library(file, settings) + library_id = library.name().id() + for name, _ in library.environment(): + self._loaded_libraries[name.id()] = Library((name._hs_libname, library._env)) + + self.emit("new-library", name.id()) + + self._loaded_library_paths[file] = library_id + + for spec in self._loaded_libraries[library_id].specifications(): + self.emit("new-refinement-tree-spec", library_id, spec) + + self.show_library(library_id) + except Exception as e: + # Unfortunately, we cannot capture the output from the console here + + self._logger.error(f"Failed to load file '{file}': %s", e) + + dialog = Gtk.MessageDialog(flags=0, message_type=Gtk.MessageType.ERROR, + buttons=Gtk.ButtonsType.CLOSE, text=f"Failed to load {file}!") + dialog.format_secondary_text(f"Check the console for more details.\nError message: {str(e)}") + dialog.run() + dialog.destroy() + return + + def show_library(self, library_id: str): + """ + Show the loaded library with the given id. + + Does nothing if a library with the ID is not loaded + + :param library_id: id of the loaded library to show + :return: + """ + + if library_id not in self._open_windows: + # Reuse the default window if no other library used it before + if self._default_window is not None: + window = self._default_window + self._default_window = None + else: + window = self._new_window() + + window.use_library(self._loaded_libraries[library_id]) + + self._open_windows[library_id] = window + + self._open_windows[library_id].show_all() + self._open_windows[library_id].present() + + def on_show_library_graph(self, sender, library_id: str): + self.show_library_graph(library_id) + + def show_library_graph(self, library_id: str): + """ + Show the library graph of a loaded library with the given id. + + :param library_id: Id of the loaded library. + :return: + """ + if library_id not in self._loaded_libraries: + self._logger.error(f"Tried to open library graph for an unloaded library with id {library_id}!") + + # Show only one library graph window at a time + if self._library_graph_window is None: + self._library_graph_window = LibraryWindow(self._loaded_libraries[library_id], + application=self._application) + + def on_destroy(_): + self._library_graph_window = None + + self._library_graph_window.connect("destroy", on_destroy) + + self._library_graph_window.show_all() + self._library_graph_window.present() + + def on_show_refinement_tree(self, sender, library_and_spec: typing.Tuple[str, str]): + library_id, spec_name = library_and_spec + self.show_refinement_tree(library_id, spec_name) + + def show_refinement_tree(self, library_id: str, spec_name: str): + """ + Show the refinement tree for a loaded library with the given id and spec name. + + :param library_id: ID of the loaded library + :param spec_name: Name of the spec + :return: + """ + + if library_id not in self._loaded_libraries: + self._logger.error( + f"Tried to open refinement tree for spec '{spec_name}' of an unloaded library with id {library_id}!") + + spec_id = f"{library_id}/{spec_name}" + if spec_id not in self._open_refinement_tree_windows: + self._open_refinement_tree_windows[spec_id] = RefinementTreeWindow(self._loaded_libraries[library_id], + spec_name, + application=self._application) + + def on_destroy(_): + del self._open_refinement_tree_windows[spec_id] + + self._open_refinement_tree_windows[spec_id].connect("destroy", on_destroy) + + self._open_refinement_tree_windows[spec_id].show_all() + self._open_refinement_tree_windows[spec_id].present() diff --git a/python/gui/src/hetsgui/Makefile b/python/gui/src/hetsgui/Makefile new file mode 100644 index 0000000000..219991f249 --- /dev/null +++ b/python/gui/src/hetsgui/Makefile @@ -0,0 +1,8 @@ +GLIB_COMPILE_RESOURCES = $(shell which glib-compile-resources ) + +.PHONY: resources + +hetsgui.gresource: hetsgui.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies --sourcedir="./" ./hetsgui.gresource.xml) + glib-compile-resources --sourcedir="./" $< --target=$@ + + diff --git a/python/gui/src/hetsgui/__init__.py b/python/gui/src/hetsgui/__init__.py new file mode 100644 index 0000000000..915a5358e1 --- /dev/null +++ b/python/gui/src/hetsgui/__init__.py @@ -0,0 +1,21 @@ +""" +Entry point for the Hets GUI application. + +run: Runs the Hets GUI application. +""" + + +from .application import HetsApplication + +def run(): + """ + Runs the Hets GUI application. + + Blocks until the application exits and exits the programm with the same status code. + + :return: None + """ + import sys + app = HetsApplication() + exit_status = app.run(sys.argv) + sys.exit(exit_status) diff --git a/python/gui/src/hetsgui/actions/__init__.py b/python/gui/src/hetsgui/actions/__init__.py new file mode 100644 index 0000000000..7f048af4da --- /dev/null +++ b/python/gui/src/hetsgui/actions/__init__.py @@ -0,0 +1,5 @@ +""" +This module contains generic action handlers. +""" + +from .model import * diff --git a/python/gui/src/hetsgui/actions/model.py b/python/gui/src/hetsgui/actions/model.py new file mode 100644 index 0000000000..196f76985d --- /dev/null +++ b/python/gui/src/hetsgui/actions/model.py @@ -0,0 +1,50 @@ +import gi +from gi.repository import Gio, GLib, Gtk + + +def toggle_tree_view_header_cell_handler(column: Gtk.TreeViewColumn, index=1): + """ + Handler of the clicked signal of a header cell of a Gtk.TreeViewColumn to toggle the state of all cells in a column. + :param column: A Gtk.TreeViewColumn which got clicked + :param index: Index of the column which contains the boolean state to toggle + :return: + """ + + tree_view: Gtk.TreeView = column.get_tree_view() + model = tree_view.get_model() + widget = column.get_widget() + + if not isinstance(widget, Gtk.CheckButton): + return + + new_state = not (widget.get_inconsistent() or widget.get_active()) + + for row in model: + row[index] = new_state + + widget.set_inconsistent(False) + widget.set_active(new_state) + + +def toggle_tree_view_cell_handler(toggle_column: Gtk.TreeViewColumn, path: str, index=1): + """ + Handler of the toggled signal of a Gtk.TreeViewColumn to adjust the state of the header cell. + :param toggle_column: Toggled column + :param path: Path of the toggled cell + :param index: Index of the column which contains the boolean state + :return: + """ + + tree_view: Gtk.TreeView = toggle_column.get_tree_view() + model = tree_view.get_model() + + header = toggle_column.get_widget() + + next_state = not model[path][index] + + model[path][index] = next_state + + if header is not None and isinstance(header, Gtk.CheckButton): + consistent = all(row[index] == next_state for row in model) + header.set_inconsistent(not consistent) + header.set_active(next_state) diff --git a/python/gui/src/hetsgui/application.py b/python/gui/src/hetsgui/application.py new file mode 100644 index 0000000000..6f3a3ec4b8 --- /dev/null +++ b/python/gui/src/hetsgui/application.py @@ -0,0 +1,296 @@ +import logging +import os.path +import threading +from typing import Optional + +import gi +import sys +from gi.repository.GLib import KeyFileFlags + +from .ApplicationSettings import ApplicationSettings +from .utils import get_variant + +gi.require_version("Gtk", "3.0") +from gi.repository import GLib, Gtk, Gio + + +class HetsApplication(Gtk.Application): + """ + The main application class for the Hets GUI. + """ + + _logger = logging.getLogger(__name__) + + _reopen_libraries_menu_section: Optional[Gio.Menu] + """ Menu section for opening already loaded libraries. """ + + _refinement_trees_menu_section: Optional[Gio.Menu] + """ Menu section for opening refinement trees. """ + + _window: Optional[Gtk.Window] + """ Start up window to be shown on application start. """ + + _file: Optional[str] + """ File to be opened on application start. """ + + settings: ApplicationSettings + """ Application specific settings. """ + + + def __init__(self): + super().__init__( + application_id="eu.hets.gui", + flags=Gio.ApplicationFlags.HANDLES_OPEN) + self._library_manager = None + self._refinement_trees_menu_section = None + self._reopen_libraries_menu_section = None + + self._window = None + self._file = None + + # Set the application name + GLib.set_application_name("Hets") + + # Declare that the application takes a file as an argument + self.set_option_context_parameter_string("FILE") + + self.connect("open", self._on_open) + + # Load and register resources + pgk_dir = os.path.dirname(os.path.realpath(__file__)) + resource_file = os.path.join(pgk_dir, "hetsgui.gresource") + self._logger.debug("Loading resources from %s", resource_file) + resource: Gio.Resource = Gio.resource_load(resource_file) + Gio.resources_register(resource) + + # Add command line options + self.add_main_option("log", ord('l'), GLib.OptionFlags.NONE, GLib.OptionArg.STRING, "Log level", + "") + self.connect("handle-local-options", self._on_handle_local_options) + + # Add actions on application level + # Action to open a window for a library + action_show_library = Gio.SimpleAction.new("open_win_for_lib", GLib.VariantType("s")) + action_show_library.connect("activate", self._on_open_win_for_lib) + self.add_action(action_show_library) + + # Action to show a window for a refinement tree + action_show_refinement_tree = Gio.SimpleAction.new("open_refinement_tree", GLib.VariantType("av")) + action_show_refinement_tree.connect("activate", self._on_open_refinement_tree) + self.add_action(action_show_refinement_tree) + + # Load settings and add action to save settings to persist restarts + self.load_settings() + action_save_settings = Gio.SimpleAction.new("save_settings", None) + action_save_settings.connect("activate", lambda _1, _2: self.save_settings()) + self.add_action(action_save_settings) + + def load_settings(self): + """ + Load the application settings from the users config directory if existing. + :return: + """ + + path = os.path.join(GLib.get_user_config_dir(), "hets", "settings.ini") + file = GLib.KeyFile() + self.settings = ApplicationSettings(file) + + if os.path.exists(path): + file.load_from_file(path, KeyFileFlags.NONE) + + def save_settings(self): + """ + Save the application settings to the users config directory. + :return: + """ + self._logger.debug("Saving settings") + dir = os.path.join(os.path.join(GLib.get_user_config_dir(), "hets")) + + os.makedirs(dir, exist_ok=True) + + path = os.path.join(dir, "settings.ini") + self.settings.keyfile.save_to_file(path) + + def _on_open_win_for_lib(self, action: Gio.SimpleAction, parameter: GLib.Variant): + """ + Handler for the open_win_for_lib action. + + Opens a window for the given library. + :param action: ignored + :param parameter: name of or path to the library to open + :return: + """ + + if self._library_manager is not None: + self._library_manager.show_library(parameter.get_string()) + + def _on_open_refinement_tree(self, action: Gio.SimpleAction, parameter: GLib.Variant): + """ + Handler for the open_refinement_tree action. + + Opens a window for the given refinement tree. + + :param action: ignored + :param parameter: tuple of library id and library name + :return: + """ + + + if self._library_manager is not None: + library_id, spec_name = parameter.unpack() + + self._library_manager.show_refinement_tree(library_id, spec_name) + + def _on_handle_local_options(self, application, options: GLib.VariantDict): + """ + Handler for the handle-local-options signal. + + Sets up the logging based options provided from the command line. + + :param application: ignored + :param options: options provided from the command line + :return: + """ + + log_value = options.lookup_value("log") + log_level_int = logging.DEBUG + if log_value is not None: + log_level = log_value.get_string().upper() + log_level_int = getattr(logging, log_level.upper(), None) + if not isinstance(log_level_int, int): + print('Invalid log level: %s' % log_level, file=sys.stderr) + return 1 + + logging.basicConfig( + level=log_level_int, + format='[%(asctime)s.%(msecs)03d] [ %(levelname)-7s ] [%(name)s] %(message)s', + datefmt='%Y-%m-%d %H:%M:%S', + ) + + return -1 + + def do_startup(self): + """ + Handler for the startup signal. + + Sets up the application and gets reference to UI variables + :return: + """ + + Gtk.Application.do_startup(self) + self._reopen_libraries_menu_section = self.get_menu_by_id("reopen-section") + self._refinement_trees_menu_section = self.get_menu_by_id("refinement-trees") + + def do_command_line(self, command_line): + self.activate() + return 0 + + def _on_open(self, action, files, n_files, hint): + """ + Handler for the open signal. + + Opens the selected file. + + :param action: ignored + :param files: selected files to open + :param n_files: number of files to open (because C array) + :param hint: ignored + :return: + """ + if n_files != 1: + print("Expected exactly one file", file=sys.stderr) + return 1 + + self._file = files[0].get_path() + self.do_activate() + + def do_activate(self): + """ + Starts the application. + + Loads necessary libraries and shows a loading window in the meantime. + + :return: + """ + + if not self._window: + # The loading window + from .windows.StartUpWindow import StartUpWindow + startup_window = StartUpWindow(application=self) + startup_window.show_all() + startup_window.present() + + self._window = startup_window + + def start_up_done(): + from .LibraryManager import LibraryManager + from hets import Options + + self._library_manager = LibraryManager(self) + self._library_manager.connect("new-library", self._on_new_library) + self._library_manager.connect("new-refinement-tree-spec", self._on_new_refinement_tree_spec) + + startup_window.close() + + if self._file: + default_options = Options(libdirs=os.environ.get("HETS_LIB", [])) + self._library_manager.load_file(self._file, default_options) + else: + self._library_manager.show_default_window() + + self._window.show_all() + self._window.present() + # self.set_action_group() + + # noinspection PyUnresolvedReferences + def start_up(): + self._logger.info("Loading python libraries") + import hets + self._logger.info("Loading python libraries done") + + # Changes to UI must be executed on the main thread / UI thread + GLib.idle_add(start_up_done) + + # Load the libraries in the background + t = threading.Thread(target=start_up) + t.start() + + self._window.show_all() + self._window.present() + + def _on_new_library(self, sender, library_id: str): + """ + Handler for the new-library signal. + + Adds a menu item for the new library to the reopen libraries menu section. + + :param sender: ignored + :param library_id: id of the new library + :return: + """ + + if self._reopen_libraries_menu_section: + item = Gio.MenuItem() + item.set_label(library_id) + item.set_action_and_target_value("app.open_win_for_lib", get_variant(library_id)) + + self._reopen_libraries_menu_section.append_item(item) + + def _on_new_refinement_tree_spec(self, sender, library_id: str, spec_name: str): + """ + Handler for the new-refinement-tree-spec signal. + + Adds a menu item for the new refinement tree to the refinement trees menu section. + + :param sender: ignored + :param library_id: id of the library + :param spec_name: name of the spec + :return: + """ + + if self._refinement_trees_menu_section: + item = Gio.MenuItem() + item.set_label(f"{library_id}: {spec_name}") + item.set_action_and_target_value("app.open_refinement_tree", get_variant((library_id, spec_name))) + + self._refinement_trees_menu_section.append_item(item) diff --git a/python/gui/src/hetsgui/formatting/__init__.py b/python/gui/src/hetsgui/formatting/__init__.py new file mode 100644 index 0000000000..a6460c4aa8 --- /dev/null +++ b/python/gui/src/hetsgui/formatting/__init__.py @@ -0,0 +1,5 @@ +""" +This module contains the formatting utilities. +""" + +from .colors import * diff --git a/python/gui/src/hetsgui/formatting/colors.py b/python/gui/src/hetsgui/formatting/colors.py new file mode 100644 index 0000000000..a693dbf634 --- /dev/null +++ b/python/gui/src/hetsgui/formatting/colors.py @@ -0,0 +1,83 @@ +""" +This module contains color constants and functions for converting between color representations. +""" + +from gi.repository import Gdk + +from hets import ProofKind, ConsistencyKind + +PROOF_KIND_BG_COLORS = { + ProofKind.UNKNOWN: "fuchsia", + ProofKind.OPEN: "white", + ProofKind.PROVEN: "limegreen", + ProofKind.PROVEN_BY_INCONSISTENCY: "orange", + ProofKind.DISPROVEN: "coral", + ProofKind.TIMED_OUT: "royalblue", + ProofKind.GUESSED: "darkseagreen", + ProofKind.CONJECTURED: "darkseagreen", + ProofKind.HANDWRITTEN: "darkseagreen" +} + +PROOF_KIND_FG_COLORS = { + ProofKind.UNKNOWN: "fuchsia", + ProofKind.OPEN: "black", + ProofKind.PROVEN: "limegreen", + ProofKind.PROVEN_BY_INCONSISTENCY: "orange", + ProofKind.DISPROVEN: "coral", + ProofKind.TIMED_OUT: "royalblue", + ProofKind.GUESSED: "darkseagreen", + ProofKind.CONJECTURED: "darkseagreen", + ProofKind.HANDWRITTEN: "darkseagreen" +} + +CONSISTENCY_KIND_BG_COLORS = { + ConsistencyKind.INCONSISTENT: "red", + ConsistencyKind.UNKNOWN: "black", + ConsistencyKind.PROOF_THEORETICALLY_CONSERVATIVE: "darkgreen", + ConsistencyKind.CONSERVATIVE: "green", + ConsistencyKind.MONOMORPHIC: "violet", + ConsistencyKind.DEFINITIONAL: "darkseagreen", + ConsistencyKind.TIMED_OUT: "blue", + ConsistencyKind.ERROR: "darkred", +} + + +def color_name_to_rgba(color_name: str) -> Gdk.RGBA: + color = Gdk.RGBA() + color.parse(color_name) + return color + + +def color_to_hex(color: Gdk.RGBA) -> str: + red = int(color.red * 255) + green = int(color.green * 255) + blue = int(color.blue * 255) + return f"#{red:02x}{green:02x}{blue:02x}" + + +# KEY: (colorname, variant, light) +COLOR_MAP = { + ("black", False, False): "gray0" + , ("black", False, True): "gray30" + , ("blue", False, False): "RoyalBlue3" + , ("blue", False, True): "RoyalBlue1" + , ("blue", True, False): "SteelBlue3" + , ("blue", True, True): "SteelBlue1" + , ("coral", False, False): "coral3" + , ("coral", False, True): "coral1" + , ("coral", True, False): "LightSalmon2" + , ("coral", True, True): "LightSalmon" + , ("green", False, False): "MediumSeaGreen" + , ("green", False, True): "PaleGreen3" + , ("green", True, False): "limegreen" + , ("green", True, True): "LightGreen" + , ("purple", False, False): "purple2" + , ("yellow", False, False): "gold" + , ("yellow", False, True): "yellow" + , ("yellow", True, False): "LightGoldenrod3" + , ("yellow", True, True): "LightGoldenrod" + , ("fuchsia", False, False): "fuchsia" + , ("fuchsia", False, True): "fuchsia" + , ("fuchsia", True, False): "fuchsia" + , ("fuchsia", True, True): "fuchsia" +} diff --git a/python/gui/src/hetsgui/hetsgui.gresource.xml b/python/gui/src/hetsgui/hetsgui.gresource.xml new file mode 100644 index 0000000000..ff331c1417 --- /dev/null +++ b/python/gui/src/hetsgui/hetsgui.gresource.xml @@ -0,0 +1,35 @@ + + + + + + resources/icon.png + resources/menus.ui + + windows/ConservativityCheckWindow.ui + windows/ConservativityCheckWindow.css + windows/ConsistencyCheckWindow.ui + windows/ConsistencyCheckWindow.css + windows/LibraryWindow.ui + windows/RefinementTreeWindow.ui + windows/MainWindow.ui + windows/MainWindow.css + windows/StartUpWindow.ui + windows/StartUpWindow.css + windows/ProveWindow.ui + windows/LibrarySettingsWindow.ui + windows/ProofDetailsWindow.ui + windows/ListViewSelectionDialog.ui + widgets/ProofDetail.ui + widgets/ProofDetail.css + widgets/EditableListView.ui + widgets/GridWithToolComorphismSelector.ui + + + diff --git a/python/gui/src/hetsgui/requirements.txt b/python/gui/src/hetsgui/requirements.txt new file mode 100644 index 0000000000..882698e9b9 --- /dev/null +++ b/python/gui/src/hetsgui/requirements.txt @@ -0,0 +1,5 @@ +pycairo +PyGObject +xdot +numpy +graphviz diff --git a/python/gui/src/hetsgui/resources/icon.png b/python/gui/src/hetsgui/resources/icon.png new file mode 100644 index 0000000000..92bd203618 Binary files /dev/null and b/python/gui/src/hetsgui/resources/icon.png differ diff --git a/python/gui/src/hetsgui/resources/menus.ui b/python/gui/src/hetsgui/resources/menus.ui new file mode 100644 index 0000000000..6f1192d366 --- /dev/null +++ b/python/gui/src/hetsgui/resources/menus.ui @@ -0,0 +1,140 @@ + + + + + + File + + Open + win.open_file + + + Open loaded library + + + + View +
+ + Library Graph + win.open_library_window + + + Refinement Tree + + +
+
+ + Show internal names + win.toggle_show_names + + + Show newly added proven edges + win.toggle_show_edges + + + Graph rendering + + + Vertical + vertical + win.change_graph_layout + + + Horizontal + horizontal + win.change_graph_layout + + +
+
+ + Proofs + + Apply proof rules automatically + win.proofs.automatic + + + Global-Subsumption + win.proofs.global_subsume + + + Global-Decomposition + win.proofs.global_decomposition + + + Local-Inference + win.proofs.local_inference + + + Local-Decomposition + win.proofs.local_decomposition + + + Prove composed edges + win.proofs.composition_prove_edges + + + Conservativity + win.proofs.conservativity + + + Hide-Theorem-Shift + win.proofs.automatic_hide_theorem_shift + + + Theorem-Hide-Shift + win.proofs.theorem_hide_shift + + + Compute colimit + win.proofs.compute_colimit + + + Compute normal form + win.proofs.normal_form + + + Triangle-Cons + win.proofs.triangle_cons + + + Freeness + win.proofs.freeness + + + Flatten imports + win.proofs.lib_flat_imports + + + Flatten D-unions + win.proofs.lib_flat_d_unions + + + Flatten renamings + win.proofs.lib_flat_renamings + + + Flatten hiding + win.proofs.lib_flat_hiding + + + Flatten heterogen + win.proofs.lib_flat_heterogen + + + Qualify lib env + win.proofs.qualify_lib_env + + + + Settings + + Library settings + win.open_library_settings + + +
+ +
diff --git a/python/gui/src/hetsgui/utils.py b/python/gui/src/hetsgui/utils.py new file mode 100644 index 0000000000..fae3fcab7d --- /dev/null +++ b/python/gui/src/hetsgui/utils.py @@ -0,0 +1,91 @@ +""" +Collection of utility and helper functions. +""" + +import re +import typing +from typing import Any + +from gi.repository import GLib, Gio + + +def get_variant_type(t: type) -> GLib.VariantType: + """ + Get the GLib.VariantType for a given data type. + :param t: Python type + :return: GLib.VariantType + """ + if t == str: + return GLib.VariantType.new("s") + if t == int: + return GLib.VariantType("i") + if t == dict: + return GLib.VariantType("a{sv}") + if t in [list, tuple, set]: + return GLib.VariantType("av") + + raise Exception(f"Unknown data type: {t}") + + +def get_variant(data: Any) -> GLib.Variant: + """ + Convert a Python object to a GLib.Variant. + + :param data: Python object + :return: GLib.Variant + """ + + # Source: https://gitlab.gnome.org/GNOME/gnome-browser-connector/-/blob/master/gnome_browser_connector/helpers.py + # Licenced under GPL-3.0-or-later + + if isinstance(data, str): + return GLib.Variant.new_string(data) + elif isinstance(data, bool): + return GLib.Variant.new_boolean(data) + elif isinstance(data, int): + return GLib.Variant.new_int32(data) + elif isinstance(data, (list, tuple, set)): + variant_builder: GLib.VariantBuilder = GLib.VariantBuilder.new(GLib.VariantType.new('av')) + + for value in data: + variant_builder.add_value(GLib.Variant.new_variant(get_variant(value))) + + return variant_builder.end() + elif isinstance(data, dict): + variant_builder = GLib.VariantBuilder.new(GLib.VariantType.new('a{sv}')) + + for key in data: + if data[key] is None: + continue + + key_string = str(key) + + variant_builder.add_value( + GLib.Variant.new_dict_entry( + get_variant(key_string), GLib.Variant.new_variant(get_variant(data[key])) + ) + ) + + return variant_builder.end() + else: + raise Exception(f"Unknown data type: {type(data)}") + + +def resource_exist(resource_path: str) -> bool: + """ + Check if a resource exists. + + :param resource_path: Path to the resource + :return: Whether the resource exists + """ + + segments = [s for s in re.split(r"([^/]+/)", resource_path) if s] + segments.reverse() + path = segments.pop() + while segments: + segment = segments.pop() + children = Gio.resources_enumerate_children(path, 0) + if segment not in children: + return False + path += segment + return True diff --git a/python/gui/src/hetsgui/widgets/CellRendererLink.py b/python/gui/src/hetsgui/widgets/CellRendererLink.py new file mode 100644 index 0000000000..4eabfd0f18 --- /dev/null +++ b/python/gui/src/hetsgui/widgets/CellRendererLink.py @@ -0,0 +1,18 @@ +import cairo +from gi.repository import Gtk, GObject, Gdk + + +class CellRendererLink(Gtk.CellRendererText): + """ + A cell renderer that displays a clickable link. + """ + + __gsignals__ = {"clicked": (GObject.SIGNAL_RUN_FIRST, None, (str,))} + __gtype_name__ = "CellRendererLink" + + def __init__(self, **kwargs): + super().__init__() + self.set_property("mode", Gtk.CellRendererMode.ACTIVATABLE) + + def do_activate(self, event, widget, path, background_area, cell_area, flags): + self.emit('clicked', path) diff --git a/python/gui/src/hetsgui/widgets/EdgeInfoDialog.py b/python/gui/src/hetsgui/widgets/EdgeInfoDialog.py new file mode 100644 index 0000000000..83e825c2f5 --- /dev/null +++ b/python/gui/src/hetsgui/widgets/EdgeInfoDialog.py @@ -0,0 +1,30 @@ +from gi.repository import Gtk + +from hets import DevGraphEdge + + +class EdgeInfoDialog(Gtk.Dialog): + """ + Dialog to show information about a DevGraphEdge. + """ + + def __init__(self, edge: DevGraphEdge): + super().__init__() + + self.set_default_size(800, 600) + + title = f"edge {edge.id()} {edge.name()}({edge.origin()} --> {edge.target()})" + self.set_title(title) + + box = self.get_content_area() + scrolled_window = Gtk.ScrolledWindow(expand=True) + + text_view = Gtk.TextView() + text_view.set_property('editable', False) + text_view.set_property('monospace', True) + text_buffer = text_view.get_buffer() + text_buffer.set_text(title + "\n" + edge.info()) + + scrolled_window.add(text_view) + box.add(scrolled_window) + self.show_all() diff --git a/python/gui/src/hetsgui/widgets/EditableListView.py b/python/gui/src/hetsgui/widgets/EditableListView.py new file mode 100644 index 0000000000..716dd442f2 --- /dev/null +++ b/python/gui/src/hetsgui/widgets/EditableListView.py @@ -0,0 +1,64 @@ +import gi +from gi.repository import Gtk, GObject + +from ..GtkSmartTemplate import GtkSmartTemplate + + +@GtkSmartTemplate +class EditableListView(Gtk.Bin): + """ + A list view that allows to add and delete rows as well as editing the content of the cells. + """ + + __gtype_name__ = "EditableListView" + + model = GObject.Property(type=Gtk.ListStore) + + _btn_delete: Gtk.Button = Gtk.Template.Child() + _treeview: Gtk.TreeView = Gtk.Template.Child() + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + self.connect("notify::model", self._on_model_changed) + + self.model = Gtk.ListStore(str) + + @Gtk.Template.Callback() + def on_add_button_clicked(self, *args): + # Add empty row + self.model.append([""] * self.model.get_n_columns()) + + @Gtk.Template.Callback() + def on_delete_button_clicked(self, *args): + # Get the selected row and remove it + model, selected = self._treeview.get_selection().get_selected() + + model.remove(selected) + + @Gtk.Template.Callback() + def on_treeview_selection_changed(self, *args): + # Enable delete button iif a row is selected + model, selected = self._treeview.get_selection().get_selected() + + self._btn_delete.set_sensitive(selected) + + @Gtk.Template.Callback() + def on_text_edited(self, widget, path, text: str, index: int): + self.model[path][index] = text + + def _on_model_changed(self, *args): + # Remove all columns from a potential previous model + columns = self._treeview.get_columns() + for column in columns: + self._treeview.remove_column(column) + + # Add a column for each column in the model + num_columns = self.model.get_n_columns() + for i in range(num_columns): + renderer = Gtk.CellRendererText(editable=True, placeholder_text="Type here...") + renderer.connect("edited", self.on_text_edited, i) + column = Gtk.TreeViewColumn("", renderer, text=i) + self._treeview.append_column(column) + + self._treeview.set_model(self.model) diff --git a/python/gui/src/hetsgui/widgets/EditableListView.ui b/python/gui/src/hetsgui/widgets/EditableListView.ui new file mode 100644 index 0000000000..12f3ae71e7 --- /dev/null +++ b/python/gui/src/hetsgui/widgets/EditableListView.ui @@ -0,0 +1,115 @@ + + + + + diff --git a/python/gui/src/hetsgui/widgets/ExtendedDotWidget.py b/python/gui/src/hetsgui/widgets/ExtendedDotWidget.py new file mode 100644 index 0000000000..27998bdb55 --- /dev/null +++ b/python/gui/src/hetsgui/widgets/ExtendedDotWidget.py @@ -0,0 +1,122 @@ +import logging + +import gi +import xdot +from gi.repository import Gtk, GObject, Gdk +from graphviz import Digraph + +from xdot.ui import DotWidget + +from hetsgui.formatting import color_to_hex + + +class ExtendedDotWidget(DotWidget): + """ + Extended version of the xdot.DotWidget. + + Added functionalities: + - emits signals when a node or edge is right-clicked + - uses the theme's background color as the graph's background color + - disables some key bindings (like quitting on 'q') + + """ + + __gtype_name__ = "ExtendedDotWidget" + + __gsignals__ = { + # Emitted when a node is right-clicked. + "node-right-click": (GObject.SIGNAL_RUN_FIRST, None, (str, object)), + + # Emitted when an edge is right-clicked. + "edge-right-click": (GObject.SIGNAL_RUN_FIRST, None, (str, str, object)) + } + + _logger = logging.getLogger(__name__) + + dotcode = GObject.Property(type=str) + """ The dot code to be rendered. """ + + def __init__(self): + super().__init__() + + # Update visualisation when dotcode changes + self.connect("notify::dotcode", self.on_dotcode_changed) + + # Listen for theme changes + settings: Gtk.Settings = Gtk.Settings.get_for_screen(self.get_screen()) + settings.connect("notify::gtk-theme-name", lambda w, p: self.render()) + settings.connect("notify::gtk-application-prefer-dark-theme", lambda w, p: self.render()) + + def render(self): + pass + + def get_themed_graph(self): + """ + Create an empty graph with the theme's background color. + + :return: + """ + g = Digraph("G") + + success, color = self.get_style_context().lookup_color("theme_bg_color") + color = color_to_hex(color) + + if color: + g.graph_attr["bgcolor"] = color + + return g + + def on_dotcode_changed(self, widget, param): + """ + Propagate the dotcode change to the xdot widget. + + :param widget: ignored + :param param: ignored + :return: + """ + dotcode = self.dotcode.encode("utf8") + + self.set_dotcode(dotcode) + + def on_key_press_event(self, widget, event): + """ + Supress some key press events to disable functionality like quitting on q, focusing search widget with f etc. + + :param widget: self + :param event: event + :return: + """ + + if event.keyval < Gdk.KEY_a or event.keyval > Gdk.KEY_z: + super().on_key_press_event(widget, event) + + def on_click(self, element, event): + """ + Handle and propagates right-click events on nodes and edges. + + :param element: the clicked element or None + :param event: the event + :return: + """ + + # If element is None, try to get the element at the click position + if element is None: + jump = self.get_jump(event.x, event.y) + element = jump.item if jump is not None else None + + # If element is still None, the click was not on a node or edge + if element is None: + return True + + if event.button == 3: # on right click + self._logger.debug("Right click on %s", element) + if isinstance(element, xdot.ui.elements.Node): + node_id = element.id.decode("utf-8") + + self.emit("node-right-click", node_id, event) + elif isinstance(element, xdot.ui.elements.Edge): + src_id, dst_id = element.src.id.decode("utf-8"), element.dst.id.decode("utf-8") + + self.emit("edge-right-click", src_id, dst_id, event) + + return True diff --git a/python/gui/src/hetsgui/widgets/GraphvizGraphWidget.py b/python/gui/src/hetsgui/widgets/GraphvizGraphWidget.py new file mode 100644 index 0000000000..e98f1791ef --- /dev/null +++ b/python/gui/src/hetsgui/widgets/GraphvizGraphWidget.py @@ -0,0 +1,456 @@ +import logging +import threading +from collections import defaultdict +from typing import Optional, Tuple, DefaultDict + +from gi.repository import Gtk, Gio, GLib, GObject +from graphviz import Digraph + +from ..formatting.colors import COLOR_MAP +from ..formatting.colors import color_to_hex +from hets import DevelopmentGraph, DevGraphNode, DevGraphEdge, TheoremDevGraphEdge, EdgeKind, ConsistencyKind +from ..utils import get_variant +from .ExtendedDotWidget import ExtendedDotWidget + + +def node_shape(node: DevGraphNode) -> str: + """ + Get the shape of a node based on its type. + :param node: The node + :return: 'rectangle' or 'ellipse' + """ + + if node.is_reference_node(): + return "rectangle" + else: + return "ellipse" + + +def node_color(node: DevGraphNode) -> str: + """ + Get the color of a node based on its type. + :param node: The node + :return: The color + """ + if node.is_proven_node(): + if node.is_consistency_proven(): + return COLOR_MAP[("green", True, False)] + else: + return COLOR_MAP[("yellow", False, True)] + else: + return COLOR_MAP[("coral", False, node.is_consistency_proven())] + + +def edge_color(edge: DevGraphEdge) -> str: + """ + Get the color of an edge based on its type. + + :param edge: The edge + :return: The color + """ + + color: Tuple[str, bool] = ("fuchsia", True) # (color name, use variant) + if isinstance(edge, TheoremDevGraphEdge): + if not edge.is_proven(): + if edge.kind() == EdgeKind.LOCAL and not edge.is_homogeneous(): + color = ("coral", True) + # coral true + elif edge.kind() == EdgeKind.HIDING: + color = ("yellow", False) + else: + color = ("coral", False) + elif not edge.is_conservativ(): + if edge.kind() == EdgeKind.LOCAL and not edge.is_homogeneous(): + color = ("yellow", True) + else: + color = ("yellow", False) + elif edge.is_pending(): + if edge.kind() == EdgeKind.LOCAL and not edge.is_homogeneous(): + color = ("yellow", True) + else: + color = ("yellow", False) + else: + if edge.kind() == EdgeKind.LOCAL and not edge.is_homogeneous(): + color = ("green", True) + elif edge.kind() == EdgeKind.HIDING: + color = ("green", True) + else: + color = ("green", False) + + else: + color = { + EdgeKind.FREE: ("blue", False), + EdgeKind.COFREE: ("blue", False), + EdgeKind.HIDING: ("blue", False), + + # default + EdgeKind.LOCAL: ("black", False), + EdgeKind.GLOBAL: ("black", False), + + # error + EdgeKind.UNKNOWN: ("fuchsia", False) + }[edge.kind()] + + color_name, color_use_variant = color + color_use_light = not edge.is_inclusion() + + # Resolve the color map. + final_color = COLOR_MAP[(color_name, color_use_variant, color_use_light)] + + # Double lines for heterogeneous signature morphisms + if edge.is_homogeneous(): + return final_color + else: + return f"{final_color}:invis:{final_color}" + + +def edge_label(edge: DevGraphEdge) -> str: + """ + Get the label of an edge based on its type. + :param edge: + :return: + """ + + status = edge.conservativity_status() + cons_required = status.required() + cons_proven = status.proven() + if cons_required == cons_proven == ConsistencyKind.NONE or cons_required == ConsistencyKind.NONE and not status.is_proven_link(): + return "" + + if status.is_proven_link(): + return cons_proven.short_name() + else: + return cons_required.short_name() + "?" + + +def edge_style(edge: DevGraphEdge): + # Note: Double lines are created with a color list. See edge_color + if isinstance(edge, TheoremDevGraphEdge) and edge.kind() == EdgeKind.LOCAL: + return "dashed" + else: + return "" + + +class GraphvizGraphWidget(ExtendedDotWidget): + """ + An extended DotWidget that can render a DevelopmentGraph. + """ + + __gtype_name__ = "GraphvizGraphWidget" + + __gsignals__ = { + "render-start": (GObject.SIGNAL_RUN_FIRST, None, ()), + "render-end": (GObject.SIGNAL_RUN_FIRST, None, ()) + } + + _logger = logging.getLogger(__name__) + + g: Optional[Digraph] + """ The graphviz graph to be rendered. """ + + development_graph: Optional[DevelopmentGraph] + """ + The development graph to be visualised. + """ + + _render_thread: Optional[threading.Thread] + """ + The thread that calculates the dot code of the graph in the background. + """ + + # View properties + _show_internal_node_names: bool = False + _show_newly_added_proven_edges: bool = False + + def set_graph_direction(self, value: str): + self._render_graph_vertical = value + if self.graph: + self.render(False) + + def get_graph_direction(self) -> str: + return self._render_graph_vertical + + graph_direction = property(fset=set_graph_direction, fget=get_graph_direction) + """ + The direction the graph should be rendered (allowed values: 'vertical', 'horizontal'). + """ + + def __init__(self): + super().__init__() + self._render_thread = None + self.g = None + self.development_graph = None + self.graph_direction = "vertical" + + self._dot_code = None + + def do_node_right_click(self, node: str, event): + self._on_node_clicked(node, event) + + def do_edge_right_click(self, src: str, dest: str, event): + self._on_edge_clicked(src, dest, event) + + def load_graph(self, graph: DevelopmentGraph): + """ + Loads and renders a development graph. + + :param graph: The graph to be loaded + :return: + """ + + self._logger.debug("Loading graph") + self.development_graph = graph + self.g = Digraph("G") + + self.render(False) + + def show_internal_node_names(self): + """ + Show the names of internal nodes. + + .. seealso:: hide_internal_node_names + + :return: + """ + + self._logger.debug("Show internal nodes") + self._show_internal_node_names = True + self.render() + + def hide_internal_node_names(self): + """ + Hide the names of internal nodes. + + .. seealso:: show_internal_node_names + + :return: + """ + + self._logger.debug("Hide internal nodes") + self._show_internal_node_names = False + self.render() + + def show_newly_added_proven_edges(self): + """ + Show newly added proven edges. + + .. seealso:: hide_newly_added_proven_edges + + :return: + """ + + self._logger.debug("Show newly added proven edges") + self._show_newly_added_proven_edges = True + self.render() + + def hide_newly_added_proven_edges(self): + """ + Hide newly added proven edges. + + .. seealso:: show_newly_added_proven_edges + + :return: + """ + + self._logger.debug("Hide newly added proven edges") + self._show_newly_added_proven_edges = False + self.render() + + def _render(self, keep_zoom: bool) -> None: + """ + Renders the graph by computing the dot code and forwarding it to the xdot widget. + + Performs blocking operations and is intended to be executed in a background thread. + + :param keep_zoom: Whether to keep the current zoom level or reset it to 1 + :return: + """ + if not self.development_graph: + return + + self._logger.debug("Render graph; keep zoom: %s, direction: %s", keep_zoom, self.graph_direction) + + # Emit the render-start signal in the main thread + GLib.idle_add(lambda: self.emit("render-start")) + + # Build the graph ... + g = self.get_themed_graph() + g.graph_attr["rankdir"] = "LR" if self.graph_direction == "horizontal" else "TB" + + for node in self.development_graph.nodes(): + g.node(str(node.id()), + label="" if node.is_internal() and not self._show_internal_node_names else node.name(), + fillcolor=node_color(node), + shape=node_shape(node), + style="filled") + + for edge in self.development_graph.edges(): + if not self._show_newly_added_proven_edges \ + and isinstance(edge, TheoremDevGraphEdge) \ + and edge.is_proven() \ + and (edge.is_conservativ() or edge.kind() in [EdgeKind.HIDING, EdgeKind.FREE, EdgeKind.COFREE]): + continue + + g.edge(str(edge.origin()), str(edge.target()), + style=edge_style(edge), + color=edge_color(edge), + label=edge_label(edge), + fontcolor="grey") + + zoom_ration, x, y = self.zoom_ratio, self.x, self.y + dot_code = g.source + + if dot_code != self._dot_code: + # Call graphviz to create dot code + self.set_dotcode(dot_code.encode("utf-8")) + if keep_zoom: + self.zoom_ratio, self.x, self.y = zoom_ration, x, y + else: + self._logger.debug("Dot code did not change. Do not call graphviz") + + self.g = g + + # Emit the render-edn signal in the main thread + GLib.idle_add(lambda: self.emit("render-end")) + + def render(self, keep_zoom: bool = True) -> None: + """ + Renders the graph asynchrounsly in the background + + :param keep_zoom: Whether to keep the current zoom level or reset it to 1 + :return: + """ + + if self._render_thread and self._render_thread.is_alive(): + self._logger.debug("Already rendering. Waiting for previous render to finish") + self._render_thread.join() + + self._render_thread = threading.Thread(target=self._render, args=[keep_zoom]) + self._render_thread.start() + + def _menu_for_node(self, node_id: str) -> Gtk.Menu: + """ + Generate a context menu for a graph node + + :param node_id: The id of the node + :return: The context menu + """ + + menu = Gio.Menu() + + node = self.development_graph.node_by_id(int(node_id)) + if node is None: + self._logger.warning( + f"Trying to open a menu for node {node_id} but the node was not found in the development graph") + return menu + + menu_item_prove = Gio.MenuItem() + menu_item_prove.set_label("Prove") + menu_item_prove.set_action_and_target_value("win.node.prove", GLib.Variant.new_string(node_id)) + menu.append_item(menu_item_prove) + + menu_item_info = Gio.MenuItem() + menu_item_info.set_label("Show node info") + menu_item_info.set_action_and_target_value("win.node.show_info", GLib.Variant.new_string(node_id)) + menu.append_item(menu_item_info) + + menu_item_show_theory = Gio.MenuItem() + menu_item_show_theory.set_label("Show theory") + menu_item_show_theory.set_action_and_target_value("win.node.show_theory", GLib.Variant.new_string(node_id)) + menu.append_item(menu_item_show_theory) + + menu_translate = Gio.Menu() + menus_by_target = dict() + for c in node.global_theory().get_available_comorphisms(): + menu_item_comorphism = Gio.MenuItem() + menu_item_comorphism.set_label(f"{c.target()} ({c.name()})") + menu_item_comorphism.set_action_and_target_value("win.node.translate", get_variant((node_id, c.name()))) + + menu_comorphism = menus_by_target.setdefault(c.target(), Gio.Menu()) + menu_comorphism.append_item(menu_item_comorphism) + + for k, v in menus_by_target.items(): + menu_item_comorphism = Gio.MenuItem() + menu_item_comorphism.set_label(k) + menu_item_comorphism.set_submenu(v) + menu_translate.append_item(menu_item_comorphism) + + menu_item_translate = Gio.MenuItem() + menu_item_translate.set_label("Translate theory") + menu_item_translate.set_submenu(menu_translate) + menu.append_item(menu_item_translate) + + if node.is_reference_node(): + menu_item_open_ref = Gio.MenuItem() + menu_item_open_ref.set_label("Open " + node.name()) + menu_item_open_ref.set_action_and_target_value("win.open_win_for_lib_by_node", + GLib.Variant.new_int32(node.id())) + menu.append_item(menu_item_open_ref) + else: + menu_item_consistency = Gio.MenuItem() + menu_item_consistency.set_label("Check consistency") + menu_item_consistency.set_action_and_target_value("win.node.check_consistency", + GLib.Variant.new_string(node_id)) + menu.append_item(menu_item_consistency) + + return Gtk.Menu.new_from_model(menu) + + def _menu_for_edge(self, src_id: str, dst_id: str) -> Gtk.Menu: + """ + Generate a context menu for an edge + + :param src_id: id of the source node + :param dst_id: id of the target node + :return: The context menu + """ + + menu = Gio.Menu() + + menu_item_info = Gio.MenuItem() + menu_item_info.set_label("Show edge info") + menu_item_info.set_action_and_target_value("win.edge.show_info", get_variant((src_id, dst_id))) + menu.append_item(menu_item_info) + + menu_item_consistency = Gio.MenuItem() + menu_item_consistency.set_label("Check conservativity") + menu_item_consistency.set_action_and_target_value("win.edge.check_conservativity", + get_variant((src_id, dst_id))) + menu.append_item(menu_item_consistency) + + return Gtk.Menu.new_from_model(menu) + + def _on_node_clicked(self, node_id: str, event): + """ + Handler of the right-click event on a node. Shows a context menu. + + :param node_id: id of the clicked node + :param event: the event + :return: + """ + + menu = self._menu_for_node(node_id) + menu.attach_to_widget(self) + menu.show_all() + menu.popup(None, None, None, None, event.button, event.time) + + def _on_edge_clicked(self, src_id: str, dst_id: str, event): + """ + Handler of the right-click event on an edge. Shows a context menu. + :param src_id: id of the source node + :param dst_id: id of the target node + :param event: the event + :return: + """ + + menu = self._menu_for_edge(src_id, dst_id) + menu.attach_to_widget(self) + menu.show_all() + menu.popup(None, None, None, None, event.button, event.time) + + def on_area_motion_notify(self, area, event): + # To be overwritten in the future ... + return super().on_area_motion_notify(area, event) + + def set_highlight(self, items, search=False): + # To be overwritten in the future ... + super().set_highlight(items, search) diff --git a/python/gui/src/hetsgui/widgets/GridWithToolComorphismSelector.py b/python/gui/src/hetsgui/widgets/GridWithToolComorphismSelector.py new file mode 100644 index 0000000000..d89b7fce1f --- /dev/null +++ b/python/gui/src/hetsgui/widgets/GridWithToolComorphismSelector.py @@ -0,0 +1,170 @@ +import typing + +from gi.repository import Gtk, GObject + +from hets import Comorphism, ConsistencyChecker, Prover, Theory +from ..GtkSmartTemplate import GtkSmartTemplate + + +@GtkSmartTemplate +class GridWithToolComorphismSelector(Gtk.Grid): + """ + A grid that contains a combobox for selecting a tool (prover or consistency checker) and a combobox for selecting a comorphism. + + This is a grid to couple the functionality of selecting a tool and a comorphism while allowing other UI elements to be placed in the same grid as well. + """ + + __gtype_name__ = "GridWithToolComorphismSelector" + + # Models for the UI elements + _tool_model: Gtk.ListStore = Gtk.Template.Child() + _comorphism_model: Gtk.ListStore = Gtk.Template.Child() + _comorphism_filtered: Gtk.TreeModelFilter = Gtk.Template.Child() + + # UI elements + _combo_comorphism: Gtk.ComboBox = Gtk.Template.Child() + _combo_tool: Gtk.ComboBox = Gtk.Template.Child() + _lbl_tool: Gtk.Label = Gtk.Template.Child() + _lbl_comorphism: Gtk.Label = Gtk.Template.Child() + + theory: Theory = GObject.Property() + """ The theory to use for selecting tools and comorphisms. """ + + use_consistency_checkers: bool = GObject.Property(type=bool, default=False) + """ Whether to use consistency checkers instead of provers. """ + + start_top: int = GObject.Property(type=int, default=0) + """ The top position of the first tool UI element in the grid. """ + + start_left: int = GObject.Property(type=int, default=0) + """ The left position of the first tool UI element in the grid. """ + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + # Connect change handlers + self.connect('notify::theory', self._update) + self.connect('notify::use-consistency-checkers', self._update) + self.connect('notify::start-top', self._reorganize_widgets) + self.connect('notify::start-left', self._reorganize_widgets) + + def _reorganize_widgets(self, *args): + """ + Reorganize the widgets in the grid according to the start_top and start_left properties. + + :param args: ignored + :return: + """ + self.child_set_property(self._lbl_tool, "top-attach", self.start_top) + self.child_set_property(self._combo_tool, "top-attach", self.start_top) + self.child_set_property(self._lbl_comorphism, "top-attach", self.start_top + 1) + self.child_set_property(self._combo_comorphism, "top-attach", self.start_top + 1) + + self.child_set_property(self._lbl_tool, "left-attach", self.start_left) + self.child_set_property(self._combo_tool, "left-attach", self.start_left + 1) + self.child_set_property(self._lbl_comorphism, "left-attach", self.start_left) + self.child_set_property(self._combo_comorphism, "left-attach", self.start_left + 1) + + def _update(self, *args): + """ + Update the models for the comboboxes when the theory or use_consistency_checkers property changes. + + :param args: + :return: + """ + + if self.theory is None: + return + + self._lbl_tool.set_label("Consistency checker:" if self.use_consistency_checkers else "Prover:") + + # Clear previous entries + self._tool_model.clear() + self._comorphism_model.clear() + + # Add provers and comorphisms to their respective models for display in combo boxes + tool_with_comorphism = self.theory.get_usable_consistency_checkers_with_comorphisms() if self.use_consistency_checkers else self.theory.get_usable_provers_with_comorphisms() + for tool, comorphisms in tool_with_comorphism.items(): + shortest_comorphism_path_len = 100 + for comorphism in comorphisms: + comorphism_path_length = comorphism.path_length() + if comorphism_path_length < shortest_comorphism_path_len: + shortest_comorphism_path_len = comorphism_path_length + + self._comorphism_model.append( + [comorphism.name(), comorphism.name(), tool.name(), comorphism_path_length]) + + shortest_comorphism_path_len = min( + c.path_length() for c in comorphisms) + self._tool_model.append( + [tool.name(), tool.name(), shortest_comorphism_path_len]) + + self._comorphism_filtered.set_visible_func(self._comorphism_filter) + self._combo_tool.set_active(0) + + @GObject.Property() + def selected_comorphism(self) -> typing.Optional[Comorphism]: + """ + The selected comorphism or None if no comorphism is selected. + :return: The comorphism or None + """ + comorphism_model = self._combo_comorphism.get_model() + comorphism_index = self._combo_comorphism.get_active() + comorphism_name = comorphism_model[comorphism_index][0] if comorphism_index >= 0 else None + comorphism = None if comorphism_name is None else next( + c for c in self.theory.get_available_comorphisms() if c.name() == comorphism_name) + return comorphism + + @GObject.Property() + def selected_prover(self) -> typing.Optional[Prover]: + """ + The selected prover or None if no prover is selected. + :return: The prover or None + """ + prover_model = self._combo_tool.get_model() + prover_index = self._combo_tool.get_active() + prover_name = prover_model[prover_index][0] if prover_index >= 0 else None + prover = self.theory.get_prover_by_name(prover_name) + return prover + + @GObject.Property() + def selected_consistency_checker(self) -> typing.Optional[ConsistencyChecker]: + """ + The selected consistency checker or None if no consistency checker is selected. + :return: The consistency checker or None + """ + prover_model = self._combo_tool.get_model() + prover_index = self._combo_tool.get_active() + cc_name = prover_model[prover_index][0] if prover_index >= 0 else None + cc = self.theory.get_consistency_checker_by_name(cc_name) + return cc + + def _comorphism_filter(self, model: Gtk.TreeModelFilter, path: Gtk.TreeIter, data): + """ + Filter function for the comorphism model to only show comorphisms that are applicable to the selected tool. + + :param model: Model of the comorphism combo box + :param path: Path in the model + :param data: ignored + :return: + """ + prover_model = self._combo_tool.get_model() + active_prover_iter = self._combo_tool.get_active_iter() + + if active_prover_iter is not None: + prover_name = prover_model[active_prover_iter][0] + return model[path][2] == prover_name + else: + return False + + @Gtk.Template.Callback() + def update_comorphisms(self, *args): + """ + Update the comorphism model when the selected tool changes. + + :param args: ignored + :return: + """ + self._comorphism_filtered.refilter() + if len(self._comorphism_filtered) > 0: + self._combo_comorphism.set_active(0) diff --git a/python/gui/src/hetsgui/widgets/GridWithToolComorphismSelector.ui b/python/gui/src/hetsgui/widgets/GridWithToolComorphismSelector.ui new file mode 100644 index 0000000000..a7eb2fee2e --- /dev/null +++ b/python/gui/src/hetsgui/widgets/GridWithToolComorphismSelector.ui @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + _comorphism_model + + + diff --git a/python/gui/src/hetsgui/widgets/NodeInfoDialog.py b/python/gui/src/hetsgui/widgets/NodeInfoDialog.py new file mode 100644 index 0000000000..adea4e5644 --- /dev/null +++ b/python/gui/src/hetsgui/widgets/NodeInfoDialog.py @@ -0,0 +1,32 @@ +from gi.repository import Gtk + +from hets import DevGraphNode + + +class NodeInfoDialog(Gtk.Dialog): + """ + Dialog to show information about a DevGraphNode. + """ + def __init__(self, node: DevGraphNode): + super().__init__() + + self.set_default_size(800, 600) + + title = "{0}node {1} {2}".format( + ("reference " if node.is_reference_node() else "internal " if node.is_internal() else ""), + node.name(), + node.id()) + self.set_title(title) + + box = self.get_content_area() + scrolled_window = Gtk.ScrolledWindow(expand=True) + + text_view = Gtk.TextView() + text_view.set_property('editable', False) + text_view.set_property('monospace', True) + text_buffer = text_view.get_buffer() + text_buffer.set_text(title + "\n" + node.info()) + + scrolled_window.add(text_view) + box.add(scrolled_window) + self.show_all() diff --git a/python/gui/src/hetsgui/widgets/ProofDetail.css b/python/gui/src/hetsgui/widgets/ProofDetail.css new file mode 100644 index 0000000000..e5841412c4 --- /dev/null +++ b/python/gui/src/hetsgui/widgets/ProofDetail.css @@ -0,0 +1,12 @@ +.axiom { + background: #00BBFF; +} + +.proof-tree { + background: white; +} + +.proof-lines { + font-family: monospace; + background: @theme_base_color; +} diff --git a/python/gui/src/hetsgui/widgets/ProofDetail.py b/python/gui/src/hetsgui/widgets/ProofDetail.py new file mode 100644 index 0000000000..278fd24aab --- /dev/null +++ b/python/gui/src/hetsgui/widgets/ProofDetail.py @@ -0,0 +1,189 @@ +import re +import typing + +from gi.repository import Gtk, GObject, Gio + +from ..GtkSmartTemplate import GtkSmartTemplate +from ..formatting.colors import PROOF_KIND_FG_COLORS +from ..widgets import ExtendedDotWidget + + +class AxiomModel(GObject.GObject): + """ + A model for axioms in the proof detail widget. + """ + sentence: typing.Optional[str] + name: str + + def __init__(self, name: str, sentence: typing.Optional[str]): + GObject.GObject.__init__(self) + self.name = name + self.sentence = sentence + + +@GtkSmartTemplate +class ProofDetail(Gtk.Bin): + """ + A widget to display details of a proof. + """ + __gtype_name__ = "ProofDetail" + + proof = GObject.Property(type=object) + """ The proof to display. """ + comorphism = GObject.Property(type=object) + """ The comorphism used in the proof. """ + theory = GObject.Property(type=object) + """ The examined theory. """ + + # UI elements + _lbl_title: Gtk.Label = Gtk.Template.Child() + _lbl_prover: Gtk.Label = Gtk.Template.Child() + _lbl_comorphism: Gtk.Label = Gtk.Template.Child() + _lbl_used_time: Gtk.Label = Gtk.Template.Child() + _lbl_status: Gtk.Label = Gtk.Template.Child() + _lbl_proof_lines: Gtk.Label = Gtk.Template.Child() + + _dot_proof_tree: ExtendedDotWidget = Gtk.Template.Child() + + _lbl_ts_time: Gtk.Label = Gtk.Template.Child() + _lbl_ts_time_value: Gtk.Label = Gtk.Template.Child() + _lbl_ts_opts: Gtk.Label = Gtk.Template.Child() + _lbl_ts_opts_value: Gtk.Label = Gtk.Template.Child() + _lbl_ts: Gtk.Label = Gtk.Template.Child() + _lbl_ts_value: Gtk.Label = Gtk.Template.Child() + + _box_axioms: Gtk.FlowBox = Gtk.Template.Child() + + # Model for the axioms + _model_axioms: Gio.ListStore + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + self._model_axioms = Gio.ListStore.new(AxiomModel) + + # Connect change handlers + self.connect("notify::comorphism", self.on_comorphism_changed) + self.connect("notify::proof", self.on_proof_changed) + self.connect("notify::theory", self.update_axioms) + + self._box_axioms.bind_model(self._model_axioms, self.create_axiom_widget) + + def create_axiom_widget(self, item: AxiomModel, *args): + """ + Creates a widget for an axiom. + + :param item: The axiom (model) to create a widget for. + :param args: + :return: + """ + label = Gtk.Label(label=item.name, tooltip_text=str(item.sentence) if item.sentence else item.name) + label.get_style_context().add_class("axiom") + return label + + def on_proof_changed(self, widget, param): + """ + Updates the widget when the proof changes. + + :param widget: ignored + :param param: ignored + :return: + """ + + self._lbl_prover.set_label(self.proof.details().used_prover()) + self._lbl_prover.set_tooltip_text(self.proof.details().used_prover()) + self._lbl_status.set_markup( + f"{self.proof.kind().to_str()}") + self._lbl_used_time.set_label(str(self.proof.details().used_time())) + + proof_lines = self.proof.details().proof_lines() + self._lbl_proof_lines.set_text("\n".join(proof_lines)) + self._lbl_proof_lines.set_lines(len(proof_lines)) + + self._dot_proof_tree.dotcode = self.proof.details().proof_tree() + + self.update_tactic_script() + self.update_axioms() + self.update_title() + + def on_comorphism_changed(self, widget, param): + """ + Updates the widget when the comorphism changes. + :param widget: ignored + :param param: ignored + :return: + """ + self._lbl_comorphism.set_label(self.comorphism.name()) + self._lbl_comorphism.set_tooltip_text(self.comorphism.name()) + + self.update_title() + + def update_title(self): + """ + Updates the title label with the proof kind and the used prover and comorphism. + :return: + """ + if not (self.comorphism and self.proof): + return + + color = PROOF_KIND_FG_COLORS[self.proof.kind()] + + self._lbl_title.set_markup( + f"{self.proof.kind().to_str()} with {self.proof.details().used_prover()} via {self.comorphism.name()}") + + def update_axioms(self, *args): + if not (self.proof and self.theory): + return + + self._model_axioms.remove_all() + for axiom in self.proof.details().used_axioms(): + sentence = self.theory.sentence_by_name(axiom) + self._model_axioms.append(AxiomModel(axiom, sentence)) + + def update_tactic_script(self): + """ + Updates the tactic script labels. + :return: + """ + + ts_regex = [ + re.compile(r"^ATPTacticScript \{tsTimeLimit = (\d+), tsExtraOpts = \[(.*)\]\}$"), + re.compile(r"^TacticScript \{timeLimit = (\d+), extraOptions = \[(.*)\]\}$") + ] + ts_time, ts_opts = None, None + + tactic_script = self.proof.details().tactic_script() + for regex in ts_regex: + match = regex.match(tactic_script) + if match: + ts_time = match.group(1) + ts_opts_list = match.group(2) + list_regex = re.compile(r"\"((?:\\\"|[^\"])*)\"") + ts_opts = [] + for match in list_regex.finditer(ts_opts_list): + ts_opts.append(match.group(1)) + + ts_opts = " ".join(ts_opts) + + break + + if ts_time is None and ts_opts is None: + self._lbl_ts_time.set_visible(False) + self._lbl_ts_time_value.set_visible(False) + self._lbl_ts_opts.set_visible(False) + self._lbl_ts_opts_value.set_visible(False) + self._lbl_ts.set_visible(True) + self._lbl_ts_value.set_visible(True) + + self._lbl_ts_value.set_label(tactic_script) + + else: + self._lbl_ts_time.set_visible(True) + self._lbl_ts_time_value.set_visible(True) + self._lbl_ts_opts.set_visible(True) + self._lbl_ts_opts_value.set_visible(True) + self._lbl_ts.set_visible(False) + self._lbl_ts_value.set_visible(False) + + self._lbl_ts_time_value.set_label(f"{ts_time}s") + self._lbl_ts_opts_value.set_label(ts_opts) diff --git a/python/gui/src/hetsgui/widgets/ProofDetail.ui b/python/gui/src/hetsgui/widgets/ProofDetail.ui new file mode 100644 index 0000000000..eb9d4ce75b --- /dev/null +++ b/python/gui/src/hetsgui/widgets/ProofDetail.ui @@ -0,0 +1,293 @@ + + + + + + + + + + + diff --git a/python/gui/src/hetsgui/widgets/SelectableTreeView.py b/python/gui/src/hetsgui/widgets/SelectableTreeView.py new file mode 100644 index 0000000000..f1ab1c2ede --- /dev/null +++ b/python/gui/src/hetsgui/widgets/SelectableTreeView.py @@ -0,0 +1,52 @@ +from gi.repository import Gtk, GObject +from gi.repository.Gtk import TreeViewColumn, CellRendererToggle, CheckButton + +from ..actions.model import toggle_tree_view_cell_handler, toggle_tree_view_header_cell_handler + + +class SelectableTreeView(Gtk.TreeView): + """ + A tree view with a selectable header cell. + """ + + # UI elements + _header_switch: CheckButton + _cell_renderer: CellRendererToggle + _column: TreeViewColumn + + # Backing field + _selected_column: int + + __gtype_name__ = "SelectableTreeView" + + @GObject.Property(type=int, default=0) + def selected_column(self) -> int: + """ + The currently selected column. + :return: The index of the selected column. + """ + return self._selected_column + + @selected_column.setter + def selected_column(self, cid: int): + self._selected_column = cid + self._column.add_attribute(self._cell_renderer, "active", cid) + + def __init__(self, **kwargs): + Gtk.TreeView.__init__(self) + + self._selected_column = 0 + + self._column = Gtk.TreeViewColumn() + self._column.set_clickable(True) + + self._cell_renderer = Gtk.CellRendererToggle() + self._cell_renderer.connect_object("toggled", toggle_tree_view_cell_handler, self._column) + self._column.pack_start(self._cell_renderer, False) + + self._header_switch = Gtk.CheckButton(active=True) + self._header_switch.show_all() + self._column.set_widget(self._header_switch) + self._column.connect("clicked", toggle_tree_view_header_cell_handler) + + self.insert_column(self._column, 0) diff --git a/python/gui/src/hetsgui/widgets/TheoryInfoDialog.py b/python/gui/src/hetsgui/widgets/TheoryInfoDialog.py new file mode 100644 index 0000000000..7a1153ebc1 --- /dev/null +++ b/python/gui/src/hetsgui/widgets/TheoryInfoDialog.py @@ -0,0 +1,81 @@ +import logging + +from gi.repository import Gtk + +from hets import DevGraphNode, Theory + +INFO_DIALOG_SAVE_RESPONSE = 0x0001_0001 +""" Magic constant for the response code for saving the theory to a file. """ + + +class TheoryInfoDialog(Gtk.Dialog): + """ + Dialog to show a theory and possibly save it to a file. + """ + + _logger = logging.getLogger(__name__) + _node: DevGraphNode + + def __init__(self, theory: Theory, name: str): + super().__init__() + + self.set_default_size(800, 600) + + self.set_title(f"Theory {name}") + + self._theory = theory + self._name = name + + box = self.get_content_area() + scrolled_window = Gtk.ScrolledWindow(expand=True) + + text_view = Gtk.TextView() + text_view.set_property('editable', False) + text_view.set_property('monospace', True) + text_buffer = text_view.get_buffer() + text_buffer.set_text(str(self._theory)) + + # Add save button + button_box = self.get_action_area() + button_box.set_property("margin", 10) + self.add_button("Save", INFO_DIALOG_SAVE_RESPONSE) + + scrolled_window.add(text_view) + box.add(scrolled_window) + self.show_all() + + # Connect response signal + self.connect("response", self._on_response) + + def _on_response(self, widget: Gtk.Widget, response: int): + if response == INFO_DIALOG_SAVE_RESPONSE: + # Propagate submission of the signal if the save button was clicked to prevent the window from closing + self.stop_emission("response") + + dialog = Gtk.FileChooserDialog(title="Please choose a file", action=Gtk.FileChooserAction.SAVE) + dialog.add_buttons("Cancel", Gtk.ResponseType.CANCEL, + "Save", Gtk.ResponseType.OK) + + # Theories are exported as .dol files + dialog.set_current_name(self._name + ".dol") + + filter_dol = Gtk.FileFilter() + filter_dol.set_name("DOL files") + filter_dol.add_pattern("*.dol") + dialog.add_filter(filter_dol) + + filter_any = Gtk.FileFilter() + filter_any.set_name("Any files") + filter_any.add_pattern("*") + dialog.add_filter(filter_any) + + response = dialog.run() + + if response == Gtk.ResponseType.OK: + filename = dialog.get_filename() + self._logger.debug("File selected: " + filename) + + with open(filename, "w") as f: + f.write(str(self._theory)) + + dialog.destroy() diff --git a/python/gui/src/hetsgui/widgets/__init__.py b/python/gui/src/hetsgui/widgets/__init__.py new file mode 100644 index 0000000000..228eb60ca1 --- /dev/null +++ b/python/gui/src/hetsgui/widgets/__init__.py @@ -0,0 +1,21 @@ +""" +This module contains custom widgets used in the GUI. +""" + +# For debugging purposes don't load hyphen. It takes too long for a short test of the UI +try: + import hyphen + + from .EdgeInfoDialog import EdgeInfoDialog + from .NodeInfoDialog import NodeInfoDialog + from .GraphvizGraphWidget import GraphvizGraphWidget + from .NodeInfoDialog import NodeInfoDialog + from .ProofDetail import ProofDetail + from .GridWithToolComorphismSelector import GridWithToolComorphismSelector +except: + pass + +from .CellRendererLink import CellRendererLink +from .SelectableTreeView import SelectableTreeView +from .ExtendedDotWidget import ExtendedDotWidget +from .EditableListView import EditableListView diff --git a/python/gui/src/hetsgui/windows/ConservativityCheckWindow.css b/python/gui/src/hetsgui/windows/ConservativityCheckWindow.css new file mode 100644 index 0000000000..37c7809ad1 --- /dev/null +++ b/python/gui/src/hetsgui/windows/ConservativityCheckWindow.css @@ -0,0 +1,10 @@ +.consistency-kind-inconsistent { background: red; color: white} +.consistency-kind-unknown { background: black; color: white} +.consistency-kind-pcons { background: darkgreen; color: white} +.consistency-kind-cons { background: green; color: white} +.consistency-kind-mono { background: violet; color: white} +.consistency-kind-defined { background: seagreen; color: white} +.consistency-kind-timed_out { background: blue; color: white} +.consistency-kind-error { background: darkred; color: white} + +.cc-output { border: 1px solid @borders; background: @theme_base_color; } diff --git a/python/gui/src/hetsgui/windows/ConservativityCheckWindow.py b/python/gui/src/hetsgui/windows/ConservativityCheckWindow.py new file mode 100644 index 0000000000..e5abd85a4b --- /dev/null +++ b/python/gui/src/hetsgui/windows/ConservativityCheckWindow.py @@ -0,0 +1,181 @@ +import logging +import threading +import traceback +import typing +from threading import Thread + +from gi.repository import Gtk, GLib + +from hets import DevGraphEdge, ConservativityChecker, ConsistencyKind +from ..GtkSmartTemplate import GtkSmartTemplate + + +@GtkSmartTemplate +class ConservativityCheckWindow(Gtk.Window): + """ + A window to check the conservativity of a DevGraphEdge. + """ + _edge: DevGraphEdge + _checking_thread: typing.Optional[Thread] + + __gtype_name__ = 'ConservativityCheckWindow' + _logger = logging.getLogger(__name__) + + # UI elements + _btn_check: Gtk.Button = Gtk.Template.Child() + _combo_checker: Gtk.ComboBox = Gtk.Template.Child() + _lbl_status: Gtk.Label = Gtk.Template.Child() + _lbl_output: Gtk.Label = Gtk.Template.Child() + + # Models + _checker_model: Gtk.ListStore = Gtk.Template.Child() + + @property + def selected_conservativity_checker(self) -> typing.Optional[ConservativityChecker]: + """ + The selected conservativity checker or None if no checker is selected. + :return: + """ + active_index = self._combo_checker.get_active() + cc_name = self._checker_model[active_index][0] if active_index >= 0 else None + cc = self._edge.get_conservativity_checker_by_name(cc_name) if cc_name else None + return cc + + def __init__(self, edge: DevGraphEdge, **kwargs): + super().__init__(title=f"Check conservativity of {edge.title()}", **kwargs) + + self._checking_thread = None + self._edge = edge + + self._update_status(edge.conservativity()) + + # Build conservativity checkers model + ccs = self._edge.get_usable_conservativity_checkers() + for cc in ccs: + self._checker_model.append([cc.name(), cc.name(), 0]) + + if len(self._checker_model) > 0: + self._combo_checker.set_active(0) + + @Gtk.Template.Callback() + def on_check_clicked(self, *args): + """ + Called when the user clicks the check button. Starts the conservativity check in a separate background thread. + :param args: ignored + :return: + """ + + if self._checking_thread is not None and self._checking_thread.is_alive(): + self._logger.warning("Conservativity check is already running. Waiting for it to finish.") + self._checking_thread.join() + + self._checking_thread = threading.Thread(target=self._check_consistency) + self._checking_thread.start() + + def _update_status(self, status: typing.Union[ConsistencyKind, str]): + """ + Update the status label with the given status. + :param status: The status to display. + :return: + """ + + # Remove the previous status class + style_context = self._lbl_status.get_style_context() + style_context.remove_class("consistency-kind-inconsistent") + style_context.remove_class("consistency-kind-unknown") + style_context.remove_class("consistency-kind-pcons") + style_context.remove_class("consistency-kind-cons") + style_context.remove_class("consistency-kind-mono") + style_context.remove_class("consistency-kind-defined") + style_context.remove_class("consistency-kind-timed_out") + style_context.remove_class("consistency-kind-error") + + if isinstance(status, ConsistencyKind): + # Add the new status class + style_context.add_class(f"consistency-kind-{status.name.lower()}") + self._lbl_status.set_markup(f'{status.to_str()}') + else: + self._lbl_status.set_markup(status) + + def _check_consistency(self): + """ + Check the conservativity of the edge. + + This function is blocking and is designed to be run in a background thread. + :return: + """ + + edge = self._edge + + # Update the UI in the main thread + GLib.idle_add(self._init_checking_progress) + + try: + conservativity_checker = self.selected_conservativity_checker + + self._logger.info("Checking conservativity on %s, checker: %s", edge.title(), + conservativity_checker.name()) + + status, explanations, obligations, diagnosis = edge.check_conservativity(conservativity_checker) + + self._logger.info("Conservativity check result for %s: %s", edge.title(), status) + + if status is None: + status = ConsistencyKind.UNKNOWN + message = "\n".join(diagnosis) + else: + if explanations: + self._logger.debug("Conservativity check explained by sentences: %s", edge.title(), + ", ".join(explanations)) + if obligations: + self._logger.debug("Conservativity check has open proof obligations: %s", edge.title(), + ", ".join(obligations)) + + # Build a message for the user + message = f"The link is {status.to_str()}" + if obligations: + message += " provided that the following obligations hold in an imported theory:\n" + message += ", ".join(obligations) + elif explanations: + message += " because of the following axioms:\n" + message += ", ".join(explanations) + + message += "\n" + "\n".join(diagnosis) + except BaseException as e: + self._logger.warning("Conservativity check for %s failed: %s", edge.title(), traceback.format_exc()) + + status = ConsistencyKind.ERROR + message = str(e) + + # Update the UI in the main thread + GLib.idle_add(self._finish_checking_progress, status, message) + + def _finish_checking_progress(self, status: ConsistencyKind, message: str): + """ + Called when the consistency check has finished. Updates the UI accordingly. + :param status: The resulting status of the check. + :param message: The message to display. + :return: + """ + + self._btn_check.set_sensitive(True) + self._update_status(status) + + message = message.strip() + if len(message) > 0: + self._lbl_output.set_lines(len(message.split("\n"))) + self._lbl_output.set_text(message) + self._lbl_output.set_halign(Gtk.Align.START) + else: + self._lbl_output.set_lines(1) + self._lbl_output.set_markup("No output") + self._lbl_output.set_halign(Gtk.Align.CENTER) + + def _init_checking_progress(self): + """ + Called when the consistency check starts. Updates the UI accordingly. + :return: + """ + + self._btn_check.set_sensitive(False) + self._update_status("Checking") diff --git a/python/gui/src/hetsgui/windows/ConservativityCheckWindow.ui b/python/gui/src/hetsgui/windows/ConservativityCheckWindow.ui new file mode 100644 index 0000000000..7e17fd12af --- /dev/null +++ b/python/gui/src/hetsgui/windows/ConservativityCheckWindow.ui @@ -0,0 +1,133 @@ + + + + + + + + + + + + + diff --git a/python/gui/src/hetsgui/windows/ConsistencyCheckWindow.css b/python/gui/src/hetsgui/windows/ConsistencyCheckWindow.css new file mode 100644 index 0000000000..37c7809ad1 --- /dev/null +++ b/python/gui/src/hetsgui/windows/ConsistencyCheckWindow.css @@ -0,0 +1,10 @@ +.consistency-kind-inconsistent { background: red; color: white} +.consistency-kind-unknown { background: black; color: white} +.consistency-kind-pcons { background: darkgreen; color: white} +.consistency-kind-cons { background: green; color: white} +.consistency-kind-mono { background: violet; color: white} +.consistency-kind-defined { background: seagreen; color: white} +.consistency-kind-timed_out { background: blue; color: white} +.consistency-kind-error { background: darkred; color: white} + +.cc-output { border: 1px solid @borders; background: @theme_base_color; } diff --git a/python/gui/src/hetsgui/windows/ConsistencyCheckWindow.py b/python/gui/src/hetsgui/windows/ConsistencyCheckWindow.py new file mode 100644 index 0000000000..16edea3be8 --- /dev/null +++ b/python/gui/src/hetsgui/windows/ConsistencyCheckWindow.py @@ -0,0 +1,133 @@ +import logging +import threading +import traceback +import typing + +from gi.repository import Gtk, GLib + +from ..GtkSmartTemplate import GtkSmartTemplate +from hets import DevGraphNode, ConsistencyChecker, Comorphism, ConsistencyKind +from ..widgets import GridWithToolComorphismSelector + + +@GtkSmartTemplate +class ConsistencyCheckWindow(Gtk.Window): + """ + A window to check the consistency of a DevGraphNode. + """ + _node: DevGraphNode + _checking_thread: None + + __gtype_name__ = 'ConsistencyCheckWindow' + _logger = logging.getLogger(__name__) + + # UI elements + _consistency_checker_comorphism_selector: GridWithToolComorphismSelector = Gtk.Template.Child() + _btn_check: Gtk.Button = Gtk.Template.Child() + + _switch_include_proven_theorems: Gtk.Switch = Gtk.Template.Child() + _txt_timeout: Gtk.SpinButton = Gtk.Template.Child() + _lbl_status: Gtk.Label = Gtk.Template.Child() + _lbl_output: Gtk.Label = Gtk.Template.Child() + + @property + def selected_comorphism(self) -> typing.Optional[Comorphism]: + """ + The selected comorphism or None if no comorphism is selected. + :return: + """ + + return self._consistency_checker_comorphism_selector.selected_comorphism + + @property + def selected_consistency_checker(self) -> typing.Optional[ConsistencyChecker]: + """ + The selected consistency checker or None if no checker is selected. + :return: + """ + return self._consistency_checker_comorphism_selector.selected_consistency_checker + + def __init__(self, node: DevGraphNode, **kwargs): + super().__init__(title=f"Check consistency of {node.name()}", **kwargs) + + self._checking_thread = None + self._consistency_checker_comorphism_selector.theory = node.global_theory() + self._node = node + + self._update_status(node.consistency_status().proven()) + + @Gtk.Template.Callback() + def on_check_clicked(self, *args): + """ + Callback for the check button. Starts the consistency check in a separate background thread. + :param args: ignored + :return: + """ + self._checking_thread = threading.Thread(target=self._check_consistency) + self._checking_thread.start() + + def _update_status(self, status: typing.Union[ConsistencyKind, str]): + style_context = self._lbl_status.get_style_context() + style_context.remove_class("consistency-kind-inconsistent") + style_context.remove_class("consistency-kind-unknown") + style_context.remove_class("consistency-kind-pcons") + style_context.remove_class("consistency-kind-cons") + style_context.remove_class("consistency-kind-mono") + style_context.remove_class("consistency-kind-defined") + style_context.remove_class("consistency-kind-timed_out") + style_context.remove_class("consistency-kind-error") + + if isinstance(status, ConsistencyKind): + style_context.add_class(f"consistency-kind-{status.name.lower()}") + self._lbl_status.set_markup(f'{status.to_str()}') + else: + self._lbl_status.set_markup(status) + + def _check_consistency(self): + GLib.idle_add(self._init_checking_progress) + + try: + consistency_checker = self.selected_consistency_checker + comorphism = self.selected_comorphism + timeout = self._txt_timeout.get_value_as_int() + include_theorems = self._switch_include_proven_theorems.get_active() + + self._logger.info( + "Checking consistency on %s, checker: %s, comorphism: %s, timeout: %s, include_theorems: %s", + self._node.name(), consistency_checker.name(), comorphism.name(), timeout, include_theorems) + + status, message = self._node.check_consistency(consistency_checker, comorphism, include_theorems, timeout) + + self._logger.info("Consistency result for %s: %s", self._node.name(), status) + self._logger.debug("Consistency check message for %s: %s", self._node.name(), message) + except Exception as e: + self._logger.warning("Consistency check for %s failed: %s", self._node.name(), traceback.format_exc()) + + dialog = Gtk.MessageDialog(transient_for=self, flags=0, message_type=Gtk.MessageType.ERROR, + buttons=Gtk.ButtonsType.CLOSE, text=f"Check failed!") + dialog.format_secondary_text(f"Check the console for more details.\nError message: {str(e)}") + dialog.run() + dialog.destroy() + + status = ConsistencyKind.ERROR + message = str(e) + + GLib.idle_add(self._finish_checking_progress, status, message) + + def _finish_checking_progress(self, status: ConsistencyKind, message: str): + self._btn_check.set_sensitive(True) + self._update_status(status) + + message = message.strip() + if len(message) > 0: + self._lbl_output.set_lines(len(message.split("\n"))) + self._lbl_output.set_text(message) + self._lbl_output.set_halign(Gtk.Align.START) + else: + self._lbl_output.set_lines(1) + self._lbl_output.set_markup("No output") + self._lbl_output.set_halign(Gtk.Align.CENTER) + + def _init_checking_progress(self): + self._btn_check.set_sensitive(False) + self._update_status("Checking") diff --git a/python/gui/src/hetsgui/windows/ConsistencyCheckWindow.ui b/python/gui/src/hetsgui/windows/ConsistencyCheckWindow.ui new file mode 100644 index 0000000000..6b608c81e2 --- /dev/null +++ b/python/gui/src/hetsgui/windows/ConsistencyCheckWindow.ui @@ -0,0 +1,139 @@ + + + + + 4294967295 + 1 + 10 + + + diff --git a/python/gui/src/hetsgui/windows/LibrarySettingsWindow.py b/python/gui/src/hetsgui/windows/LibrarySettingsWindow.py new file mode 100644 index 0000000000..fe18721dce --- /dev/null +++ b/python/gui/src/hetsgui/windows/LibrarySettingsWindow.py @@ -0,0 +1,152 @@ +import logging +import typing +from typing import Dict, Any + +from gi.repository import Gtk, GObject + +from hets import Options +from ..GtkSmartTemplate import GtkSmartTemplate +from ..widgets import EditableListView + + +def natural_case(s: str) -> str: + return s.capitalize().replace("_", " ") + + +@GtkSmartTemplate +class LibrarySettingsWindow(Gtk.Window): + """ + A generic window to edit the library settings. + """ + _widgets: dict[str, Gtk.Widget] + _next_row: int + + __gtype_name__ = "LibrarySettingsWindow" + + __gsignals__ = {"apply-settings": (GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,))} + _logger = logging.getLogger(__name__) + + _grid: Gtk.Grid = Gtk.Template.Child() + + def __init__(self, settings: typing.Optional[Options] = None, **kwargs): + super().__init__(**kwargs) + + self._next_row = 0 + self._widgets = {} + + if settings is None: + settings = Options() + + self._settings = settings + + # Add fields for each option dynamically + for option in sorted(list(settings), key=lambda o: o.name): + name, typ = option.name, option.typ + if typ == bool: + self._add_bool_field(name) + elif typ == str: + self.add_string_field(name) + elif typ == int: + self.add_int_field(name) + elif isinstance(typ, list): + self.add_list_field(name, typ[0]) + else: + self._logger.warning(f"Field '%s' has unknown type '%s'", name, typ) + + def _add_row(self, field_name: str, widget: Gtk.Widget): + """ + Helper function to add a row to the grid with a label and a widget. + + :param field_name: The name of the field. + :param widget: The widget to add. + :return: + """ + label = Gtk.Label(label=natural_case(field_name), halign=Gtk.Align.START) + self._grid.attach(label, 0, self._next_row, 1, 1) + self._grid.attach(widget, 1, self._next_row, 1, 1) + self._next_row += 1 + + self._widgets[field_name] = widget + + def _add_bool_field(self, name: str): + """ + Adds a boolean field to the grid. + :param name: The name of the field. + :return: + """ + + widget = Gtk.Switch(active=self._settings[name], halign=Gtk.Align.END, valign=Gtk.Align.CENTER) + self._add_row(name, widget) + + def add_string_field(self, name: str): + """ + Adds a string field to the grid. + + :param name: The name of the field. + :return: + """ + + widget = Gtk.Entry(text=self._settings[name], halign=Gtk.Align.FILL, hexpand=True, valign=Gtk.Align.CENTER) + self._add_row(name, widget) + + def add_int_field(self, name: str): + """ + Adds an integer field to the grid. + :param name: The name of the field. + :return: + """ + + widget = Gtk.SpinButton(value=self._settings[name], halign=Gtk.Align.FILL, hexpand=True, + valign=Gtk.Align.CENTER) + widget.set_range(0, 2 ** 64 - 1) + self._add_row(name, widget) + + def add_list_field(self, name: str, item_type): + """ + Adds a list field to the grid. + + :param name: The name of the field + :param item_type: The type of the items in the list. + :return: + """ + + widget = EditableListView() + num_args = len(item_type) if isinstance(item_type, tuple) else 1 + model = Gtk.ListStore(*[str for _ in range(num_args)]) + values = list(self._settings[name]) + for value in values: + if num_args == 1: + model.append([value]) + else: + model.append(list(value)) + + widget.model = model + self._add_row(name, widget) + + @Gtk.Template.Callback() + def on_apply_clicked(self, *args): + """ + Updates the settings and emits the apply-settings signal. + + :param args: + :return: + """ + + for name, widget in self._widgets.items(): + if isinstance(widget, Gtk.Switch): + self._settings[name] = widget.get_active() + elif isinstance(widget, Gtk.SpinButton): + self._settings[name] = widget.get_value_as_int() + elif isinstance(widget, Gtk.Entry): + self._settings[name] = widget.get_text() + elif isinstance(widget, EditableListView): + entries = [tuple(list(x)) if len(list(x)) > 1 else x[0] for x in widget.model] + self._settings[name] = entries + + self._logger.debug("Library settings: %s", self._settings.to_dict()) + + self.emit('apply-settings', self._settings) + + @Gtk.Template.Callback() + def on_cancel_clicked(self, *args): + self.close() diff --git a/python/gui/src/hetsgui/windows/LibrarySettingsWindow.ui b/python/gui/src/hetsgui/windows/LibrarySettingsWindow.ui new file mode 100644 index 0000000000..12895c65cd --- /dev/null +++ b/python/gui/src/hetsgui/windows/LibrarySettingsWindow.ui @@ -0,0 +1,99 @@ + + + + + diff --git a/python/gui/src/hetsgui/windows/LibraryWindow.py b/python/gui/src/hetsgui/windows/LibraryWindow.py new file mode 100644 index 0000000000..3ff105606d --- /dev/null +++ b/python/gui/src/hetsgui/windows/LibraryWindow.py @@ -0,0 +1,69 @@ +import logging + +from gi.repository import Gtk, Gio + +import hets +from ..GtkSmartTemplate import GtkSmartTemplate +from ..formatting import COLOR_MAP +from ..utils import get_variant +from ..widgets import ExtendedDotWidget + + +@GtkSmartTemplate +class LibraryWindow(Gtk.Window): + """ + A window to show a library and its dependencies. + """ + + __gtype_name__ = "LibraryWindow" + + _logger = logging.getLogger(__name__) + _ui_graph: ExtendedDotWidget = Gtk.Template.Child() + _library: hets.Library + + def __init__(self, library: hets.Library, **kwargs): + super().__init__(**kwargs) + self._library = library + + self._ui_graph.set_dotcode(self.get_graph().encode("utf-8")) + + self._ui_graph.connect("node-right-click", self._on_node_right_clicked) + + def get_graph(self, ) -> str: + """ + Create a graph of the library and its dependencies. + :return: The dot code of the graph. + """ + + g = self._ui_graph.get_themed_graph() + for name, _ in self._library.environment(): + g.node(name.id(), label=str(name), + fillcolor=COLOR_MAP[("green", True, True)], + style="filled", + shape="rectangle") + + for source, target in self._library.dependencies(): + g.edge(source.id(), target.id()) + + return g.source + + def _on_node_right_clicked(self, widget, node_id: str, event): + """ + Handle the right-click event on a node. Show a context menu. + + :param widget: ignored + :param node_id: The id of the (library) node that was right-clicked. + :param event: The event that triggered the right-click. + :return: + """ + model = Gio.Menu() + + menu_item_open_ref = Gio.MenuItem() + menu_item_open_ref.set_label("Open library") + menu_item_open_ref.set_action_and_target_value("app.open_win_for_lib", get_variant(node_id)) + model.append_item(menu_item_open_ref) + + menu = Gtk.Menu.new_from_model(model) + menu.attach_to_widget(self) + menu.show_all() + menu.popup(None, None, None, None, event.button, event.time) diff --git a/python/gui/src/hetsgui/windows/LibraryWindow.ui b/python/gui/src/hetsgui/windows/LibraryWindow.ui new file mode 100644 index 0000000000..08d27c8ced --- /dev/null +++ b/python/gui/src/hetsgui/windows/LibraryWindow.ui @@ -0,0 +1,24 @@ + + + + + + diff --git a/python/gui/src/hetsgui/windows/ListViewSelectionDialog.ui b/python/gui/src/hetsgui/windows/ListViewSelectionDialog.ui new file mode 100644 index 0000000000..ae0db3160b --- /dev/null +++ b/python/gui/src/hetsgui/windows/ListViewSelectionDialog.ui @@ -0,0 +1,53 @@ + + + + + diff --git a/python/gui/src/hetsgui/windows/MainWindow.css b/python/gui/src/hetsgui/windows/MainWindow.css new file mode 100644 index 0000000000..d01bccdc01 --- /dev/null +++ b/python/gui/src/hetsgui/windows/MainWindow.css @@ -0,0 +1 @@ +statusbar { border-top: 1px solid @borders; } diff --git a/python/gui/src/hetsgui/windows/MainWindow.py b/python/gui/src/hetsgui/windows/MainWindow.py new file mode 100644 index 0000000000..9885f18f62 --- /dev/null +++ b/python/gui/src/hetsgui/windows/MainWindow.py @@ -0,0 +1,585 @@ +import logging +import os +import typing +from typing import Callable, Any, Optional + +from gi.repository import GLib, Gtk, Gio, GObject +from gi.repository.Gio import SimpleAction + +import hets +from hets import Library, ReferenceDevGraphNode, Options +from ..ApplicationSettings import ApplicationSettings +from ..GtkSmartTemplate import GtkSmartTemplate +from ..utils import get_variant +from ..widgets.EdgeInfoDialog import EdgeInfoDialog +from ..widgets.GraphvizGraphWidget import GraphvizGraphWidget +from ..widgets.NodeInfoDialog import NodeInfoDialog +from ..widgets.TheoryInfoDialog import TheoryInfoDialog +from ..windows.ConservativityCheckWindow import ConservativityCheckWindow +from ..windows.ConsistencyCheckWindow import ConsistencyCheckWindow +from ..windows.LibrarySettingsWindow import LibrarySettingsWindow +from ..windows.ProveWindow import ProveWindow + +T = typing.TypeVar("T") + + +@GtkSmartTemplate +class MainWindow(Gtk.ApplicationWindow): + """ + The main window of the application. It contains the graph widget and the status bar. + """ + __gtype_name__ = "MainWindow" + __gsignals__ = { + # Emitted when a file should be loaded. + "load-file": (GObject.SIGNAL_RUN_FIRST, None, (str, object)), + # Emitted when a library should be shown + "show-library": (GObject.SIGNAL_RUN_FIRST, None, (str,)), + # Emitted when a library grpah should be shown + "show-library-graph": (GObject.SIGNAL_RUN_FIRST, None, (str,)), + } + + _logger = logging.getLogger(__name__) + + _library_actions: list[SimpleAction] + """ The actions that are available when a library is loaded """ + + _library_settings_window: Optional[LibrarySettingsWindow] + """ The instance of the library settings window or None if no such window is open """ + + _settings: hets.Options + """ The current settings for loading new libraries """ + + _loaded_library: Optional[hets.Library] + """ The loaded library shown in this window""" + + # UI Elements + _ui_graph: GraphvizGraphWidget = Gtk.Template.Child() + _status_bar: Gtk.Statusbar = Gtk.Template.Child() + + def __init__(self, settings: Optional[Options] = None, **kwargs): + super().__init__(**kwargs) + self._library_settings_window = None + self._settings = hets.Options( + libdirs=[os.environ["HETS_LIB"]] if "HETS_LIB" in os.environ else []) if settings is None else settings + self._loaded_library = None + + # Update the status bar when rendering starts and ends + self._ui_graph.connect("render-start", + lambda _: self._status_bar.push(self._status_bar.get_context_id("render"), + "Rendering graph ...")) + self._ui_graph.connect("render-end", lambda _: self._status_bar.push(self._status_bar.get_context_id("render"), + "Graph rendered!")) + + # Style the window + self.set_auto_startup_notification(True) + icon = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../resources/icon.png")) + self.set_default_icon_from_file(icon) + self.set_icon_from_file(icon) + + # Add actions + # Library actions are only available, when a library is loaded and disabled otherwise + self._library_actions: typing.List[Gio.SimpleAction] = [] + + self._action("open_file", self._on_menu_open_file) + self._action("open_library_settings", self.on_open_library_settings) + self._action_state("change_graph_layout", self._on_change_graph_layout, "vertical") + + self._library_actions.append(self._action("open_library_window", self._on_open_library_window)) + + self._library_actions.append(self._action("node.prove", self._on_prove_node, "s")) + self._library_actions.append(self._action("node.check_consistency", self._on_check_consistency_node, "s")) + self._library_actions.append(self._action("node.show_info", self._on_show_node_info, "s")) + self._library_actions.append(self._action("node.show_theory", self._on_show_theory, "s")) + self._library_actions.append(self._action("node.translate", self._on_translate_node, "av")) + self._library_actions.append( + self._action("edge.check_conservativity", self._on_check_conservativity_edge, "av")) + self._library_actions.append(self._action("edge.show_info", self._on_show_edge_info, "av")) + + self._library_actions.append(self._action_toggle("toggle_show_names", self._on_toggle_show_names)) + self._library_actions.append(self._action_toggle("toggle_show_edges", self._on_toggle_show_edges)) + + self._library_actions.append(self._action("proofs.automatic", self.on_automatic)) + self._library_actions.append(self._action("proofs.global_subsume", self.on_global_subsume)) + self._library_actions.append(self._action("proofs.global_decomposition", self.on_global_decomposition)) + self._library_actions.append(self._action("proofs.local_inference", self.on_local_inference)) + self._library_actions.append(self._action("proofs.local_decomposition", self.on_local_decomposition)) + self._library_actions.append(self._action("proofs.composition_prove_edges", self.on_composition_prove_edges)) + self._library_actions.append(self._action("proofs.conservativity", self.on_conservativity)) + self._library_actions.append( + self._action("proofs.automatic_hide_theorem_shift", self.on_automatic_hide_theorem_shift)) + self._library_actions.append(self._action("proofs.theorem_hide_shift", self.on_theorem_hide_shift)) + self._library_actions.append(self._action("proofs.compute_colimit", self.on_compute_colimit)) + self._library_actions.append(self._action("proofs.normal_form", self.on_normal_form)) + self._library_actions.append(self._action("proofs.triangle_cons", self.on_triangle_cons)) + self._library_actions.append(self._action("proofs.freeness", self.on_freeness)) + self._library_actions.append(self._action("proofs.lib_flat_imports", self.on_lib_flat_imports)) + self._library_actions.append(self._action("proofs.lib_flat_d_unions", self.on_lib_flat_d_unions)) + self._library_actions.append(self._action("proofs.lib_flat_renamings", self.on_lib_flat_renamings)) + self._library_actions.append(self._action("proofs.lib_flat_hiding", self.on_lib_flat_hiding)) + self._library_actions.append(self._action("proofs.lib_flat_heterogen", self.on_lib_flat_heterogen)) + self._library_actions.append(self._action("proofs.qualify_lib_env", self.on_qualify_lib_env)) + + self._library_actions.append(self._action("open_win_for_lib_by_node", self._on_open_win_for_lib_by_node, "i")) + + self._set_library_actions_enabled(False) + + def _set_library_actions_enabled(self, enabled: bool): + """ + Enable or disable all library actions. + :param enabled: True to enable, False to disable. + :return: + """ + for action in self._library_actions: + action.set_enabled(enabled) + + def _action(self, name: str, cb: Callable[[Gio.SimpleAction, T], Any], + param_type_str: Optional[str] = None, target: Optional[Gio.ActionMap] = None) -> Gio.SimpleAction: + """ + Create a new action with the given name and callback and add it to the window. + :param name: The name of the action. + :param cb: The callback function. + :param param_type_str: The type of the parameter or None if no parameter is required. + :param target: The target action map to add the action to or None to add it to the window. + :return: The created action. + """ + + action = Gio.SimpleAction.new(name, GLib.VariantType(param_type_str) if param_type_str else None) + action.connect("activate", cb) + + if target is None: + self.add_action(action) + else: + target.add_action(action) + return action + + def _action_toggle(self, name: str, cb: Callable[[Gio.SimpleAction, GLib.Variant], Any], + default: bool = False) -> Gio.SimpleAction: + """ + Create a new toggle action with the given name and callback and add it to the window. + :param name: The name of the action. + :param cb: The callback function. + :param default: The default value of the toggle. + :return: The created action. + """ + return self._action_state(name, cb, default, None) + + def _action_state(self, name: str, cb: Callable[[Gio.SimpleAction, GLib.Variant], Any], default: Optional[T] = None, + param_type_str: Optional[str | typing.Literal["infer"]] = "infer") -> Gio.SimpleAction: + """ + Create a new stateful action with the given name and callback and add it to the window. + :param name: The name of the action. + :param cb: The callback function. + :param default: The default value of the action. + :param param_type_str: The type of the parameter, "infer" to infer the type from the default value, or None if no parameter is required. + :return: The created action. + """ + + default_variant = get_variant(default) + if param_type_str == "infer": + param_type = default_variant.get_type() + elif param_type_str is not None: + param_type = GLib.VariantType(param_type_str) + else: + param_type = None + + action = Gio.SimpleAction.new_stateful(name, param_type, default_variant) + action.connect("change-state", cb) + self.add_action(action) + return action + + def use_library(self, library: Library): + """ + Use the given library in this window. + + :param library: The library to use. + :return: + """ + + # Check if the user wants to apply automatic proof rules + settings: ApplicationSettings = self.get_application().settings + apply_auto = settings.apply_proof_rules_automatically + if apply_auto is None: + dialog = Gtk.MessageDialog(message_type=Gtk.MessageType.QUESTION, text="Apply automatic proof rules", + secondary_text="Do you want to apply automatic proof rules?") + dialog.add_buttons("Yes", Gtk.ResponseType.YES, + "No", Gtk.ResponseType.NO, + "Always", 1, + "Never", 2) + r = dialog.run() + # If the user selected a persistent option, save it + if r in (1, 2): + apply_auto = bool(2 - r) # Map response to true or false + settings.apply_proof_rules_automatically = apply_auto + self.get_application().activate_action("save_settings", None) + + if r == Gtk.ResponseType.YES: + apply_auto = True + else: + apply_auto = False + + dialog.destroy() + + self._loaded_library = library + if apply_auto: + self._loaded_library.automatic() + + # Render the graph + if self._ui_graph: + self._ui_graph.load_graph(self._loaded_library.development_graph()) + + self.set_title(f"{library.name().id()} - Heterogeneous Toolset") + + self._set_library_actions_enabled(True) + + def _on_change_graph_layout(self, action: Gio.SimpleAction, parameter: GLib.Variant): + """ + Callback for the change_graph_layout action. Changes the layout of the graph. + + :param action: The action that was activated. + :param parameter: The parameter of the action. + :return: + """ + + # Accept the new state + action.set_state(parameter) + + direction = parameter.get_string() + + self._logger.debug(f"Changing graph layout to {direction}") + + # Rerender the graph + self._ui_graph.graph_direction = direction + + def _on_open_library_window(self, action: Gio.SimpleAction, parameter: str): + self.emit("show-library-graph", self._loaded_library.name().id()) + + def _on_menu_open_file(self, action: Gio.SimpleAction, parameter: str): + """ + Callback for the open_file action. Opens a file dialog to load a new library. + + :param action: ignored + :param parameter: ignored + :return: + """ + dialog = Gtk.FileChooserDialog( + title="Please choose a file", parent=self, action=Gtk.FileChooserAction.OPEN + ) + + for libdir in self._settings["libdirs"]: + dialog.add_shortcut_folder(libdir) + + dialog.add_buttons( + Gtk.STOCK_CANCEL, + Gtk.ResponseType.CANCEL, + Gtk.STOCK_OPEN, + Gtk.ResponseType.OK, + ) + + filter_text = Gtk.FileFilter() + filter_text.set_name("Text files") + filter_text.add_mime_type("text/plain") + dialog.add_filter(filter_text) + + filter_any = Gtk.FileFilter() + filter_any.set_name("Any files") + filter_any.add_pattern("*") + dialog.add_filter(filter_any) + + response = dialog.run() + file = None + if response == Gtk.ResponseType.OK: + file = dialog.get_filename() + + dialog.destroy() + + if file: + # Ask the application to load the file (to avoid loading the same library multiple times) + self.emit("load-file", file, self._settings) + + def _on_prove_node(self, action, parameter: GLib.Variant): + """ + Callback for the prove node action. Opens a prove window for the selected node. + + :param action: ignored + :param parameter: The id of the node to prove. + :return: + """ + node_id = parameter.get_string() + if self._loaded_library: + node = [n for n in self._loaded_library.development_graph().nodes() if str(n.id()) == node_id][0] + + prove_window = ProveWindow(node, transient_for=self) + prove_window.show_all() + prove_window.present() + + prove_window.connect("destroy", lambda _: self._ui_graph.render()) + else: + self._logger.warning(f'Action: prove node {node_id}. But no library is loaded!') + + def _on_check_consistency_node(self, action, parameter: GLib.Variant): + """ + Callback for the check consistency node action. Opens a check consistency window for the selected node. + + :param action: ignored + :param parameter: The id of the node to check consistency for. + :return: + """ + + node_id = parameter.get_string() + if self._loaded_library: + node = [n for n in self._loaded_library.development_graph().nodes() if str(n.id()) == node_id][0] + + check_consistency_window = ConsistencyCheckWindow(node, transient_for=self) + check_consistency_window.show_all() + check_consistency_window.present() + + check_consistency_window.connect("destroy", lambda _: self._ui_graph.render()) + else: + self._logger.warning(f'Action: check consistency node {node_id}. But no library is loaded!') + + def _on_show_node_info(self, action, parameter: GLib.Variant): + """ + Callback for the show node info action. Opens a dialog with information about the selected node. + :param action: ignored + :param parameter: The id of the node to show info for. + :return: + """ + + node_id = parameter.get_string() + if self._loaded_library: + node = [n for n in self._loaded_library.development_graph().nodes() if str(n.id()) == node_id][0] + + info_dialog = NodeInfoDialog(node) + info_dialog.run() + info_dialog.destroy() + else: + self._logger.warning(f'Action: Show info for node {node_id}. But no library is loaded!') + + def _on_translate_node(self, action, parameter: GLib.Variant): + """ + Callback for the translate node action. Opens a dialog with information about the translated node. + :param action: ignored + :param parameter: A tuple of the node id which should be translated and the comorphism name with which it should be translated. + :return: + """ + + node_id = parameter.get_child_value(0).get_child_value(0).get_string() + comorphism_name = parameter.get_child_value(1).get_child_value(0).get_string() + if self._loaded_library: + node = [n for n in self._loaded_library.development_graph().nodes() if str(n.id()) == node_id][0] + comorphism = next( + (c for c in node.global_theory().get_available_comorphisms() if c.name() == comorphism_name), None) + + if comorphism is None: + self._logger.error("Could not find comorphism") + else: + translated = node.global_theory().translate(comorphism) + info_dialog = TheoryInfoDialog(translated, node.name()) + info_dialog.run() + info_dialog.destroy() + else: + self._logger.warning(f'Action: Show info for node {node_id}. But no library is loaded!') + + def _on_show_theory(self, action, parameter: GLib.Variant): + """ + Callback for the show theory action. Opens a dialog with information about the theory of the selected node. + :param action: ignored + :param parameter: The id of the node to show the theory for. + :return: + """ + node_id = parameter.get_string() + if self._loaded_library: + node = [n for n in self._loaded_library.development_graph().nodes() if str(n.id()) == node_id][0] + + info_dialog = TheoryInfoDialog(node.global_theory(), node.name()) + info_dialog.run() + info_dialog.destroy() + else: + self._logger.warning(f'Action: Show info for node {node_id}. But no library is loaded!') + + def _on_show_edge_info(self, action, parameter: GLib.Variant): + """ + Callback for the show edge info action. Opens a dialog with information about the selected edge. + :param action: ignored + :param parameter: A tuple of origin and target id of the edge to show info for. + :return: + """ + origin_id = parameter.get_child_value(0).get_child_value(0).get_string() + target_id = parameter.get_child_value(1).get_child_value(0).get_string() + if self._loaded_library: + edge = [e for e in self._loaded_library.development_graph().edges() if + str(e.origin()) == origin_id and str(e.target()) == target_id][0] + + info_dialog = EdgeInfoDialog(edge) + info_dialog.run() + else: + self._logger.warning(f'Action: Show info for edge {origin_id}->{target_id}. But no library is loaded!') + + def _on_check_conservativity_edge(self, action, parameter: GLib.Variant): + """ + Callback for the check conservativity edge action. Opens a dialog to check the conservativity of the selected edge. + :param action: ignored + :param parameter: A tuple of origin and target id of the edge to check conservativity for. + :return: + """ + origin_id = parameter.get_child_value(0).get_child_value(0).get_string() + target_id = parameter.get_child_value(1).get_child_value(0).get_string() + if self._loaded_library: + edge = [e for e in self._loaded_library.development_graph().edges() if + str(e.origin()) == origin_id and str(e.target()) == target_id][0] + + check_conservativity_window = ConservativityCheckWindow(edge, transient_for=self) + check_conservativity_window.show_all() + check_conservativity_window.present() + + check_conservativity_window.connect("destroy", lambda _: self._ui_graph.render()) + else: + self._logger.warning(f'Action: Show info for edge {origin_id}->{target_id}. But no library is loaded!') + + def _on_toggle_show_names(self, action: Gio.SimpleAction, target: GLib.Variant): + action.set_state(target) + state = target.get_boolean() + + if state: + self._ui_graph.show_internal_node_names() + else: + self._ui_graph.hide_internal_node_names() + + def _on_toggle_show_edges(self, action: Gio.SimpleAction, target: GLib.Variant): + action.set_state(target) + state = target.get_boolean() + + if state: + self._ui_graph.show_newly_added_proven_edges() + else: + self._ui_graph.hide_newly_added_proven_edges() + + def on_automatic(self, action: Gio.SimpleAction, target): + self._loaded_library.automatic() + self._ui_graph.render() + + def on_global_subsume(self, action: Gio.SimpleAction, target): + self._loaded_library.global_subsume() + self._ui_graph.render() + + def on_global_decomposition(self, action: Gio.SimpleAction, target): + self._loaded_library.global_decomposition() + self._ui_graph.render() + + def on_local_inference(self, action: Gio.SimpleAction, target): + self._loaded_library.local_inference() + self._ui_graph.render() + + def on_local_decomposition(self, action: Gio.SimpleAction, target): + self._loaded_library.local_decomposition() + self._ui_graph.render() + + def on_composition_prove_edges(self, action: Gio.SimpleAction, target): + self._loaded_library.composition_prove_edges() + self._ui_graph.render() + + def on_conservativity(self, action: Gio.SimpleAction, target): + self._loaded_library.conservativity() + self._ui_graph.render() + + def on_automatic_hide_theorem_shift(self, action: Gio.SimpleAction, target): + self._loaded_library.automatic_hide_theorem_shift() + self._ui_graph.render() + + def on_theorem_hide_shift(self, action: Gio.SimpleAction, target): + self._loaded_library.theorem_hide_shift() + self._ui_graph.render() + + def on_compute_colimit(self, action: Gio.SimpleAction, target): + self._loaded_library.compute_colimit() + self._ui_graph.render() + + def on_normal_form(self, action: Gio.SimpleAction, target): + self._loaded_library.normal_form() + self._ui_graph.render() + + def on_triangle_cons(self, action: Gio.SimpleAction, target): + self._loaded_library.triangle_cons() + self._ui_graph.render() + + def on_freeness(self, action: Gio.SimpleAction, target): + self._loaded_library.freeness() + self._ui_graph.render() + + def on_lib_flat_imports(self, action: Gio.SimpleAction, target): + self._loaded_library.lib_flat_imports() + self._ui_graph.render() + + def on_lib_flat_d_unions(self, action: Gio.SimpleAction, target): + self._loaded_library.lib_flat_d_unions() + self._ui_graph.render() + + def on_lib_flat_renamings(self, action: Gio.SimpleAction, target): + self._loaded_library.lib_flat_renamings() + self._ui_graph.render() + + def on_lib_flat_hiding(self, action: Gio.SimpleAction, target): + self._loaded_library.lib_flat_hiding() + self._ui_graph.render() + + def on_lib_flat_heterogen(self, action: Gio.SimpleAction, target): + self._loaded_library.lib_flat_heterogen() + self._ui_graph.render() + + def on_qualify_lib_env(self, action: Gio.SimpleAction, target): + self._loaded_library.qualify_lib_env() + self._ui_graph.render() + + def on_open_library_settings(self, action: Gio.SimpleAction, target): + if self._library_settings_window is None: + self._library_settings_window = LibrarySettingsWindow(settings=self._settings) + self._library_settings_window.connect('apply-settings', self._on_settings_changed) + + self._library_settings_window.show_all() + self._library_settings_window.present() + + def _on_settings_changed(self, widget, settings: hets.Options): + self._settings = settings + + self._library_settings_window.close() + self._library_settings_window = None + + if self._loaded_library is not None: + self.emit("load-file", self._loaded_library.name().location(), self._settings) + + def _on_open_win_for_lib_by_node(self, action: Gio.SimpleAction, parameter: GLib.Variant): + """ + Callback for the open_win_for_lib_by_node action. Opens a new library window for the library of the selected node. + :param action: ignored + :param parameter: The id of the node to open the library window for. + :return: + """ + if self._loaded_library is None: + return + + node_id = parameter.get_int32() + node = self._loaded_library.development_graph().node_by_id(node_id) + if node is None: + self._logger.error( + f"Attempted to load referenced library for node {node_id} but the node could not be found in the development graph.") + + dialog = Gtk.MessageDialog(transient_for=self, flags=0, message_type=Gtk.MessageType.ERROR, + buttons=Gtk.ButtonsType.CLOSE, text=f"Failed to referenced library!") + dialog.format_secondary_text( + f"The node of the referenced library was not found in the development graph. Please contact the developers.") + dialog.run() + dialog.destroy() + + if isinstance(node, ReferenceDevGraphNode): + lib_name = node.referenced_libname() + self.emit("show-library", lib_name.id()) + else: + self._logger.error( + f"Attempted to load referenced library for node {node_id} but the node is not a reference node!") + + dialog = Gtk.MessageDialog(transient_for=self, flags=0, message_type=Gtk.MessageType.ERROR, + buttons=Gtk.ButtonsType.CLOSE, text=f"Failed to referenced library!") + dialog.format_secondary_text( + f"The node of the referenced library is not a reference node. Please contact the developers.") + dialog.run() + dialog.destroy() diff --git a/python/gui/src/hetsgui/windows/MainWindow.ui b/python/gui/src/hetsgui/windows/MainWindow.ui new file mode 100644 index 0000000000..1552b671a1 --- /dev/null +++ b/python/gui/src/hetsgui/windows/MainWindow.ui @@ -0,0 +1,34 @@ + + + + + + diff --git a/python/gui/src/hetsgui/windows/ProofDetailsWindow.py b/python/gui/src/hetsgui/windows/ProofDetailsWindow.py new file mode 100644 index 0000000000..87d771a0d6 --- /dev/null +++ b/python/gui/src/hetsgui/windows/ProofDetailsWindow.py @@ -0,0 +1,37 @@ +from gi.repository import Gtk + +from hets import Sentence, Theory +from ..GtkSmartTemplate import GtkSmartTemplate +from ..widgets import ProofDetail + + +@GtkSmartTemplate +class ProofDetailsWindow(Gtk.Window): + """ + A window to show the details of a proof. + """ + __gtype_name__ = "ProofDetailsWindow" + + theory: Theory + goal: Sentence + + # UI elements + _box_proof_details: Gtk.Box = Gtk.Template.Child() + + def __init__(self, goal: Sentence, theory: Theory, **kwargs): + super().__init__(**kwargs) + self.maximize() + self.set_title(f"Proof details for {goal.name()}") + + self.goal = goal + self.theory = theory + + for comorphism, proof in goal.theorem_status(): + proof_detail = ProofDetail() + proof_detail.proof = proof + proof_detail.comorphism = comorphism + proof_detail.theory = theory + + self._box_proof_details.pack_start(proof_detail, False, True, 4) + + self.show_all() diff --git a/python/gui/src/hetsgui/windows/ProofDetailsWindow.ui b/python/gui/src/hetsgui/windows/ProofDetailsWindow.ui new file mode 100644 index 0000000000..81f5d96e46 --- /dev/null +++ b/python/gui/src/hetsgui/windows/ProofDetailsWindow.ui @@ -0,0 +1,21 @@ + + + + + + diff --git a/python/gui/src/hetsgui/windows/ProveWindow.py b/python/gui/src/hetsgui/windows/ProveWindow.py new file mode 100644 index 0000000000..aee2d4c81e --- /dev/null +++ b/python/gui/src/hetsgui/windows/ProveWindow.py @@ -0,0 +1,260 @@ +import concurrent.futures +import logging +import threading +import traceback +from threading import Thread +from typing import Optional, Tuple + +from gi.repository import Gtk, GLib, Gdk + +from hets import DevGraphNode, ProofKind, Comorphism, Prover, Sentence +from ..GtkSmartTemplate import GtkSmartTemplate +from ..formatting.colors import PROOF_KIND_BG_COLORS, color_name_to_rgba +from ..widgets import GridWithToolComorphismSelector +from ..windows.ProofDetailsWindow import ProofDetailsWindow + + +@GtkSmartTemplate +class ProveWindow(Gtk.Window): + """ + A window to prove goals at a DevGraphNode. + """ + + __gtype_name__ = "ProveWindow" + _logger = logging.getLogger(__name__) + _prove_lock: threading.Lock + _proving_thread: Optional[Thread] + + _node: DevGraphNode + + # Models + _goals_model: Gtk.ListStore = Gtk.Template.Child() + _axioms_model: Gtk.ListStore = Gtk.Template.Child() + + # UI elements + _notebook: Gtk.Notebook = Gtk.Template.Child() + _btn_prove: Gtk.Button = Gtk.Template.Child() + _txt_extra_options: Gtk.Entry = Gtk.Template.Child() + _txt_timeout: Gtk.SpinButton = Gtk.Template.Child() + _switch_include_proven_theorems: Gtk.Switch = Gtk.Template.Child() + + _lbl_sublogic: Gtk.Label = Gtk.Template.Child() + _prover_comorphism_selector: GridWithToolComorphismSelector = Gtk.Template.Child() + + @property + def selected_comorphism(self) -> Optional[Comorphism]: + """ + The selected comorphism or None if no comorphism is selected. + :return: + """ + return self._prover_comorphism_selector.selected_comorphism + + @property + def selected_prover(self) -> Optional[Prover]: + """ + The selected prover or None if no prover is selected. + :return: + """ + return self._prover_comorphism_selector.selected_prover + + def __init__(self, node: DevGraphNode, **kwargs): + super().__init__(title=f"Prove {node.name()}", **kwargs) + + self._proving_thread: Optional[threading.Thread] = None + self._prove_lock = threading.Lock() + self._node = node + self._prover_comorphism_selector.theory = node.global_theory() + self._init_view() + + self.update_sublogic() + + def _init_view(self): + """ + Add goals and axioms to the models. + :return: + """ + + # Add goals to goals model for display in tree view + for goal in self._node.global_theory().goals(): + color, text = self._goal_style(goal) + + self._goals_model.append( + [goal.name(), True, text, goal.name(), str(goal), color]) + + # Add axioms to axioms model for display in tree view + for axiom in self._node.global_theory().axioms(): + self._axioms_model.append( + [axiom.name(), True, axiom.name(), str(axiom)]) + + def _goal_style(self, g: Sentence) -> Tuple[Gdk.RGBA, str]: + """ + Get the style for a goal. + :param g: The goal. + :return: The color and text for the goal. + """ + proof = g.best_proof() + kind = proof.kind() if proof is not None else ProofKind.OPEN + goal_text = f'{kind.to_str()}' + color_name = PROOF_KIND_BG_COLORS[kind] + goal_color = color_name_to_rgba(color_name) + return goal_color, goal_text + + @Gtk.Template.Callback() + def on_close(self, widget, event): + """ + Called when the window is closed. Prevents the window from being closed if a proving process is currently running. + :param widget: ignored + :param event: ignored + :return: + """ + if self._proving_thread is not None and self._proving_thread.is_alive(): + return True # Stop the window from being closed if a proving process is currently running + + return False + + @Gtk.Template.Callback() + def on_prove_clicked(self, _): + """ + Called when the user clicks the prove button. Starts the proving process in a separate background thread. + :param _: ignored + :return: + """ + self._proving_thread = threading.Thread(target=self._prove) + self._proving_thread.start() + + @Gtk.Template.Callback() + def on_proof_details_clicked(self, widget, path): + """ + Called when the user clicks the proof details button. Shows the proof details for the selected goal. + :param widget: ignored + :param path: The path of the selected goal. + :return: + """ + goal_name = self._goals_model[path][0] + goal = next(iter(g for g in self._node.global_theory().goals() + if g.name() == goal_name), None) + + if goal is not None: + details_window = ProofDetailsWindow(goal, self._node.global_theory()) + details_window.show_all() + details_window.present() + + @Gtk.Template.Callback() + def on_goals_changed(self, model: Gtk.ListStore, path: Gtk.TreePath, it: Gtk.TreeIter): + self.update_sublogic() + + @Gtk.Template.Callback() + def on_axioms_changed(self, model: Gtk.ListStore, path: Gtk.TreePath, it: Gtk.TreeIter): + self.update_sublogic() + + def _init_prove_progress(self): + """ + Called when the proving process is started. Updates the UI accordingly. + :return: + """ + self._btn_prove.set_sensitive(False) + self._notebook.set_current_page(0) + + for goal in self._goals_model: + if goal[1]: # if selected to be proven + goal[2] = 'Waiting...' + color = color_name_to_rgba("white") + goal[5] = color + + def _finish_prove_progress_goal(self, goal_name: str): + """ + Called when a goal has been proven. Updates the UI accordingly. + :param goal_name: The proven goal + :return: + """ + + goal_row = next(iter(g for g in self._goals_model if g[0] == goal_name), None) + goal = next(iter(g for g in self._node.global_theory().goals() if g.name() == goal_name), None) + + if goal_row is not None: + color, text = self._goal_style(goal) + goal_row[2] = text + goal_row[5] = color + + def _start_prove_progress_goal(self, goal_name: str): + """ + Called when a goal is being proven. Updates the UI accordingly. + :param goal_name: The goal being proven + :return: + """ + goal_row = next( + iter(g for g in self._goals_model if g[0] == goal_name), None) + if goal_row is not None: + goal_row[2] = 'Proving...' + + def _finish_prove_progress(self): + """ + Called when the proving process has finished. Updates the UI accordingly. + :return: + """ + self._btn_prove.set_sensitive(True) + self._notebook.set_current_page(0) + + def _prove(self): + """ + Proves the selected goals at the node. The function is blocking and designed to be run in a separate background thread. + :return: + """ + + # Update the UI in the main thread + GLib.idle_add(self._init_prove_progress) + + goals = [row[0] for row in self._goals_model if row[1]] + axioms = [row[0] for row in self._axioms_model if row[1]] + + prover = self.selected_prover + comorphism = self.selected_comorphism + timeout = self._txt_timeout.get_value_as_int() + include_theorems = self._switch_include_proven_theorems.get_active() + + def prove_goal(g): + GLib.idle_add(self._start_prove_progress_goal, g) + + self._logger.debug("Proving at node %s. Next goal: %s", self._node.name(), g) + try: + self._node.prove(prover, comorphism, include_theorems, [g], axioms, timeout) + except Exception as e: + self._logger.warning("Proving at node %s failed for goal %s: %s", self._node.name(), g, + traceback.format_exc()) + + dialog = Gtk.MessageDialog(transient_for=self, flags=0, message_type=Gtk.MessageType.ERROR, + buttons=Gtk.ButtonsType.CLOSE, text=f"Proving failed!") + dialog.format_secondary_text(f"Check the console for more details. Error message: {str(e)}") + dialog.run() + dialog.destroy() + + GLib.idle_add(self._finish_prove_progress_goal, g) + + self._logger.info( + "Proving at node %s, goals: %s, axioms: %s, prover: %s, comorphism: %s, timeout: %s, include_theorems: %s", + self._node.name(), goals, axioms, prover.name(), comorphism.name(), timeout, include_theorems) + + # Prove the goals in parallel if previously proven goals should be included in subsequent proofs, otherwise prove them sequentially + if include_theorems: + self._logger.debug("Theorems included. Proving sequentially") + for goal in goals: + prove_goal(goal) + else: + self._logger.debug("Theorems not included. Proving in parallel") + with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor: + concurrent.futures.wait([executor.submit(prove_goal, goal) for goal in goals]) + + # Update the UI in the main thread + GLib.idle_add(self._finish_prove_progress) + + def update_sublogic(self): + """ + Updates the label for the sublogic when the selection of axioms and goals changes. + :return: + """ + if self._proving_thread is None or not self._proving_thread.is_alive(): + axioms = [row[0] for row in self._axioms_model if row[1]] + goals = [row[0] for row in self._goals_model if row[1]] + + sub_logic = self._node.global_theory().with_selection(axioms, goals).get_sublogic() + self._lbl_sublogic.set_label(sub_logic) diff --git a/python/gui/src/hetsgui/windows/ProveWindow.ui b/python/gui/src/hetsgui/windows/ProveWindow.ui new file mode 100644 index 0000000000..9f60a30e0e --- /dev/null +++ b/python/gui/src/hetsgui/windows/ProveWindow.ui @@ -0,0 +1,332 @@ + + + + + 4294967295 + 1 + 10 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/python/gui/src/hetsgui/windows/RefinementTreeWindow.py b/python/gui/src/hetsgui/windows/RefinementTreeWindow.py new file mode 100644 index 0000000000..f0c7c4eca1 --- /dev/null +++ b/python/gui/src/hetsgui/windows/RefinementTreeWindow.py @@ -0,0 +1,63 @@ +import logging + + +import hets + +from gi.repository import Gtk + +from hets.RefinementTreeLink import RefinementTreeLinkKind +from ..GtkSmartTemplate import GtkSmartTemplate +from ..formatting import COLOR_MAP +from ..widgets import ExtendedDotWidget + + +@GtkSmartTemplate +class RefinementTreeWindow(Gtk.Window): + """ + A window to show a refinement tree. + """ + __gtype_name__ = "RefinementTreeWindow" + + _logger = logging.getLogger(__name__) + _ui_graph: ExtendedDotWidget = Gtk.Template.Child() + _library: hets.Library + _refinement_tree: hets.RefinementTree + _spec_name: str + + def __init__(self, library: hets.Library, spec_name: str, **kwargs): + super().__init__(**kwargs) + self._library = library + self._spec_name = spec_name + + self._refinement_tree = self._library.get_refinement_tree(spec_name) + + self._ui_graph.set_dotcode(self.get_graph().encode("utf-8")) + + self._ui_graph.connect("node-right-click", self._on_node_right_clicked) + + def get_graph(self, ): + """ + Create a graphviz graph of the refinement tree. + :return: + """ + g = self._ui_graph.get_themed_graph() + for node in self._refinement_tree.nodes(): + g.node(str(node.id()), + label=node.name(), + fillcolor=COLOR_MAP[("blue" if node.is_root() else "green", True, True)], + style="filled") + + for edge in self._refinement_tree.edges(): + black = COLOR_MAP[("black", False, False)] + color = { + RefinementTreeLinkKind.SIMPLE: f"{black}:invis:{black}", + RefinementTreeLinkKind.COMPONENT: black, + RefinementTreeLinkKind.UNKNOWN: black, + }[edge.kind()] + + g.edge(str(edge.source_id()), str(edge.target_id()), color=color) + + return g.source + + def _on_node_right_clicked(self, widget, node_id: str, event): + pass \ No newline at end of file diff --git a/python/gui/src/hetsgui/windows/RefinementTreeWindow.ui b/python/gui/src/hetsgui/windows/RefinementTreeWindow.ui new file mode 100644 index 0000000000..a7522e250e --- /dev/null +++ b/python/gui/src/hetsgui/windows/RefinementTreeWindow.ui @@ -0,0 +1,24 @@ + + + + + + diff --git a/python/gui/src/hetsgui/windows/StartUpWindow.css b/python/gui/src/hetsgui/windows/StartUpWindow.css new file mode 100644 index 0000000000..624c72868d --- /dev/null +++ b/python/gui/src/hetsgui/windows/StartUpWindow.css @@ -0,0 +1,5 @@ +window.startup-window { + background-color: blue; + background: linear-gradient(45deg, rgba(9,9,121,1) 0%, rgba(0,212,255,1) 100%); + color: white; +} diff --git a/python/gui/src/hetsgui/windows/StartUpWindow.py b/python/gui/src/hetsgui/windows/StartUpWindow.py new file mode 100644 index 0000000000..44cd382b93 --- /dev/null +++ b/python/gui/src/hetsgui/windows/StartUpWindow.py @@ -0,0 +1,21 @@ +import os.path + +from gi.repository import Gtk + +from ..GtkSmartTemplate import GtkSmartTemplate + + +@GtkSmartTemplate +class StartUpWindow(Gtk.Window): + """ + A window to be shown during start-up when loading the libraries. + """ + __gtype_name__ = "StartUpWindow" + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + icon = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../resources/icon.png")) + self.set_default_icon_from_file(icon) + + self.show_all() diff --git a/python/gui/src/hetsgui/windows/StartUpWindow.ui b/python/gui/src/hetsgui/windows/StartUpWindow.ui new file mode 100644 index 0000000000..c05615d5a1 --- /dev/null +++ b/python/gui/src/hetsgui/windows/StartUpWindow.ui @@ -0,0 +1,69 @@ + + + + + + diff --git a/python/gui/src/test-resource-application.py b/python/gui/src/test-resource-application.py new file mode 100644 index 0000000000..19aa509067 --- /dev/null +++ b/python/gui/src/test-resource-application.py @@ -0,0 +1,173 @@ +import logging +import os.path +import sys +from typing import List + +import gi + +from hetsgui.utils import resource_exist + +gi.require_version("Gtk", "3.0") + + +from gi.repository import Gtk, Gio, GObject, GLib + + +def get_test_window_for_window_resource(resource_name: str): + name = resource_name.split("/")[-1] + style_resource_name = f"{resource_name}.css" + + @Gtk.Template(resource_path=f"{resource_name}.ui") + class PreviewWindow(Gtk.Window): + __gtype_name__ = name + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + if resource_exist(style_resource_name): + provider = Gtk.CssProvider() + provider.load_from_resource(style_resource_name) + self.get_style_context().add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) + + return PreviewWindow + + +class PreviewWidgetWindow(Gtk.Window): + def __init__(self, resource: str, **kwargs): + super().__init__(**kwargs) + self.set_default_size(400, 400) + import hetsgui.widgets + + widget_name = resource.split("/")[-1] + widget_class = hetsgui.widgets.__dict__[widget_name] + widget = widget_class() + self.add(widget) + + +class ResourceSelectorWindow(Gtk.Window): + __gsignals__ = { + "preview-resource": (GObject.SignalFlags.RUN_FIRST, None, (str,)) + } + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def collect_resources(path: str): + rs: List[str] = Gio.resources_enumerate_children(path, 0) + result = [] + for resource in rs: + # if resource.endswith("Window.ui"): + if resource.endswith("/"): + result += collect_resources(path + resource) + elif resource.endswith(".ui"): + result.append(path + resource[:-3]) + + return result + + resources = collect_resources("/eu/hets/") + resource_model = Gtk.ListStore(str) + + for r in resources: + resource_model.append([r]) + + self.combo: Gtk.ComboBox = Gtk.ComboBox.new_with_model(resource_model) + renderer_text = Gtk.CellRendererText() + self.combo.pack_start(renderer_text, True) + self.combo.add_attribute(renderer_text, "text", 0) + self.combo.set_active(0) + + button = Gtk.Button(label="Open") + button.connect("clicked", self.on_open_clicked) + + check_button = Gtk.CheckButton(label="Use stub class", active=True) + + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + box.pack_start(self.combo, False, False, 0) + + button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + button_box.pack_start(button, True, True, 14) + button_box.pack_end(check_button, False, False, 0) + + box.pack_start(button_box, False, False, 0) + + self._check_stub = check_button + + self.add(box) + + def on_open_clicked(self, widget): + resource_model = self.combo.get_model() + selected = self.combo.get_active() + resource = resource_model[selected][0] + self.emit("preview-resource", resource) + + def use_stub(self) -> bool: + return self._check_stub.get_active() + + +class PreviewApplication(Gtk.Application): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.window = None + + pgk_dir = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "hetsgui")) + resource_file = os.path.join(pgk_dir, "hetsgui.gresource") + resource: Gio.Resource = Gio.resource_load(resource_file) + Gio.resources_register(resource) + + self.add_main_option("log", ord('l'), GLib.OptionFlags.NONE, GLib.OptionArg.STRING, "Log level", "") + self.connect("handle-local-options", self.on_handle_local_options) + + # noinspection PyUnresolvedReferences + import hetsgui.widgets + + def on_handle_local_options(self, application, options: GLib.VariantDict): + log_value = options.lookup_value("log") + if log_value is not None: + log_level = log_value.get_string().upper() + log_level_int = getattr(logging, log_level.upper(), None) + if not isinstance(log_level_int, int): + print('Invalid log level: %s' % log_level, file=sys.stderr) + return 1 + logging.basicConfig(level=log_level_int) + + return -1 + + def do_activate(self): + if not self.window: + resource_selector_window = ResourceSelectorWindow(application=self) + resource_selector_window.connect("preview-resource", self.preview_resource) + + self.window = resource_selector_window + + self.window.show_all() + self.window.present() + + def preview_resource(self, widget, resource): + use_stub = True + if self.window is not None: + self.window.close() + use_stub = self.window.use_stub() + + if resource.endswith("Window"): + if use_stub: + template_window = get_test_window_for_window_resource(resource)(application=self) + self.window = template_window + else: + import importlib + module_name = resource[len("/eu/hets/"):].replace("/", ".") + window_module = importlib.import_module(module_name) + window_type = window_module.__dict__[resource.split("/")[-1]] + window = window_type(application=self) + self.window = window + else: + self.window = PreviewWidgetWindow(resource, application=self) + + self.window.show_all() + self.window.present() + + + +if __name__ == "__main__": + app = PreviewApplication() + exit_status = app.run(sys.argv) + sys.exit(exit_status) diff --git a/python/hets/Comorphism.py b/python/hets/Comorphism.py deleted file mode 100644 index 7f3d4e7f29..0000000000 --- a/python/hets/Comorphism.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -Description : Represents `Logic.Comorphism` -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" - -from .haskell import comorphismName, PyComorphism - - -class Comorphism: - def __init__(self, hs_comorphism: PyComorphism) -> None: - self._hs_comorphism = hs_comorphism - - def name(self) -> str: - return comorphismName(self._hs_comorphism) - - def __eq__(self, other): - return isinstance(other, Comorphism) and self.name() == other.name() - - def __hash__(self): - return self.name().__hash__() diff --git a/python/hets/ConsistencyChecker.py b/python/hets/ConsistencyChecker.py deleted file mode 100644 index fbaabfb7ee..0000000000 --- a/python/hets/ConsistencyChecker.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -Description : Represents `Logic.Prover.ConsChecker` -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" -from .haskell import consCheckerName, PyConsChecker - - -class ConsistencyChecker: - def __init__(self, hs_cons_checker: PyConsChecker) -> None: - self._hs_cons_checker = hs_cons_checker - - def name(self) -> str: - return consCheckerName(self._hs_cons_checker) - - def __eq__(self, other): - return isinstance(other, ConsistencyChecker) and self.name() == other.name() - - def __hash__(self): - return self.name().__hash__() diff --git a/python/hets/ConsistencyStatus.py b/python/hets/ConsistencyStatus.py deleted file mode 100644 index b26bcc0b7e..0000000000 --- a/python/hets/ConsistencyStatus.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -Description : Represents `Static.DgUtils.ConsStatus` -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" - -from .haskell import ConsStatus, showConsistencyStatus - - -class ConsistencyStatus: - def __init__(self, hs_cons_status: ConsStatus): - self._hs_cons_status = hs_cons_status - - def required(self) -> str: - hsCons = self._hs_cons_status.requiredConservativity() - return showConsistencyStatus(hsCons) - - def proven(self) -> str: - hsCons = self._hs_cons_status.provenConservativity() - return showConsistencyStatus(hsCons) - - def is_proven_link(self) -> bool: - return self._hs_cons_status.isProvenConsStatusLink() diff --git a/python/hets/DevGraphEdge.py b/python/hets/DevGraphEdge.py deleted file mode 100644 index 544b5e4bcf..0000000000 --- a/python/hets/DevGraphEdge.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -Description : Represents `Static.DevGraph.DGLinkLab` -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" - -from typing import Tuple, Optional - -from .haskell import DGLinkLab, fstOf3, sndOf3, thd, gmorphismOfEdge, developmentGraphEdgeLabelName -from .HsWrapper import HsHierarchyElement -from .GMorphism import GMorphism - - -class DevGraphEdge(HsHierarchyElement): - def __init__(self, hs_edge: Tuple[int, int, DGLinkLab], parent: Optional[HsHierarchyElement]) -> None: - super().__init__(parent) - - self._hs_edge = hs_edge - - def hs_obj(self): - return self._hs_edge - - def origin(self) -> int: - return fstOf3(self._hs_edge) - - def target(self) -> int: - return sndOf3(self._hs_edge) - - def _label(self) -> DGLinkLab: - return thd(self._hs_edge) - - def morphism(self) -> GMorphism: - return GMorphism(gmorphismOfEdge(self._label())) - - def name(self): - return developmentGraphEdgeLabelName(self._label()) diff --git a/python/hets/DevGraphNode.py b/python/hets/DevGraphNode.py deleted file mode 100644 index 34ce1d7fea..0000000000 --- a/python/hets/DevGraphNode.py +++ /dev/null @@ -1,146 +0,0 @@ -""" -Description : Represents `Static.DevGraph.DGNodeLab` -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" - -from typing import Tuple, Optional, List - -from .ConsistencyStatus import ConsistencyStatus -from .Comorphism import Comorphism -from .result import result_or_raise -from .ConsistencyChecker import ConsistencyChecker -from .ProofStatus import ProofStatus -from .Prover import Prover -from .HsWrapper import HsHierarchyElement -from .haskell import snd, theoryOfNode, DGNodeLab, fst, Just, Nothing, PyProver, PyComorphism, defaultProofOptions, \ - mkPyProofOptions, proveNodeAndRecord, ConsistencyStatus as HsConsistencyStatus, PyConsChecker, \ - defaultConsCheckingOptions, \ - PyConsCheckingOptions, checkConsistencyAndRecord, TheoryPointer, globalTheory, recomputeNode, fromJust, \ - developmentGraphNodeLabelName, developmentGraphEdgeLabelName - -from .Theory import Theory - - -class DevGraphNode(HsHierarchyElement): - def __init__(self, hs_node: Tuple[int, DGNodeLab], parent: Optional[HsHierarchyElement]) -> None: - super().__init__(parent) - - self._hs_node = hs_node - - self._theory: Optional[Theory] = None - - def hs_obj(self): - return self._hs_node - - def id(self) -> int: - return fst(self._hs_node) - - def label(self) -> DGNodeLab: - return snd(self._hs_node) - - def name(self) -> str: - return developmentGraphNodeLabelName(self.label()) - - def _theory_pointer(self) -> TheoryPointer: - node = self.hs_obj() - graph = self.parent().hs_obj() - env_name = self.parent().parent().hs_obj() - - name = fst(env_name) - env = snd(env_name) - - return name, env, graph, node - - def prove(self, - prover: Optional[Prover] = None, - comorphism: Optional[Comorphism] = None, - use_theorems: Optional[bool] = None, - goals_to_prove: Optional[List[str]] = None, - axioms_to_include: Optional[List[str]] = None, - timeout: Optional[int] = None - ) -> List[ProofStatus]: - prover_maybe = Just(prover._hs_prover) if prover else Nothing().subst(a=PyProver()) - comorphism_maybe = Just(comorphism._hs_comorphism) if comorphism else Nothing().subst(a=PyComorphism()) - - default_opts = defaultProofOptions - - opts = mkPyProofOptions( - prover_maybe, - comorphism_maybe)( - use_theorems if use_theorems is not None else default_opts.proofOptsUseTheorems(), - goals_to_prove if goals_to_prove is not None else default_opts.proofOptsGoalsToProve(), - axioms_to_include if axioms_to_include is not None else default_opts.proofOptsAxiomsToInclude(), - timeout if timeout is not None else default_opts.proofOptsTimeout(), - ) - - prove_result = proveNodeAndRecord(self._theory_pointer(), opts).act() - result = result_or_raise(prove_result) - new_th_and_statuses = fst(result) - goal_statuses = snd(new_th_and_statuses) - new_env = snd(result) - - self.root().hs_update(new_env) - - return goal_statuses - - def check_consistency(self, - cons_checker: Optional[ConsistencyChecker] = None, - comorphism: Optional[Comorphism] = None, - include_theorems: Optional[bool] = None, - timeout: Optional[int] = None - ) -> HsConsistencyStatus: - cc_maybe = Just(cons_checker._hs_cons_checker) if cons_checker else Nothing().subst(a=PyConsChecker()) - comorphism_maybe = Just(comorphism._hs_comorphism) if comorphism else Nothing().subst(a=PyComorphism()) - - default_opts = defaultConsCheckingOptions - - opts = PyConsCheckingOptions( - cc_maybe, - comorphism_maybe, - include_theorems if include_theorems is not None else default_opts.consOptsIncludeTheorems(), - timeout if timeout is not None else default_opts.consOptsTimeout(), - ) - - result = checkConsistencyAndRecord(self._theory_pointer(), opts).act() - cc_result, new_env = fst(result), snd(result) - - self.root().hs_update(new_env) - - return cc_result - - def consistency_status(self) -> ConsistencyStatus: - node_lab = snd(self._hs_node) - hs_cons_status = node_lab.getNodeConsStatus() - return ConsistencyStatus(hs_cons_status) - - def global_theory(self) -> Optional[Theory]: - node_lab = snd(self._hs_node) - - py_theory_maybe = globalTheory(node_lab) - - if isinstance(py_theory_maybe, Just): - py_theory = fromJust(py_theory_maybe) - return Theory(py_theory, self) - - return None - - def recompute(self) -> None: - new_lib_env = recomputeNode(self._theory_pointer()) - - root = self.parent().parent() - root.hs_update(new_lib_env) - - def hs_update(self, new_hs_obj) -> None: - self._hs_node = new_hs_obj - - if self._theory: - node_lab = snd(self._hs_node) - hs_theory = theoryOfNode(node_lab) - self._theory.hs_update(hs_theory) - - def theory(self) -> Theory: - if self._theory is None: - self._theory = Theory(theoryOfNode(snd(self._hs_node)), self) - - return self._theory diff --git a/python/hets/DevelopmentGraph.py b/python/hets/DevelopmentGraph.py deleted file mode 100644 index 50a910c44e..0000000000 --- a/python/hets/DevelopmentGraph.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Description : Represents `Static.DevGraph.DGraph` -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" -from typing import List, Optional - -from .DevGraphNode import DevGraphNode -from .DevGraphEdge import DevGraphEdge -from .GlobalAnnotations import GlobalAnnotations -from .HsWrapper import HsHierarchyElement - -from .haskell import getLNodesFromDevelopmentGraph, DGraph, Nothing, fromJust, getDGNodeById, \ - getLEdgesFromDevelopmentGraph, globalAnnotations - - -class DevelopmentGraph(HsHierarchyElement): - def __init__(self, hs_development_graph: DGraph, parent: HsHierarchyElement) -> None: - super().__init__(parent) - - self._hs_development_graph = hs_development_graph - - self._nodes: Optional[List[DevGraphNode]] = None - self._edges: Optional[List[DevGraphEdge]] = None - - def hs_obj(self): - return self._hs_development_graph - - def hs_update(self, new_hs_obj: DGraph): - self._hs_development_graph = new_hs_obj - - if self._nodes: - for node in self._nodes: - hs_node_maybe = getDGNodeById(self._hs_development_graph, node.id()) - if isinstance(hs_node_maybe, Nothing): - print(f"Node {node.id} could not be found. Probably, it has been deleted") - else: - hsNode = fromJust(hs_node_maybe) - node.hs_update((node.id(), hsNode)) - - def nodes(self) -> List[DevGraphNode]: - if self._nodes is None: - hs_nodes = getLNodesFromDevelopmentGraph(self._hs_development_graph) - self._nodes = [DevGraphNode(x, self) for x in hs_nodes] - - return self._nodes - - def edges(self) -> List[DevGraphEdge]: - if self._edges is None: - hs_edges = getLEdgesFromDevelopmentGraph(self._hs_development_graph) - self._edges = [DevGraphEdge(x, self) for x in hs_edges] - - return self._edges - - def global_annotations(self) -> GlobalAnnotations: - return GlobalAnnotations(globalAnnotations(self._hs_development_graph)) diff --git a/python/hets/HsWrapper.py b/python/hets/HsWrapper.py deleted file mode 100644 index 126c7279eb..0000000000 --- a/python/hets/HsWrapper.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -Description : Defines a common base class for wrapped haskell elements -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" - -from typing import Generic, TypeVar, Optional - - -class HsWrapper: - def hs_obj(self): - pass - - def hs_update(self, new_hs_obj): - pass - - -class HsHierarchyElement(HsWrapper): - def __init__(self, parent: Optional): - super().__init__() - - self._parent = parent - - def parent(self) -> Optional: - return self._parent - - def root(self): - if self.parent() is None: - return self - - return self.parent().root() - - - diff --git a/python/hets/Library.py b/python/hets/Library.py deleted file mode 100644 index 86729eabf3..0000000000 --- a/python/hets/Library.py +++ /dev/null @@ -1,145 +0,0 @@ -""" -Description : Represents `(Common.LibName.LibName, Static.DevGraph.LibEnv)` -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" - -from typing import Optional - -from .HsWrapper import HsWrapper, HsHierarchyElement -from .haskell import defaultHetcatsOpts, loadLibrary as loadHsLibrary, fst, snd, getGraphForLibrary, HetcatsOpts, \ - checkConsistencyAndRecord - -from .DevelopmentGraph import DevelopmentGraph -from .result import result_or_raise - -from .haskell import ( - automatic as automaticHs, - globalSubsume as globalSubsumeHs, - globalDecomposition as globalDecompositionHs, - localInference as localInferenceHs, - localDecomposition as localDecompositionHs, - compositionProveEdges as compositionProveEdgesHs, - conservativity as conservativityHs, - automaticHideTheoremShift as automaticHideTheoremShiftHs, - theoremHideShift as theoremHideShiftHs, - computeColimit as computeColimitHs, - normalForm as normalFormHs, - triangleCons as triangleConsHs, - freeness as freenessHs, - libFlatImports as libFlatImportsHs, - libFlatDUnions as libFlatDUnionsHs, - libFlatRenamings as libFlatRenamingsHs, - libFlatHiding as libFlatHidingHs, - libFlatHeterogen as libFlatHeterogenHs, - qualifyLibEnv as qualifyLibEnvHs -) - - -class Library(HsHierarchyElement): - def __init__(self, hs_library) -> None: - super().__init__(None) - self._name = fst(hs_library) - self._env = snd(hs_library) - - self._dgraph: Optional[DevelopmentGraph] = None - - def hs_obj(self): - return self._name, self._env - - def hs_update(self, new_env): - self._env = new_env - - if self._dgraph: - hs_graph = getGraphForLibrary(self._name, self._env) - self._dgraph.hs_update(hs_graph) - - def development_graph(self) -> DevelopmentGraph: - if self._dgraph is None: - self._dgraph = DevelopmentGraph(getGraphForLibrary(self._name, self._env), self) - - return self._dgraph - - def automatic(self): - new_env = automaticHs(self._name, self._env) - self.hs_update(new_env) - - def global_subsume(self): - new_env = globalSubsumeHs(self._name, self._env) - self.hs_update(new_env) - - def global_decomposition(self): - new_env = globalDecompositionHs(self._name, self._env) - self.hs_update(new_env) - - def local_inference(self): - new_env = localInferenceHs(self._name, self._env) - self.hs_update(new_env) - - def local_decomposition(self): - new_env = localDecompositionHs(self._name, self._env) - self.hs_update(new_env) - - def composition_prove_edges(self): - new_env = compositionProveEdgesHs(self._name, self._env) - self.hs_update(new_env) - - def conservativity(self): - new_env = conservativityHs(self._name, self._env) - self.hs_update(new_env) - - def automatic_hide_theorem_shift(self): - new_env = automaticHideTheoremShiftHs(self._name, self._env) - self.hs_update(new_env) - - def theorem_hide_shift(self): - new_env = theoremHideShiftHs(self._name, self._env) - self.hs_update(new_env) - - def compute_colimit(self): - new_env = computeColimitHs(self._name, self._env) - self.hs_update(new_env) - - def normal_form(self): - new_env = normalFormHs(self._name, self._env) - self.hs_update(new_env) - - def triangle_cons(self): - new_env = triangleConsHs(self._name, self._env) - self.hs_update(new_env) - - def freeness(self): - new_env = freenessHs(self._name, self._env) - self.hs_update(new_env) - - def lib_flat_imports(self): - new_env = libFlatImportsHs(self._name, self._env) - self.hs_update(new_env) - - def lib_flat_d_unions(self): - new_env = libFlatDUnionsHs(self._name, self._env) - self.hs_update(new_env) - - def lib_flat_renamings(self): - new_env = libFlatRenamingsHs(self._name, self._env) - self.hs_update(new_env) - - def lib_flat_hiding(self): - new_env = libFlatHidingHs(self._name, self._env) - self.hs_update(new_env) - - def lib_flat_heterogen(self): - new_env = libFlatHeterogenHs(self._name, self._env) - self.hs_update(new_env) - - def qualify_lib_env(self): - new_env = qualifyLibEnvHs(self._name, self._env) - self.hs_update(new_env) - - -def load_library(path: str, options: HetcatsOpts = defaultHetcatsOpts) -> Library: - result = loadHsLibrary(path, options).act() - - name_and_env = result_or_raise(result, "Failed to load library") - - return Library(name_and_env) diff --git a/python/hets/ProofStatus.py b/python/hets/ProofStatus.py deleted file mode 100644 index aac4f5bc39..0000000000 --- a/python/hets/ProofStatus.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Description : Represents `Logic.Prover.ProofStatus` -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" -from .haskell import ProofStatus as ProofStatusHs, GoalStatus, TimeOfDay, TacticScript - -from typing import Any, List - - -class ProofStatus: - def __init__(self, hs_proof_status: ProofStatusHs): - self._hs_proof_status = hs_proof_status - - def goal_name(self) -> str: - return self._hs_proof_status.goalName() - - def goal_status(self) -> GoalStatus: - return self._hs_proof_status.goalStatus() - - def used_axioms(self) -> List[str]: - return self._hs_proof_status.usedAxioms() - - def used_prover(self) -> str: - return self._hs_proof_status.usedProver() - - def used_time(self) -> TimeOfDay: - return self._hs_proof_status.usedTime() - - def tactic_script(self) -> TacticScript: - return self._hs_proof_status.tacticScript() - - def proof_lines(self) -> List[str]: - return self._hs_proof_status.proofLines() - diff --git a/python/hets/Sentence.py b/python/hets/Sentence.py deleted file mode 100644 index 8d18278153..0000000000 --- a/python/hets/Sentence.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Description : Represents `Logic.Logic.Sentences` -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" -import json -from typing import Tuple, Callable - -from .haskell import fst, show, snd, Sentence as PySentence -from .json_conversion import as_json - - -class Sentence: - def __init__(self, hs_sentence_with_name: Tuple[str, PySentence], hs_pretty_fn: Callable[[PySentence], str]) -> None: - self._name = fst(hs_sentence_with_name) - self._hs_sentence = snd(hs_sentence_with_name) - self._hs_pretty_fn = hs_pretty_fn - - def name(self) -> str: - return self._name - - def as_json(self) -> dict: - return as_json(self._hs_sentence) - - def __str__(self) -> str: - return self._hs_pretty_fn(self._hs_sentence) - - def __repr__(self): - return f"<{__name__} object representing sentence {self.name()} = '{str(self)}'>" diff --git a/python/hets/Theory.py b/python/hets/Theory.py deleted file mode 100644 index edec20fe3f..0000000000 --- a/python/hets/Theory.py +++ /dev/null @@ -1,76 +0,0 @@ -""" -Description : Represents `Static.GTheory.G_theory` -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" - -from typing import List, Optional, Tuple - -from .HsWrapper import HsHierarchyElement -from .Logic import Logic -from .Signature import Signature -from .haskell import (Just, Nothing, fst, thd, PyTheory, getUsableProvers, getUsableConsistencyCheckers, - proveNodeAndRecord, - getAvailableComorphisms, getAllSentences, getAllGoals, getAllAxioms, getProvenGoals, - prettySentence, - getUnprovenGoals, OMap, fstOf3, sndOf3, ProofStatus, PyProver, PyComorphism, PyConsChecker, - ConsistencyStatus, defaultProofOptions, PyProofOptions, mkPyProofOptions, TheoryPointer, snd, - defaultConsCheckingOptions, PyConsCheckingOptions, checkConsistencyAndRecord, logicNameOfTheory, - logicDescriptionOfTheory, signatureOfTheory) - -from .result import result_or_raise - -from .Prover import Prover -from .ConsistencyChecker import ConsistencyChecker -from .Comorphism import Comorphism -from .ProofState import ProofState -from .Sentence import Sentence - - -class Theory(HsHierarchyElement): - def __init__(self, hs_theory: PyTheory, parent: Optional[HsHierarchyElement]) -> None: - super().__init__(parent) - self._hs_theory = hs_theory - self._hs_pretty_sentence = prettySentence(hs_theory) - - def hs_obj(self): - return self._hs_theory - - def hs_update(self, new_hs_obj): - self._hs_theory = new_hs_obj - - def get_usable_provers(self) -> List[Prover]: - provers = getUsableProvers(self._hs_theory).act() - return list({Prover(fst(p)) for p in provers}) - - def get_usable_consistency_checkers(self) -> List[ConsistencyChecker]: - ccs = getUsableConsistencyCheckers(self._hs_theory).act() - return list({ConsistencyChecker(fst(cc)) for cc in ccs}) - - def get_available_comorphisms(self) -> List[Comorphism]: - comorphisms = getAvailableComorphisms(self._hs_theory) - return [Comorphism(x) for x in comorphisms] - - def sentences(self) -> List[Sentence]: - sentences = getAllSentences(self._hs_theory) - return [Sentence(x, self._hs_pretty_sentence) for x in OMap.toList(sentences)] - - def axioms(self) -> List[Sentence]: - axioms = getAllAxioms(self._hs_theory) - return [Sentence(x, self._hs_pretty_sentence) for x in OMap.toList(axioms)] - - def goals(self) -> List[Sentence]: - return [Sentence(x, self._hs_pretty_sentence) for x in OMap.toList(getAllGoals(self._hs_theory))] - - def proven_goals(self) -> List[Sentence]: - return [Sentence(x, self._hs_pretty_sentence) for x in OMap.toList(getProvenGoals(self._hs_theory))] - - def unproven_goals(self) -> List[Sentence]: - return [Sentence(x, self._hs_pretty_sentence) for x in OMap.toList(getUnprovenGoals(self._hs_theory))] - - def logic(self) -> Logic: - return Logic(logicNameOfTheory(self._hs_theory), logicDescriptionOfTheory(self._hs_theory)) - - def signature(self) -> Signature: - return Signature(signatureOfTheory(self._hs_theory)) - diff --git a/python/hets/__init__.py b/python/hets/__init__.py deleted file mode 100644 index 3332b0befa..0000000000 --- a/python/hets/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -Description : Reexports all modules of the API -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" - -from .Comorphism import * -from .ConsistencyChecker import * -from .ConsistencyStatus import * -from .DevelopmentGraph import * -from .DevGraphEdge import * -from .DevGraphNode import * -from .GMorphism import * -from .Library import * -from .Logic import * -from .ProofState import * -from .ProofStatus import * -from .Prover import * -from .Sentence import * -from .Signature import * -from .Theory import * diff --git a/python/hets/haskell/Internal.py b/python/hets/haskell/Internal.py deleted file mode 100644 index edf3be587e..0000000000 --- a/python/hets/haskell/Internal.py +++ /dev/null @@ -1,40 +0,0 @@ -""" Auto generated python imports for haskell module ../HetsAPI/Internal.hs""" - -from .base import * - -from hs.HetsAPI.Internal import (DGNodeLab, - getNodeConsStatus, - DGLinkLab, - ConsStatus, - isProvenConsStatusLink, - requiredConservativity, - provenConservativity, - Conservativity, - DGraph, - GlobalAnnos, - developmentGraphNodeLabelName, - developmentGraphEdgeLabelName, - globalAnnotations, - precedenceAnnotations, - associativityAnnotations, - displayAnnos, - literalAnnos, - prefixMap, - Result, - resultToMaybe, - GoalStatus, - TimeOfDay, - TacticScript, - ConsistencyStatus, - LibName, - LibEnv, - ProofStatus, - showConsistencyStatus, - ExtSign, - plainSign, - nonImportedSymbols, - fromJust, - ProofState, - Diagnosis, - HetcatsOpts, - defaultHetcatsOpts) diff --git a/python/hets/haskell/Internal.pyi b/python/hets/haskell/Internal.pyi deleted file mode 100644 index 9a6cce95c3..0000000000 --- a/python/hets/haskell/Internal.pyi +++ /dev/null @@ -1,123 +0,0 @@ -""" Auto generated python stubs for haskell module ../HetsAPI/Internal.hs""" - -import typing -from .Prelude import * - -a = typing.TypeVar("a") - -class DGNodeLab: - def getNodeConsStatus(self) -> ConsStatus: ... - - ... - - -class DGLinkLab: - ... - - -class ConsStatus: - def isProvenConsStatusLink(self) -> bool: ... - - def requiredConservativity(self) -> Conservativity: ... - - def provenConservativity(self) -> Conservativity: ... - - ... - -class Conservativity: ... -class DGraph: ... - -class GlobalAnnos: ... - - -def developmentGraphNodeLabelName(x0: DGNodeLab) -> str: ... - - -def developmentGraphEdgeLabelName(x0: DGLinkLab) -> str: ... - - -def globalAnnotations(x0: DGraph) -> GlobalAnnos: ... - - -def precedenceAnnotations(x0: GlobalAnnos) -> object: ... - - -def associativityAnnotations(x0: GlobalAnnos) -> object: ... - - -def displayAnnos(x0: GlobalAnnos) -> object: ... - - -def literalAnnos(x0: GlobalAnnos) -> object: ... - - -def prefixMap(x0: GlobalAnnos) -> object: ... - - -class Result(typing.Generic[a]): - def diags(self) -> typing.List[typing.Any]: ... - - -def resultToMaybe(r: Result[a]) -> Maybe[a]: ... - - - -class GoalStatus: ... - - -class TimeOfDay: ... - - -class TacticScript: ... - -class ConsistencyStatus: ... - - -class LibName: ... - - -class LibEnv: ... - -class ProofStatus(typing.Generic[a]): - def goalName(self) -> str: ... - - def goalStatus(self) -> GoalStatus: ... - - def usedAxioms(self) -> typing.List[str]: ... - - def usedProver(self) -> str: ... - - def proofTree(self) -> a: ... - - def usedTime(self) -> TimeOfDay: ... - - def tacticScript(self) -> TacticScript: ... - - def proofLines(self) -> typing.List[str]: ... - - -class ExtSign(typing.Generic[a, b]): - def plainSign(self): ... - - def nonImportedSymbols(self): ... - -def showConsistencyStatus(c: Conservativity) -> str: ... - -def fromJust(m: Maybe[a]) -> a: ... - -class ProofState: ... - -class Diagnosis: ... - - -def getLNodesFromDevelopmentGraph(g: DGraph) -> typing.List[typing.Tuple[int, DGNodeLab]]: ... - - -def getEdgesFromDevelopmentGraph(g: DGraph) -> typing.List[DGLinkLab]: ... - - -def getLEdgesFromDevelopmentGraph(g: DGraph) -> typing.List[typing.Tuple[int, int, DGLinkLab]]: ... - -class HetcatsOpts: ... - -def defaultHetcatsOpts() -> HetcatsOpts: ... diff --git a/python/hets/haskell/Prelude.py b/python/hets/haskell/Prelude.py deleted file mode 100644 index 7106994846..0000000000 --- a/python/hets/haskell/Prelude.py +++ /dev/null @@ -1,2 +0,0 @@ -from .base import * -from hs.Prelude import Just, Nothing, fst, snd, show, String diff --git a/python/hets/haskell/Python.py b/python/hets/haskell/Python.py deleted file mode 100644 index bb486a3f87..0000000000 --- a/python/hets/haskell/Python.py +++ /dev/null @@ -1,23 +0,0 @@ -""" Auto generated python imports for haskell module ../HetsAPI/Python.hs""" - -from .base import * - -from hs.HetsAPI.Python import (fstOf3, sndOf3, thd, PyTheory, PyProver, PyConsChecker, PyComorphism, PyProofTree, - PyGMorphism, PyProofOptions, PyProofOptions, mkPyProofOptions, PyConsCheckingOptions, - PyConsCheckingOptions, defaultProofOptions, defaultConsCheckingOptions, proverName, - comorphismName, targetLogicName, targetLogicDescriptionName, sourceLogicName, - sourceLogicDescriptionName, consCheckerName, theoryOfNode, getUsableProvers, proveNode, - proveNodeAndRecord, translateTheory, getAvailableComorphisms, - getUsableConsistencyCheckers, checkConsistency, checkConsistencyAndRecord, - getAllSentences, getAllAxioms, getAllGoals, getProvenGoals, getUnprovenGoals, - prettySentence, signatureOfTheory, logicNameOfTheory, logicDescriptionOfTheory, - getDGNodeById, globalTheory, gmorphismOfEdge, comorphismOfGMorphism, - signatureOfGMorphism, comorphismNameOfGMorphism, comorphismDescriptionOfGMorphism, - domainOfGMorphism, codomainOfGMorphism, isGMorphismInclusion, gMorphismToTransportType, - TheoryPointer, Sentence, recomputeNode, getLNodesFromDevelopmentGraph, - getLEdgesFromDevelopmentGraph, getEdgesFromDevelopmentGraph, loadLibrary, - getGraphForLibrary, automatic, globalSubsume, - globalDecomposition, localInference, localDecomposition, compositionProveEdges, - conservativity, automaticHideTheoremShift, theoremHideShift, computeColimit, normalForm, - triangleCons, freeness, libFlatImports, libFlatDUnions, libFlatRenamings, libFlatHiding, - libFlatHeterogen, qualifyLibEnv ) diff --git a/python/hets/haskell/Python.pyi b/python/hets/haskell/Python.pyi deleted file mode 100644 index 3d4e90c1a1..0000000000 --- a/python/hets/haskell/Python.pyi +++ /dev/null @@ -1,278 +0,0 @@ -""" Auto generated python stubs for haskell module ../HetsAPI/Python.hs""" - -import typing - -from .Prelude import * -from .Internal import * -from .OMap import * - -a = typing.TypeVar("a") -b = typing.TypeVar("b") -c = typing.TypeVar("c") - -GenericTransportType = typing.Any -SignatureJSON = GenericTransportType -SymbolJSON = GenericTransportType -SentenceByName = OMap[str, Sentence] - - -def fstOf3(x0: typing.Tuple[a, b, c]) -> a: ... - - -def sndOf3(x0: typing.Tuple[a, b, c]) -> b: ... - - -def thd(x0: typing.Tuple[a, b, c]) -> c: ... - - -class PyTheory: - ... - - -class PyProver: - ... - - -class PyConsChecker: - ... - - -class PyComorphism: - ... - - -class PyProofTree: - ... - - -class PyGMorphism: - ... - - -class PyProofOptions: - def __init__(self, proofOptsProver: Maybe[PyProver], proofOptsComorphism: Maybe[PyComorphism], - proofOptsUseTheorems: bool, proofOptsGoalsToProve: typing.List[str], - proofOptsAxiomsToInclude: typing.List[str], proofOptsTimeout: int): ... - - def proofOptsProver(self) -> Maybe[PyProver]: ... - - def proofOptsComorphism(self) -> Maybe[PyComorphism]: ... - - def proofOptsUseTheorems(self) -> bool: ... - - def proofOptsGoalsToProve(self) -> typing.List[str]: ... - - def proofOptsAxiomsToInclude(self) -> typing.List[str]: ... - - def proofOptsTimeout(self) -> int: ... - - ... - - -def mkPyProofOptions(x0: Maybe[PyProver]) -> typing.Callable[[Maybe[PyComorphism]], typing.Callable[ - [bool], typing.Callable[ - [typing.List[str]], typing.Callable[[typing.List[str]], typing.Callable[[int], PyProofOptions]]]]]: ... - - -class PyConsCheckingOptions: - def __init__(self, consOptsConsChecker: Maybe[PyConsChecker], consOptsComorphism: Maybe[PyComorphism], - consOptsIncludeTheorems: bool, consOptsTimeout: int): ... - - def consOptsConsChecker(self) -> Maybe[PyConsChecker]: ... - - def consOptsComorphism(self) -> Maybe[PyComorphism]: ... - - def consOptsIncludeTheorems(self) -> bool: ... - - def consOptsTimeout(self) -> int: ... - - ... - - -def defaultProofOptions() -> PyProofOptions: ... - - -def defaultConsCheckingOptions() -> PyConsCheckingOptions: ... - - -def proverName(x0: PyProver) -> str: ... - - -def comorphismName(x0: PyComorphism) -> str: ... - - -def targetLogicName(x0: PyComorphism) -> str: ... - - -def targetLogicDescriptionName(x0: PyComorphism) -> str: ... - - -def sourceLogicName(x0: PyComorphism) -> str: ... - - -def sourceLogicDescriptionName(x0: PyComorphism) -> str: ... - - -def consCheckerName(x0: PyConsChecker) -> str: ... - - -def theoryOfNode(x0: DGNodeLab) -> PyTheory: ... - - -def getUsableProvers(x0: PyTheory) -> IO[typing.List[typing.Tuple[PyProver, PyComorphism]]]: ... - - -def proveNode(x0: PyTheory) -> typing.Callable[ - [PyProofOptions], IO[Result[typing.Tuple[PyTheory, typing.List[ProofStatus[PyProofTree]]]]]]: ... - - -def proveNodeAndRecord(x0: TheoryPointer) -> typing.Callable[[PyProofOptions], IO[ - Result[typing.Tuple[typing.Tuple[PyTheory, typing.List[ProofStatus[PyProofTree]]], LibEnv]]]]: ... - - -def translateTheory(x0: PyComorphism) -> typing.Callable[[PyTheory], Result[PyTheory]]: ... - - -def getAvailableComorphisms(x0: PyTheory) -> typing.List[PyComorphism]: ... - - -def getUsableConsistencyCheckers(x0: PyTheory) -> IO[typing.List[typing.Tuple[PyConsChecker, PyComorphism]]]: ... - - -def checkConsistency(x0: TheoryPointer) -> typing.Callable[[PyConsCheckingOptions], IO[ConsistencyStatus]]: ... - - -def checkConsistencyAndRecord(x0: TheoryPointer) -> typing.Callable[ - [PyConsCheckingOptions], IO[typing.Tuple[ConsistencyStatus, LibEnv]]]: ... - - -def getAllSentences(x0: PyTheory) -> SentenceByName: ... - - -def getAllAxioms(x0: PyTheory) -> SentenceByName: ... - - -def getAllGoals(x0: PyTheory) -> SentenceByName: ... - - -def getProvenGoals(x0: PyTheory) -> SentenceByName: ... - - -def getUnprovenGoals(x0: PyTheory) -> SentenceByName: ... - - -def prettySentence(x0: PyTheory) -> typing.Callable[[Sentence], str]: ... - - -def signatureOfTheory(x0: PyTheory) -> ExtSign[SignatureJSON, SymbolJSON]: ... - - -def logicNameOfTheory(x0: PyTheory) -> str: ... - - -def logicDescriptionOfTheory(x0: PyTheory) -> str: ... - - -def getDGNodeById(x0: DGraph) -> typing.Callable[[int], Maybe[DGNodeLab]]: ... - - -def globalTheory(x0: DGNodeLab) -> Maybe[PyTheory]: ... - - -def gmorphismOfEdge(x0: DGLinkLab) -> PyGMorphism: ... - - -def comorphismOfGMorphism(x0: PyGMorphism) -> PyComorphism: ... - - -def signatureOfGMorphism(x0: PyGMorphism) -> ExtSign[SignatureJSON, SymbolJSON]: ... - - -def comorphismNameOfGMorphism(x0: PyGMorphism) -> str: ... - - -def comorphismDescriptionOfGMorphism(x0: PyGMorphism) -> str: ... - - -def domainOfGMorphism(x0: PyGMorphism) -> GenericTransportType: ... - - -def codomainOfGMorphism(x0: PyGMorphism) -> GenericTransportType: ... - - -def isGMorphismInclusion(x0: PyGMorphism) -> bool: ... - - -def gMorphismToTransportType(x0: PyGMorphism) -> GenericTransportType: ... - - -TheoryPointer = typing.Tuple[typing.Any, typing.Any, typing.Any, typing.Any] - - -class Sentence: ... - - -def recomputeNode(thPtr: TheoryPointer) -> LibEnv: ... - - -def loadLibrary(path: str, opts: HetcatsOpts) -> IO[Result[typing.Tuple[LibName, LibEnv]]]: ... - - -def getGraphForLibrary(n: LibName, e: LibEnv) -> DGraph: ... - - -def automatic(name: LibName, env: LibEnv) -> LibEnv: ... - - -def globalSubsume(name: LibName, env: LibEnv) -> LibEnv: ... - - -def globalDecomposition(name: LibName, env: LibEnv) -> LibEnv: ... - - -def localInference(name: LibName, env: LibEnv) -> LibEnv: ... - - -def localDecomposition(name: LibName, env: LibEnv) -> LibEnv: ... - - -def compositionProveEdges(name: LibName, env: LibEnv) -> LibEnv: ... - - -def conservativity(name: LibName, env: LibEnv) -> LibEnv: ... - - -def automaticHideTheoremShift(name: LibName, env: LibEnv) -> LibEnv: ... - - -def theoremHideShift(name: LibName, env: LibEnv) -> LibEnv: ... - - -def computeColimit(name: LibName, env: LibEnv) -> LibEnv: ... - - -def normalForm(name: LibName, env: LibEnv) -> LibEnv: ... - - -def triangleCons(name: LibName, env: LibEnv) -> LibEnv: ... - - -def freeness(name: LibName, env: LibEnv) -> LibEnv: ... - - -def libFlatImports(name: LibName, env: LibEnv) -> LibEnv: ... - - -def libFlatDUnions(name: LibName, env: LibEnv) -> LibEnv: ... - - -def libFlatRenamings(name: LibName, env: LibEnv) -> LibEnv: ... - - -def libFlatHiding(name: LibName, env: LibEnv) -> LibEnv: ... - - -def libFlatHeterogen(name: LibName, env: LibEnv) -> LibEnv: ... - - -def qualifyLibEnv(name: LibName, env: LibEnv) -> LibEnv: ... diff --git a/python/hets/haskell/__init__.py b/python/hets/haskell/__init__.py deleted file mode 100644 index d1c965f6ef..0000000000 --- a/python/hets/haskell/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .base import * -import hyphen - -from .ByteString import * -from .Internal import * -from .OMap import * -from .Prelude import * -from .Python import * diff --git a/python/hets/haskell/base.py b/python/hets/haskell/base.py deleted file mode 100644 index a164abc0d1..0000000000 --- a/python/hets/haskell/base.py +++ /dev/null @@ -1,5 +0,0 @@ -import hyphen - -# Some moules in a module hierarchy do not exist in haskell. E.g. the module `Driver.Options` in python implies the existence of a module `Driver` which does not have to exist in python. Hence, these modules need to be marked explicitly empty -hyphen.importing.FORCED_EMPTY += ["Driver", "Common", "Static", "Logic", "Proofs", "HetsAPI"] -hyphen.importing.EXPECTED_EMPTY += ["Driver", "Common", "Static", "Logic", "Proofs", "HetsAPI"] diff --git a/python/hets/result.py b/python/hets/result.py deleted file mode 100644 index f11b1b9868..0000000000 --- a/python/hets/result.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -Description : Provides utility functions to interact with `Common.Result.Result` instances -Copyright : (c) Otto-von-Guericke University of Magdeburg -License : GPLv2 or higher, see LICENSE.txt -""" - -from typing import Any, TypeVar, Generic -from .haskell import Result, resultToMaybe, fromJust, Just, show - -T = TypeVar("T") - - -def result_or_raise(result: Generic[T], msg: str = "An error occured") -> T: - result_maybe = resultToMaybe(result) - if isinstance(result_maybe, Just): - return fromJust(result_maybe) - else: - raise Exception(f"{msg}: {show(result.diags())}") diff --git a/utils/DrIFT-src/ParseLib2.hs b/utils/DrIFT-src/ParseLib2.hs index 51492cfbc5..2bc293fad0 100644 --- a/utils/DrIFT-src/ParseLib2.hs +++ b/utils/DrIFT-src/ParseLib2.hs @@ -45,6 +45,7 @@ module ParseLib2 import Data.Char import Control.Applicative import Control.Monad +import qualified Control.Monad.Fail as Fail infixr 5 +++ @@ -82,7 +83,7 @@ instance MonadPlus Parser where -- mplus :: Parser a -> Parser a -> Parser a (P p) `mplus` (P q) = P (\ pos inp -> (p pos inp ++ q pos inp)) -instance MonadFail Parser where +instance Fail.MonadFail Parser where fail _ = P (\ _ _ -> [])