diff --git a/k-distribution/src/main/scripts/lib/pyk/kast.py b/k-distribution/src/main/scripts/lib/pyk/kast.py
index 19c6fa537e0..57fe68d7559 100644
--- a/k-distribution/src/main/scripts/lib/pyk/kast.py
+++ b/k-distribution/src/main/scripts/lib/pyk/kast.py
@@ -407,14 +407,14 @@ def prettyPrintKast(kast, symbolTable, debug = False):
if getAttribute(kast, 'label') is not None:
ruleStr = ruleStr + '[' + getAttribute(kast, 'label') + ']:'
ruleStr = ruleStr + ' ' + body
- requiresStr = ''
- ensuresStr = ''
attsStr = prettyPrintKast(kast['att'], symbolTable, debug = debug)
if kast['requires'] is not None:
requiresStr = 'requires ' + '\n '.join(prettyPrintKastBool(kast['requires'], symbolTable, debug = debug).split('\n'))
+ ruleStr = ruleStr + '\n ' + requiresStr
if kast['ensures'] is not None:
ensuresStr = 'ensures ' + '\n '.join(prettyPrintKastBool(kast['ensures'], symbolTable, debug = debug).split('\n'))
- return ruleStr + '\n ' + requiresStr + '\n ' + ensuresStr + '\n ' + attsStr
+ ruleStr = ruleStr + '\n ' + ensuresStr
+ return ruleStr + '\n ' + attsStr
if isKContext(kast):
body = indent(prettyPrintKast(kast['body'], symbolTable, debug = debug))
contextStr = 'context alias ' + body
diff --git a/k-distribution/src/main/scripts/lib/pyk/ktool.py b/k-distribution/src/main/scripts/lib/pyk/ktool.py
index 75860e58dbe..2e4e400427a 100644
--- a/k-distribution/src/main/scripts/lib/pyk/ktool.py
+++ b/k-distribution/src/main/scripts/lib/pyk/ktool.py
@@ -71,13 +71,13 @@ def prove(self, specFile, specModuleName, args = [], haskellArgs = [], logAxioms
fatal('Proof took zero steps, likely the LHS is invalid: ' + str(specFile))
return finalState
- def proveClaim(self, claim, claimId, args = [], haskellArgs = [], logAxiomsFile = None, allowZeroStep = False):
+ def proveClaim(self, claim, claimId, lemmas = [], args = [], haskellArgs = [], logAxiomsFile = None, allowZeroStep = False):
"""Given a K claim, write the definition needed for the prover, and attempt to prove it.
- Input: KAST representation of a claim to prove, and an identifer for said claim.
- Output: KAST representation of final state the prover supplies for it.
"""
- self._writeClaimDefinition(claim, claimId)
+ self._writeClaimDefinition(claim, claimId, lemmas = lemmas)
return self.prove(self.useDirectory / (claimId.lower() + '-spec.k'), claimId.upper() + '-SPEC', args = args, haskellArgs = haskellArgs, logAxiomsFile = logAxiomsFile, allowZeroStep = allowZeroStep)
def proveClaimNoBranching(self, claim, claimId, args = [], haskellArgs = [], logAxiomsFile = None, maxDepth = 1000, allowZeroStep = False):
@@ -96,7 +96,7 @@ def proveClaimNoBranching(self, claim, claimId, args = [], haskellArgs = [], log
nextStates = flattenLabel('#Or', nextState)
return (depth, nextStates)
- def _writeClaimDefinition(self, claim, claimId, rule = False):
+ def _writeClaimDefinition(self, claim, claimId, lemmas = [], rule = False):
"""Given a K claim, write the definition file needed for the prover to it.
- Input: KAST representation of a claim to prove, and an identifier for said claim.
@@ -106,7 +106,7 @@ def _writeClaimDefinition(self, claim, claimId, rule = False):
tmpModuleName = claimId.upper() if rule else (claimId.upper() + '-SPEC')
tmpClaim = tmpClaim.with_suffix('.k')
with open(tmpClaim, 'w') as tc:
- claimModule = KFlatModule(tmpModuleName, [KImport(self.mainModule, True)], [claim])
+ claimModule = KFlatModule(tmpModuleName, [KImport(self.mainModule, True)], lemmas + [claim])
claimDefinition = KDefinition(tmpModuleName, [claimModule], requires = [KRequire(self.mainFileName)])
tc.write(genFileTimestamp() + '\n')
tc.write(self.prettyPrint(claimDefinition) + '\n\n')
diff --git a/k-distribution/tests/pyk/Makefile b/k-distribution/tests/pyk/Makefile
index f9019276020..c47eee9800a 100644
--- a/k-distribution/tests/pyk/Makefile
+++ b/k-distribution/tests/pyk/Makefile
@@ -54,10 +54,12 @@ FILE_DEPS =
pytests = defn_test.py \
emit_json_spec_test.py \
+ simple_proofs_test.py \
unit_test.py
defn_test.py.pytest: FILE_DEPS = imp-verification/haskell.kompiled
emit_json_spec_test.py.pytest: FILE_DEPS = imp-verification/looping-spec.json.json-spec
+simple_proofs_test.py.pytest: FILE_DEPS = simple-proofs/haskell.kompiled
test-pytest: $(pytests:=.pytest)
@@ -93,7 +95,8 @@ KOMPILE_BACKEND = haskell
KOMPILE_COVERAGE =
KOMPILE_OPTS =
-definitions := imp-verification/haskell
+definitions := imp-verification/haskell \
+ simple-proofs/haskell
build-definitions: $(definitions:=.kompiled)
diff --git a/k-distribution/tests/pyk/k-files/simple-proofs.k b/k-distribution/tests/pyk/k-files/simple-proofs.k
new file mode 100644
index 00000000000..bbcefdd07f0
--- /dev/null
+++ b/k-distribution/tests/pyk/k-files/simple-proofs.k
@@ -0,0 +1,18 @@
+requires "domains.md"
+
+module SIMPLE-PROOFS
+ imports INT
+ imports MAP
+ imports BOOL
+
+ configuration $PGM:KItem
+ .Map
+
+ syntax Bool ::= pred1 ( Int ) [function, functional, klabel(pred1), symbol, no-evaluators]
+ syntax KItem ::= "foo" | "bar"
+
+ rule foo => bar ...
+ ... 3 |-> N ...
+ requires pred1(N)
+
+endmodule
diff --git a/k-distribution/tests/pyk/simple_proofs_test.py b/k-distribution/tests/pyk/simple_proofs_test.py
new file mode 100644
index 00000000000..0df7e47bbf8
--- /dev/null
+++ b/k-distribution/tests/pyk/simple_proofs_test.py
@@ -0,0 +1,42 @@
+import json
+from pathlib import Path
+
+from kprove_test import KProveTest
+from pyk.kast import KApply, KAtt, KClaim, KRule, KToken, KVariable
+from pyk.kastManip import rewriteAnywhereWith
+
+
+class SimpleProofTest(KProveTest):
+ DEFN_DIR = 'definitions/simple-proofs/haskell/simple-proofs-kompiled'
+ MAIN_FILE_NAME = 'simple-proofs.k'
+ USE_DIR = '.simple-proof-test'
+ INCLUDE_DIRS = ['k-files']
+
+ def setUp(self):
+ super().setUp()
+
+ @staticmethod
+ def _update_symbol_table(symbol_table):
+ pass
+
+ def test_prove_claim_with_lemmas(self):
+ # Given
+ new_lemma = KRule(KToken('pred1(3) => true', 'Bool'), requires=KToken('pred1(4)', 'Bool'), att=KAtt(atts={'simplification': ''}))
+ new_claim = KClaim(KToken(' foo => bar ... 3 |-> 3 ', 'TCellFragment'), requires=KToken('pred1(4)', 'Bool'))
+
+ # When
+ result1 = self.kprove.proveClaim(new_claim, 'claim-without-lemma')
+ result2 = self.kprove.proveClaim(new_claim, 'claim-with-lemma', lemmas = [new_lemma])
+
+ # Then
+ self.assertNotEqual(result1['label'], '#Top')
+ self.assertEqual(result2['label'], '#Top')
+
+
+def extract_claims(module):
+ return [claim for claim in module['localSentences'] if claim['node'] == 'KClaim']
+
+
+def eliminate_generated_top(term):
+ rule = KApply('', [KVariable('CONFIG'), KVariable('_')]), KVariable('CONFIG')
+ return rewriteAnywhereWith(rule, term)