From 8938cdd611a2d30b836c72708b571db0590f265c Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Mon, 8 Sep 2025 11:59:28 +0200 Subject: [PATCH 01/90] first steps to allow scripts in jml (cherry-picked from earlier attempt to implement JML proof scripts) # Conflicts: # key.core/src/main/java/de/uka/ilkd/key/java/Recoder2KeYConverter.java # key.core/src/main/java/de/uka/ilkd/key/java/recoderext/JMLTransformer.java # key.core/src/main/java/de/uka/ilkd/key/java/recoderext/JmlAssert.java # key.core/src/main/java/de/uka/ilkd/key/java/statement/JmlAssert.java # key.core/src/main/java/de/uka/ilkd/key/speclang/jml/pretranslation/TextualJMLAssertStatement.java # key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java # Conflicts: # key.core/src/main/antlr4/JmlParser.g4 # key.core/src/main/java/de/uka/ilkd/key/java/Recoder2KeYConverter.java # key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java # key.core/src/main/java/de/uka/ilkd/key/java/recoderext/JMLTransformer.java # key.core/src/main/java/de/uka/ilkd/key/java/recoderext/JmlAssert.java # key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java --- key.core/src/main/antlr4/JmlLexer.g4 | 2 ++ key.core/src/main/antlr4/JmlParser.g4 | 14 +++++++- key.core/src/main/antlr4/KeYLexer.g4 | 2 ++ .../key/java/ast/statement/JmlAssert.java | 35 +++++++++++++------ .../java/de/uka/ilkd/key/nparser/KeyAst.java | 14 ++++++++ .../TextualJMLAssertStatement.java | 15 ++++++-- .../uka/ilkd/key/speclang/njml/JmlFacade.java | 8 +++++ .../key/speclang/njml/TextualTranslator.java | 7 ++-- 8 files changed, 80 insertions(+), 17 deletions(-) diff --git a/key.core/src/main/antlr4/JmlLexer.g4 b/key.core/src/main/antlr4/JmlLexer.g4 index fc7f144bd34..986d9079b89 100644 --- a/key.core/src/main/antlr4/JmlLexer.g4 +++ b/key.core/src/main/antlr4/JmlLexer.g4 @@ -175,6 +175,8 @@ mode expr; /* Java keywords */ BOOLEAN: 'boolean'; BYTE: 'byte'; +CASE: 'case'; +DEFAULT: 'default'; FALSE: 'false'; INSTANCEOF: 'instanceof'; INT: 'int'; diff --git a/key.core/src/main/antlr4/JmlParser.g4 b/key.core/src/main/antlr4/JmlParser.g4 index 174acbf4331..cfde5a6124c 100644 --- a/key.core/src/main/antlr4/JmlParser.g4 +++ b/key.core/src/main/antlr4/JmlParser.g4 @@ -203,11 +203,23 @@ block_specification: method_specification; block_loop_specification: loop_contract_keyword spec_case ((also_keyword)+ loop_contract_keyword spec_case)*; loop_contract_keyword: LOOP_CONTRACT; -assert_statement: (ASSERT expression | UNREACHABLE) SEMI_TOPLEVEL; +assert_statement: (ASSERT expression | UNREACHABLE) (SEMI_TOPLEVEL | assertionProof); //breaks_clause: BREAKS expression; //continues_clause: CONTINUES expression; //returns_clause: RETURNS expression; +// --- proof scripts in JML +assertionProof: BY (proofCmd | LBRACE ( proofCmd )+ RBRACE) ; +proofCmd: + cmd=IDENT ( (argLabel=IDENT COLON)? proofArg )* + ( SEMI | BY proofCmd | LBRACE (( proofCmd )+ | proofCmdCase) RBRACE ) + ; +proofCmdCase: + CASE ( STRING_LITERAL )? COLON ( proofCmd )* + | DEFAULT COLON ( proofCmd )* + ; +proofArg: expression; +// --- mergeparamsspec: MERGE_PARAMS diff --git a/key.core/src/main/antlr4/KeYLexer.g4 b/key.core/src/main/antlr4/KeYLexer.g4 index 60113499f8f..73572088b3a 100644 --- a/key.core/src/main/antlr4/KeYLexer.g4 +++ b/key.core/src/main/antlr4/KeYLexer.g4 @@ -40,6 +40,7 @@ lexer grammar KeYLexer; } private Token tokenBackStorage = null; + // see: https://keyproject.github.io/key-docs/devel/NewKeyParser/#why-does-the-lexer-required-some-pieces-of-java-code @Override public void emit(Token token) { int MAX_K = 10; @@ -220,6 +221,7 @@ AXIOMS : '\\axioms'; PROBLEM : '\\problem'; CHOOSECONTRACT : '\\chooseContract'; PROOFOBLIGATION : '\\proofObligation'; +// for PROOF see: https://keyproject.github.io/key-docs/devel/NewKeyParser/#why-does-the-lexer-required-some-pieces-of-java-code PROOF : '\\proof'; PROOFSCRIPT : '\\proofScript'; CONTRACTS : '\\contracts'; diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java index dd5c3a67f0a..02d626c791f 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java @@ -8,11 +8,14 @@ import de.uka.ilkd.key.java.ast.PositionInfo; import de.uka.ilkd.key.java.ast.ProgramElement; import de.uka.ilkd.key.java.visitor.Visitor; -import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.speclang.jml.pretranslation.TextualJMLAssertStatement; - +import org.jspecify.annotations.Nullable; import org.key_project.util.ExtList; +import java.util.Objects; + +import de.uka.ilkd.key.nparser.KeyAst; + /** * A JML assert statement. * @@ -32,21 +35,25 @@ public class JmlAssert extends JavaStatement { /** * the condition in parse tree form */ - private KeyAst.Expression condition; + private final KeyAst.Expression condition; /** - * @param kind - * assert or assume - * @param condition - * the condition of this statement - * @param positionInfo - * the position information for this statement + * the assertion proof in parse tree form */ - public JmlAssert(TextualJMLAssertStatement.Kind kind, KeyAst.Expression condition, + private final KeyAst.@Nullable JMLProofScript assertionProof; + + /** + * @param kind assert or assume + * @param condition the condition of this statement + * @param assertionProof the optional proof for an assert statement (not for assume) + * @param positionInfo the position information for this statement + */ + public JmlAssert(TextualJMLAssertStatement.Kind kind, KeyAst.Expression condition, KeyAst.@Nullable JMLProofScript assertionProof, PositionInfo positionInfo) { super(positionInfo); this.kind = kind; this.condition = condition; + this.assertionProof = assertionProof; } /** @@ -57,10 +64,12 @@ public JmlAssert(ExtList children) { super(children); this.kind = Objects.requireNonNull(children.get(TextualJMLAssertStatement.Kind.class)); this.condition = Objects.requireNonNull(children.get(KeyAst.Expression.class)); + // script may be null + this.assertionProof = children.get(KeyAst.JMLProofScript.class); } public JmlAssert(JmlAssert other) { - this(other.kind, other.condition, other.getPositionInfo()); + this(other.kind, other.condition, other.assertionProof, other.getPositionInfo()); } public TextualJMLAssertStatement.Kind getKind() { @@ -152,6 +161,10 @@ protected int computeHashCode() { return System.identityHashCode(this); } + public KeyAst.@Nullable JMLProofScript getAssertionProof() { + return assertionProof; + } + @Override public int getChildCount() { return 0; diff --git a/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java b/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java index 22a4e64f10d..41663b5ba0e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java +++ b/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java @@ -213,6 +213,20 @@ public Expression(JmlParser.@NonNull ExpressionContext ctx) { } } + public static class JMLProofScript extends KeyAst { + public JMLProofScript(JmlParser.@NonNull AssertionProofContext ctx) { + super(ctx); + } + + public static JMLProofScript fromContext(JmlParser.AssertionProofContext ctx) { + if(ctx == null) { + return null; + } else { + return new JMLProofScript(ctx); + } + } + } + public static class Term extends KeyAst { Term(KeYParser.TermContext ctx) { diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/pretranslation/TextualJMLAssertStatement.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/pretranslation/TextualJMLAssertStatement.java index 849b3f45190..457c92792b1 100755 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/pretranslation/TextualJMLAssertStatement.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/pretranslation/TextualJMLAssertStatement.java @@ -4,22 +4,27 @@ package de.uka.ilkd.key.speclang.jml.pretranslation; import de.uka.ilkd.key.nparser.KeyAst; - -import org.key_project.util.collection.ImmutableSLList; - import org.antlr.v4.runtime.RuleContext; +import org.jspecify.annotations.Nullable; +import org.key_project.util.collection.ImmutableSLList; /** * A JML assert/assume statement. */ public class TextualJMLAssertStatement extends TextualJMLConstruct { private final KeyAst.Expression context; + private final KeyAst.@Nullable JMLProofScript assertionProof; private final Kind kind; public TextualJMLAssertStatement(Kind kind, KeyAst.Expression clause) { + this(kind, clause, null); + } + + public TextualJMLAssertStatement(Kind kind, KeyAst.Expression clause, KeyAst.@Nullable JMLProofScript assertionProof) { super(ImmutableSLList.nil(), kind.toString() + " " + clause); this.kind = kind; this.context = clause; + this.assertionProof = assertionProof; } public KeyAst.Expression getContext() { @@ -77,4 +82,8 @@ public String toString() { return name; } } + + public KeyAst.@Nullable JMLProofScript getAssertionProof() { + return assertionProof; + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlFacade.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlFacade.java index 80b93be0ce9..0099c13e0bf 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlFacade.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlFacade.java @@ -3,6 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.speclang.njml; +import java.io.IOException; import java.net.URI; import de.uka.ilkd.key.java.Position; @@ -114,4 +115,11 @@ private static ParserRuleContext getExpressionContext(JmlLexer lexer) { p.getErrorReporter().throwException(); return ctx; } + + public static void main(String[] args) throws IOException { + String input = new String(System.in.readAllBytes()); + var parser = createParser(createLexer(input)); + var tree = parser.methodlevel_comment(); + System.out.println(tree.toStringTree(parser)); + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java index 4ca3aa427d3..5360451a5a4 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java @@ -3,6 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.speclang.njml; +import de.uka.ilkd.key.java.transformations.pipeline.JMLTransformer; import de.uka.ilkd.key.java.transformations.pipeline.JMLTransformer; import de.uka.ilkd.key.ldt.HeapLDT; import de.uka.ilkd.key.logic.label.OriginTermLabel; @@ -549,8 +550,10 @@ public Object visitAssume_statement(JmlParser.Assume_statementContext ctx) { @Override public Object visitAssert_statement(JmlParser.Assert_statementContext ctx) { - TextualJMLAssertStatement b = new TextualJMLAssertStatement( - TextualJMLAssertStatement.Kind.ASSERT, new KeyAst.Expression(ctx.expression())); + TextualJMLAssertStatement b = + new TextualJMLAssertStatement(TextualJMLAssertStatement.Kind.ASSERT, + new KeyAst.Expression(ctx.expression()), + KeyAst.JMLProofScript.fromContext(ctx.assertionProof())); finishConstruct(b); return null; } From 9d4e01bd47defdc883aaeb059cfbdc0e9438c1ee Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Mon, 8 Sep 2025 13:26:57 +0200 Subject: [PATCH 02/90] first working example of a proof script in Java code (albeit with JavaDL arguments) (cherry-picked from earlier attempt to implement JML proof scripts) --- key.core/src/main/antlr4/JmlParser.g4 | 7 +- .../key/java/visitor/CreatingASTVisitor.java | 9 ++ .../de/uka/ilkd/key/pp/PrettyPrinter.java | 6 + .../uka/ilkd/key/scripts/BranchesCommand.java | 122 ++++++++++++++++++ .../de/uka/ilkd/key/scripts/EngineState.java | 11 ++ .../ilkd/key/scripts/ProofScriptEngine.java | 1 + .../ilkd/key/scripts/meta/ValueInjector.java | 2 +- .../TextualJMLAssertStatement.java | 3 +- .../de.uka.ilkd.key.macros.ProofMacro | 1 + ...de.uka.ilkd.key.scripts.ProofScriptCommand | 1 + 10 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java diff --git a/key.core/src/main/antlr4/JmlParser.g4 b/key.core/src/main/antlr4/JmlParser.g4 index cfde5a6124c..e6904fde610 100644 --- a/key.core/src/main/antlr4/JmlParser.g4 +++ b/key.core/src/main/antlr4/JmlParser.g4 @@ -208,17 +208,18 @@ assert_statement: (ASSERT expression | UNREACHABLE) (SEMI_TOPLEVEL | assertionPr //continues_clause: CONTINUES expression; //returns_clause: RETURNS expression; + // --- proof scripts in JML -assertionProof: BY (proofCmd | LBRACE ( proofCmd )+ RBRACE) ; +assertionProof: BY (proofCmd | LBRACE ( proofCmd )+ RBRACE) ; proofCmd: - cmd=IDENT ( (argLabel=IDENT COLON)? proofArg )* + cmd=IDENT ( proofArg )* ( SEMI | BY proofCmd | LBRACE (( proofCmd )+ | proofCmdCase) RBRACE ) ; proofCmdCase: CASE ( STRING_LITERAL )? COLON ( proofCmd )* | DEFAULT COLON ( proofCmd )* ; -proofArg: expression; +proofArg: (argLabel=IDENT COLON)? expression; // --- mergeparamsspec: diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java b/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java index e79746808de..e2098f34e52 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java @@ -1519,12 +1519,21 @@ public void performActionOnJmlAssert(JmlAssert x) { ProgramElement createNewElement(ExtList changeList) { changeList.add(x.getKind()); changeList.add(x.getCondition()); + changeList.add(x.getAssertionProof()); return new JmlAssert(changeList); } }; def.doAction(x); } + // XXX Is this still needed (merge artifact) + @Override + public void performActionOnJmlAssertCondition(final Term cond) { + // should only be called by walk(), which puts an ExtList on the stack + assert stack.peek() != null; + stack.peek().add(cond); + } + /** * returns the position of pe2 in the virtual child array of pe1 * diff --git a/key.core/src/main/java/de/uka/ilkd/key/pp/PrettyPrinter.java b/key.core/src/main/java/de/uka/ilkd/key/pp/PrettyPrinter.java index 5551e134e2d..78818a43e31 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/pp/PrettyPrinter.java +++ b/key.core/src/main/java/de/uka/ilkd/key/pp/PrettyPrinter.java @@ -1962,6 +1962,12 @@ public void performActionOnJmlAssert(JmlAssert jmlAssert) { layouter.print(text); } } + + if (jmlAssert.getAssertionProof() != null) { + // For now: Just say that there is a script. It can be seen in the source pane anyways. + layouter.print(" \\by ..."); + } + layouter.end(); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java new file mode 100644 index 00000000000..f8b283237a9 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java @@ -0,0 +1,122 @@ +package de.uka.ilkd.key.macros.scripts; + +import de.uka.ilkd.key.logic.Semisequent; +import de.uka.ilkd.key.logic.Sequent; +import de.uka.ilkd.key.logic.SequentFormula; +import de.uka.ilkd.key.logic.Term; +import de.uka.ilkd.key.macros.scripts.meta.Option; +import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.proof.Node; +import de.uka.ilkd.key.proof.Proof; +import org.key_project.util.collection.ImmutableList; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Stack; +import java.util.function.Function; + +public class BranchesCommand extends AbstractCommand { + public BranchesCommand() { + super(Parameters.class); + } + + @Override + public Parameters evaluateArguments(EngineState state, Map arguments) + throws Exception { + return state.getValueInjector().inject(this, new Parameters(), arguments); + } + + @Override + public void execute(Parameters args) throws ScriptException, InterruptedException { + Stack stack = (Stack) state.getUserData("_branchStack"); + if (stack == null) { + stack = new Stack<>(); + state.putUserData("_branchStack", stack); + } + + switch (args.mode) { + case "push": + Node node = state.getFirstOpenAutomaticGoal().node(); + // this is the first goal. The parent is the decision point + node = node.parent(); + stack.push(node.serialNr()); + break; + case "pop": + stack.pop(); + break; + case "select": + Node root = findNodeByNumber(proof, stack.peek()); + Goal goal; + if (args.branch == null) { + goal = findGoalByNode(state.getProof(), root.child(args.child)); + } else { + goal = findGoalByName(root, args.branch); + } + state.setGoal(goal); + break; + default: + throw new ScriptException(); + } + } + + private Goal findGoalByName(Node root, String branch) throws ScriptException { + Iterator it = root.childrenIterator(); + List knownBranchLabels = new ArrayList<>(); + while (it.hasNext()) { + Node node = it.next(); + String label = node.getNodeInfo().getBranchLabel(); + knownBranchLabels.add(label); + if (branch.equals(label)) { + return findGoalByNode(root.proof(), node); + } + } + throw new ScriptException( + "Unknown branch " + branch + ". Known branches are " + knownBranchLabels); + } + + private static Goal findGoalByNode(Proof proof, Node node) throws ScriptException { + Optional result = + proof.openEnabledGoals().stream().filter(g -> g.node() == node).findAny(); + if (result.isEmpty()) { + throw new ScriptException(); + } + return result.get(); + } + + private Node findNodeByNumber(Proof proof, int serial) throws ScriptException { + Deque todo = new LinkedList<>(); + todo.add(proof.root()); + while (!todo.isEmpty()) { + Node n = todo.remove(); + if (n.serialNr() == serial) { + return n; + } + Iterator it = n.childrenIterator(); + while (it.hasNext()) { + todo.add(it.next()); + } + } + throw new ScriptException(); + } + + @Override + public String getName() { + return "branches"; + } + + public static class Parameters { + /** A formula defining the goal to select */ + @Option(value = "#2", required = true) + public String mode; + @Option(value = "branch", required = false) + public String branch; + @Option(value = "child", required = false) + public int child; + } + +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java index ad91a17dca5..275ea656e7b 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java @@ -6,6 +6,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Deque; +import java.util.HashMap; import java.util.LinkedList; import java.util.Objects; import java.util.Optional; @@ -61,6 +62,8 @@ public class EngineState { private @Nullable Goal goal; private @Nullable Node lastSetGoalNode; + private final HashMap userData = new HashMap<>(); + /** * If set to true, outputs all commands to observers and console. Otherwise, only shows explicit * echo messages. @@ -362,4 +365,12 @@ public NamespaceSet getCurrentNamespaces() { public ExprEvaluator getEvaluator() { return exprEvaluator; } + + public void putUserData(String key, Object val) { + userData.put(key, val); + } + + public Object getUserData(String key) { + return userData.get(key); + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java index 4212e60ec03..59a0eb98bf2 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java @@ -154,6 +154,7 @@ public void execute(AbstractUserInterfaceControl uiControl, List Date: Mon, 30 Jan 2023 18:58:25 +0100 Subject: [PATCH 03/90] added missing files for script aware macros (cherry-picked from earlier attempt to implement JML proof scripts) --- .../ilkd/key/macros/ApplyScriptsMacro.java | 147 ++++++++++++++++++ .../ilkd/key/macros/ScriptAwareAutoMacro.java | 109 +++++++++++++ .../uka/ilkd/key/macros/ScriptAwareMacro.java | 61 ++++++++ 3 files changed, 317 insertions(+) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java create mode 100644 key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareAutoMacro.java create mode 100644 key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java new file mode 100644 index 00000000000..2adc33c57c3 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -0,0 +1,147 @@ +package de.uka.ilkd.key.macros; + +import de.uka.ilkd.key.control.AbstractUserInterfaceControl; +import de.uka.ilkd.key.control.UserInterfaceControl; +import de.uka.ilkd.key.java.JavaTools; +import de.uka.ilkd.key.java.SourceElement; +import de.uka.ilkd.key.java.statement.JmlAssert; +import de.uka.ilkd.key.logic.PosInOccurrence; +import de.uka.ilkd.key.logic.Term; +import de.uka.ilkd.key.logic.op.UpdateApplication; +import de.uka.ilkd.key.macros.scripts.ProofScriptEngine; +import de.uka.ilkd.key.parser.Location; +import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.prover.ProverTaskListener; +import de.uka.ilkd.key.rule.JmlAssertBuiltInRuleApp; +import de.uka.ilkd.key.rule.JmlAssertRule; +import de.uka.ilkd.key.rule.RuleApp; +import de.uka.ilkd.key.speclang.njml.JmlParser.AssertionProofContext; +import de.uka.ilkd.key.speclang.njml.JmlParser.ProofArgContext; +import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdCaseContext; +import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdContext; +import org.key_project.util.collection.ImmutableList; + +import java.net.URL; +import java.util.Map; + +public class ApplyScriptsMacro extends AbstractProofMacro { + + private final ProofMacro fallBackMacro; + + public ApplyScriptsMacro(ProofMacro fallBackMacro) { + this.fallBackMacro = fallBackMacro; + } + + @Override + public String getName() { + return "null"; + } + + @Override + public String getCategory() { + return "null"; + } + + @Override + public String getDescription() { + return "null"; + } + + @Override + public boolean canApplyTo(Proof proof, ImmutableList goals, PosInOccurrence posInOcc) { + return fallBackMacro.canApplyTo(proof, goals, posInOcc) + || goals.exists(g -> getScript(g) != null); + } + + private static AssertionProofContext getScript(Goal goal) { + RuleApp ruleApp = goal.node().parent().getAppliedRuleApp(); + if (ruleApp instanceof JmlAssertBuiltInRuleApp) { + Term target = ruleApp.posInOccurrence().subTerm(); + if (target.op() instanceof UpdateApplication) { + target = UpdateApplication.getTarget(target); + } + final SourceElement activeStatement = JavaTools.getActiveStatement(target.javaBlock()); + if (activeStatement instanceof JmlAssert) { + return ((JmlAssert) activeStatement).getAssertionProof(); + } + } + return null; + } + + @Override + public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, + ImmutableList goals, PosInOccurrence posInOcc, ProverTaskListener listener) + throws InterruptedException, Exception { + for (Goal goal : goals) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + AssertionProofContext proofCtx = getScript(goal); + if (proofCtx == null) { + fallBackMacro.applyTo(uic, proof, ImmutableList.of(goal), posInOcc, listener); + continue; + } + String renderedProof = renderProof(proofCtx); + // TODO get this location from the jmlAssertion statement ... + Location loc = new Location(new URL("file:///tmp/unknown.key"), 42, 42); + ProofScriptEngine pse = new ProofScriptEngine(renderedProof, loc, goal); + System.out.println("---- Script"); + System.out.println(renderedProof); + pse.execute((AbstractUserInterfaceControl) uic, proof); + } + return new ProofMacroFinishedInfo(this, proof); + } + + private static String renderProof(AssertionProofContext ctx) { + StringBuilder sb = new StringBuilder(); + for (ProofCmdContext proofCmdContext : ctx.proofCmd()) { + renderProofCmd(proofCmdContext, sb); + } + return sb.toString(); + } + + private static void renderProofCmd(ProofCmdContext ctx, StringBuilder sb) { + if (ctx.cmd != null) { + sb.append(ctx.cmd.getText()).append(" "); + for (ProofArgContext arg : ctx.proofArg()) { + if (arg.argLabel != null) { + sb.append(arg.argLabel.getText()).append("="); + } + sb.append(arg.token.getText()).append(" "); + } + sb.append(";\n"); + + } else if (ctx.assertion != null) { + sb.append("cut ").append(ctx.assertion).append(";\n"); + sb.append("branches \"push\";\n"); + sb.append("branches \"select\" child=0;\n"); + if (ctx.proofCmd().isEmpty()) { + sb.append("auto;\n"); + } else { + for (ProofCmdContext proofCmdContext : ctx.proofCmd()) { + renderProofCmd(proofCmdContext, sb); + } + } + sb.append("branches \"select\" child=1;\n"); + sb.append("branches \"pop\";\n"); + + } else if (!ctx.proofCmdCase().isEmpty()) { + sb.append("branches \"push\";\n"); + int no = 0; + for (ProofCmdCaseContext caseContext : ctx.proofCmdCase()) { + if (caseContext.STRING_LITERAL() != null) { + sb.append("branches \"select\" branch=") + .append(caseContext.STRING_LITERAL().getText()).append(";\n"); + } else { + sb.append("branches \"select\" child=").append(no++).append(";\n"); + } + for (ProofCmdContext proofCmdContext : caseContext.proofCmd()) { + renderProofCmd(proofCmdContext, sb); + } + } + sb.append("branches \"pop\";\n"); + } + + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareAutoMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareAutoMacro.java new file mode 100644 index 00000000000..532d2ca7a2e --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareAutoMacro.java @@ -0,0 +1,109 @@ +//// This file is part of KeY - Integrated Deductive Software Design +//// +//// Copyright (C) 2001-2011 Universitaet Karlsruhe (TH), Germany +//// Universitaet Koblenz-Landau, Germany +//// Chalmers University of Technology, Sweden +//// Copyright (C) 2011-2014 Karlsruhe Institute of Technology, Germany +//// Technical University Darmstadt, Germany +//// Chalmers University of Technology, Sweden +//// +//// The KeY system is protected by the GNU General +//// Public License. See LICENSE.TXT for details. +//// +// +// package de.uka.ilkd.key.macros; +// +// import de.uka.ilkd.key.java.ProofCommandStatement; +// import de.uka.ilkd.key.logic.Name; +// import de.uka.ilkd.key.logic.PosInOccurrence; +// import de.uka.ilkd.key.proof.Goal; +// import de.uka.ilkd.key.proof.Proof; +// import de.uka.ilkd.key.rule.ProofCommandStatementRule; +// import de.uka.ilkd.key.rule.RuleApp; +// import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdContext; +// import de.uka.ilkd.key.strategy.RuleAppCost; +// import de.uka.ilkd.key.strategy.RuleAppCostCollector; +// import de.uka.ilkd.key.strategy.Strategy; +// +// import java.util.IdentityHashMap; +// import java.util.Map; +// +// public class ScriptAwareAutoMacro extends StrategyProofMacro { +// +// public ScriptAwareAutoMacro() { super(); } +// +// private Map detectedProofs = new IdentityHashMap<>(); +// +// @Override +// public String getName() { +// return "Script-aware auto mode"; +// } +// +// @Override +// public String getCategory() { +// return "Auto Pilot"; +// } +// +// @Override +// public String getDescription() { +// return "TODO"; +// } +// +// public Map getDetectedProofs() { +// return detectedProofs; +// } +// +// @Override +// public String getScriptCommandName() { +// return "script-auto"; +// } +// +// private static final Name NAME = new Name("Script-aware filter strategy"); +// +// private class ScriptAwareStrategy implements Strategy { +// +// private final Strategy delegate; +// +// public ScriptAwareStrategy(Proof proof, PosInOccurrence posInOcc) { +// this.delegate = proof.getActiveStrategy(); +// } +// +// @Override +// public Name name() { +// return NAME; +// } +// +// @Override +// public boolean isApprovedApp(RuleApp app, PosInOccurrence pio, Goal goal) { +// if (detectedProofs.containsKey(goal)) { +// // we had found a command earlier. +// return false; +// } +// if (app.rule() instanceof ProofCommandStatementRule) { +// detectedProofs.put(goal, ProofCommandStatementRule.getCommand(pio)); +// } +// return delegate.isApprovedApp(app, pio, goal); +// } +// +// @Override +// public void instantiateApp(RuleApp app, PosInOccurrence pio, Goal goal, +// RuleAppCostCollector collector) { +// delegate.instantiateApp(app, pio, goal, collector); +// } +// +// @Override +// public boolean isStopAtFirstNonCloseableGoal() { +// return false; +// } +// +// @Override +// public RuleAppCost computeCost(RuleApp app, PosInOccurrence pos, Goal goal) { +// return delegate.computeCost(app, pos, goal); +// } +// } +// +// @Override +// protected Strategy createStrategy(Proof proof, PosInOccurrence posInOcc) { +// return new ScriptAwareStrategy(proof, posInOcc); +// } +// } diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java new file mode 100644 index 00000000000..2dfebc32b83 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java @@ -0,0 +1,61 @@ +// This file is part of KeY - Integrated Deductive Software Design +// +// Copyright (C) 2001-2011 Universitaet Karlsruhe (TH), Germany +// Universitaet Koblenz-Landau, Germany +// Chalmers University of Technology, Sweden +// Copyright (C) 2011-2014 Karlsruhe Institute of Technology, Germany +// Technical University Darmstadt, Germany +// Chalmers University of Technology, Sweden +// +// The KeY system is protected by the GNU General +// Public License. See LICENSE.TXT for details. +// + +package de.uka.ilkd.key.macros; + +/** + * This class captures a proof macro which is meant to fully automise KeY proof + * workflow. + * + * It is experimental. + * + * It performs the following steps: + *
    + *
  1. Finish symbolic execution + *
  2. >Separate proof obligations" + + *
  3. Expand invariant definitions + *
  4. Try to close all proof obligations + *
+ * + * @author mattias ulbrich + */ +public class ScriptAwareMacro extends SequentialProofMacro { + + private final ProofMacro autoMacro = new FinishSymbolicExecutionMacro(); + private final ApplyScriptsMacro applyMacro = new ApplyScriptsMacro(new TryCloseMacro()); + + @Override + public String getScriptCommandName() { + return "script-auto"; + } + + @Override + public String getName() { + return "Script-aware Auto"; + } + + @Override + public String getCategory() { + return "Auto Pilot"; + } + + @Override + public String getDescription() { + return "TODO"; + } + + @Override + protected ProofMacro[] createProofMacroArray() { + return new ProofMacro[] { autoMacro, applyMacro }; + } +} From 62319c74d70cf920a74314375dfbd84a4171099b Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Mon, 8 Sep 2025 15:50:45 +0200 Subject: [PATCH 04/90] more infrastructure for proof scripts # Conflicts: # key.core/src/main/java/de/uka/ilkd/key/logic/Semisequent.java # key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java # key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java # key.core/src/main/java/de/uka/ilkd/key/strategy/StrategyProperties.java --- key.core/src/main/antlr4/JmlParser.g4 | 4 +- .../key/java/ast/statement/JmlAssert.java | 23 ++++ .../key/java/visitor/CreatingASTVisitor.java | 8 -- .../ilkd/key/macros/ApplyScriptsMacro.java | 123 +++++++++--------- .../java/de/uka/ilkd/key/nparser/KeyAst.java | 42 +++++- .../de/uka/ilkd/key/rule/JmlAssertRule.java | 3 +- .../uka/ilkd/key/scripts/BranchesCommand.java | 27 ++-- .../uka/ilkd/key/scripts/meta/Converter.java | 11 +- .../ilkd/key/scripts/meta/ValueInjector.java | 9 +- .../jml/translation/JMLSpecFactory.java | 4 +- 10 files changed, 152 insertions(+), 102 deletions(-) diff --git a/key.core/src/main/antlr4/JmlParser.g4 b/key.core/src/main/antlr4/JmlParser.g4 index e6904fde610..b3512d7c7c6 100644 --- a/key.core/src/main/antlr4/JmlParser.g4 +++ b/key.core/src/main/antlr4/JmlParser.g4 @@ -213,10 +213,10 @@ assert_statement: (ASSERT expression | UNREACHABLE) (SEMI_TOPLEVEL | assertionPr assertionProof: BY (proofCmd | LBRACE ( proofCmd )+ RBRACE) ; proofCmd: cmd=IDENT ( proofArg )* - ( SEMI | BY proofCmd | LBRACE (( proofCmd )+ | proofCmdCase) RBRACE ) + ( SEMI | BY proofCmd | LBRACE (proofCmd+ | proofCmdCase+) RBRACE ) ; proofCmdCase: - CASE ( STRING_LITERAL )? COLON ( proofCmd )* + CASE ( label=STRING_LITERAL )? COLON ( proofCmd )* | DEFAULT COLON ( proofCmd )* ; proofArg: (argLabel=IDENT COLON)? expression; diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java index 02d626c791f..0fd8208357b 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java @@ -8,13 +8,21 @@ import de.uka.ilkd.key.java.ast.PositionInfo; import de.uka.ilkd.key.java.ast.ProgramElement; import de.uka.ilkd.key.java.visitor.Visitor; +import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.speclang.jml.pretranslation.TextualJMLAssertStatement; +import de.uka.ilkd.key.speclang.njml.LabeledParserRuleContext; +import org.antlr.v4.runtime.ParserRuleContext; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.key_project.util.ExtList; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import de.uka.ilkd.key.nparser.KeyAst; +import org.key_project.util.collection.ImmutableList; +import org.key_project.util.collection.Immutables; /** * A JML assert statement. @@ -179,4 +187,19 @@ public ProgramElement getChildAt(int index) { public void visit(Visitor v) { v.performActionOnJmlAssert(this); } + + /** + * This method collects all terms contained in this assertion. This is at least the condition. + * If there is a proof script, all terms in the proof script are collected as well. + * + * @return a freshly created list of at least one term + */ + public @NonNull ImmutableList collectTerms() { + ImmutableList result = ImmutableList.of(); + if(assertionProof != null) { + result = result.prepend(assertionProof.collectTerms()); + } + result = result.prepend(condition.ctx); + return result; + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java b/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java index e2098f34e52..684b641f125 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java @@ -1526,14 +1526,6 @@ ProgramElement createNewElement(ExtList changeList) { def.doAction(x); } - // XXX Is this still needed (merge artifact) - @Override - public void performActionOnJmlAssertCondition(final Term cond) { - // should only be called by walk(), which puts an ExtList on the stack - assert stack.peek() != null; - stack.peek().add(cond); - } - /** * returns the position of pe2 in the virtual child array of pe1 * diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 2adc33c57c3..f032e190773 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -3,26 +3,31 @@ import de.uka.ilkd.key.control.AbstractUserInterfaceControl; import de.uka.ilkd.key.control.UserInterfaceControl; import de.uka.ilkd.key.java.JavaTools; +import de.uka.ilkd.key.java.Position; import de.uka.ilkd.key.java.SourceElement; import de.uka.ilkd.key.java.statement.JmlAssert; -import de.uka.ilkd.key.logic.PosInOccurrence; -import de.uka.ilkd.key.logic.Term; +import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.op.UpdateApplication; -import de.uka.ilkd.key.macros.scripts.ProofScriptEngine; +import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.parser.Location; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Proof; -import de.uka.ilkd.key.prover.ProverTaskListener; import de.uka.ilkd.key.rule.JmlAssertBuiltInRuleApp; -import de.uka.ilkd.key.rule.JmlAssertRule; -import de.uka.ilkd.key.rule.RuleApp; -import de.uka.ilkd.key.speclang.njml.JmlParser.AssertionProofContext; +import de.uka.ilkd.key.scripts.ProofScriptEngine; +import de.uka.ilkd.key.scripts.ScriptCommandAst; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofArgContext; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdCaseContext; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdContext; +import org.jspecify.annotations.NonNull; +import org.key_project.prover.engine.ProverTaskListener; +import org.key_project.prover.rules.RuleApp; +import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.util.collection.ImmutableList; -import java.net.URL; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; public class ApplyScriptsMacro extends AbstractProofMacro { @@ -49,21 +54,21 @@ public String getDescription() { } @Override - public boolean canApplyTo(Proof proof, ImmutableList goals, PosInOccurrence posInOcc) { + public boolean canApplyTo(Proof proof, ImmutableList<@NonNull Goal> goals, PosInOccurrence posInOcc) { return fallBackMacro.canApplyTo(proof, goals, posInOcc) || goals.exists(g -> getScript(g) != null); } - private static AssertionProofContext getScript(Goal goal) { + private static KeyAst.JMLProofScript getScript(Goal goal) { RuleApp ruleApp = goal.node().parent().getAppliedRuleApp(); if (ruleApp instanceof JmlAssertBuiltInRuleApp) { - Term target = ruleApp.posInOccurrence().subTerm(); + JTerm target = (JTerm) ruleApp.posInOccurrence().subTerm(); if (target.op() instanceof UpdateApplication) { target = UpdateApplication.getTarget(target); } final SourceElement activeStatement = JavaTools.getActiveStatement(target.javaBlock()); - if (activeStatement instanceof JmlAssert) { - return ((JmlAssert) activeStatement).getAssertionProof(); + if (activeStatement instanceof JmlAssert jmlAssert) { + return jmlAssert.getAssertionProof(); } } return null; @@ -77,15 +82,15 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, if (Thread.interrupted()) { throw new InterruptedException(); } - AssertionProofContext proofCtx = getScript(goal); - if (proofCtx == null) { + KeyAst.JMLProofScript proofScript = getScript(goal); + if (proofScript == null) { fallBackMacro.applyTo(uic, proof, ImmutableList.of(goal), posInOcc, listener); continue; } - String renderedProof = renderProof(proofCtx); + List renderedProof = renderProof(proofScript); // TODO get this location from the jmlAssertion statement ... - Location loc = new Location(new URL("file:///tmp/unknown.key"), 42, 42); - ProofScriptEngine pse = new ProofScriptEngine(renderedProof, loc, goal); + Location loc = new Location(new URI("unknown:XXX"), Position.UNDEFINED); + ProofScriptEngine pse = new ProofScriptEngine(renderedProof, goal); System.out.println("---- Script"); System.out.println(renderedProof); pse.execute((AbstractUserInterfaceControl) uic, proof); @@ -93,55 +98,53 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, return new ProofMacroFinishedInfo(this, proof); } - private static String renderProof(AssertionProofContext ctx) { - StringBuilder sb = new StringBuilder(); - for (ProofCmdContext proofCmdContext : ctx.proofCmd()) { - renderProofCmd(proofCmdContext, sb); + private static List renderProof(KeyAst.JMLProofScript script) { + List result = new ArrayList<>(); + for (ProofCmdContext proofCmdContext : script.ctx.proofCmd()) { + result.addAll(renderProofCmd(proofCmdContext)); } - return sb.toString(); + return result; } - private static void renderProofCmd(ProofCmdContext ctx, StringBuilder sb) { - if (ctx.cmd != null) { - sb.append(ctx.cmd.getText()).append(" "); - for (ProofArgContext arg : ctx.proofArg()) { - if (arg.argLabel != null) { - sb.append(arg.argLabel.getText()).append("="); - } - sb.append(arg.token.getText()).append(" "); - } - sb.append(";\n"); - - } else if (ctx.assertion != null) { - sb.append("cut ").append(ctx.assertion).append(";\n"); - sb.append("branches \"push\";\n"); - sb.append("branches \"select\" child=0;\n"); - if (ctx.proofCmd().isEmpty()) { - sb.append("auto;\n"); + private static List renderProofCmd(ProofCmdContext ctx) { + List result = new ArrayList<>(); + + // Push the current branch context + result.add(new ScriptCommandAst("branches", Map.of(), List.of("push"))); + + // Compose the command itself + Map named = new HashMap<>(); + List positional = new ArrayList<>(); + for (ProofArgContext argContext : ctx.proofArg()) { + // FIXME: actually render the argument value. This is just a placeholder + var value = argContext.expression().getText(); + if (argContext.argLabel != null) { + named.put(argContext.argLabel.getText(), value); } else { - for (ProofCmdContext proofCmdContext : ctx.proofCmd()) { - renderProofCmd(proofCmdContext, sb); - } + positional.add(value); } - sb.append("branches \"select\" child=1;\n"); - sb.append("branches \"pop\";\n"); - - } else if (!ctx.proofCmdCase().isEmpty()) { - sb.append("branches \"push\";\n"); - int no = 0; - for (ProofCmdCaseContext caseContext : ctx.proofCmdCase()) { - if (caseContext.STRING_LITERAL() != null) { - sb.append("branches \"select\" branch=") - .append(caseContext.STRING_LITERAL().getText()).append(";\n"); - } else { - sb.append("branches \"select\" child=").append(no++).append(";\n"); - } - for (ProofCmdContext proofCmdContext : caseContext.proofCmd()) { - renderProofCmd(proofCmdContext, sb); - } + } + result.add(new ScriptCommandAst(ctx.cmd.getText(), named, positional, null, Location.fromToken(ctx.start))); + + // handle proofCmd if present + if(!ctx.proofCmd().isEmpty()) { + result.add(new ScriptCommandAst("branches", Map.of("child", 0), List.of("select"))); + for (ProofCmdContext proofCmdContext : ctx.proofCmd()) { + result.addAll(renderProofCmd(proofCmdContext)); } - sb.append("branches \"pop\";\n"); } + // handle proofCmdCase if present + for (ProofCmdCaseContext pcase : ctx.proofCmdCase()) { + result.add(new ScriptCommandAst("branches", Map.of("name", pcase.label), List.of("select"))); + for (ProofCmdContext proofCmdContext : pcase.proofCmd()) { + result.addAll(renderProofCmd(proofCmdContext)); + } + } + + // Pop the branch stack + result.add(new ScriptCommandAst("branches", Map.of(), List.of("pop"))); + + return result; } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java b/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java index 41663b5ba0e..6a1ebc43ba0 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java +++ b/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java @@ -24,6 +24,7 @@ import de.uka.ilkd.key.settings.ProofSettings; import de.uka.ilkd.key.speclang.njml.JmlParser; +import org.key_project.util.collection.ImmutableList; import org.key_project.util.java.StringUtil; import org.antlr.v4.runtime.CharStream; @@ -48,7 +49,7 @@ */ public abstract class KeyAst { - final @NonNull T ctx; + public final @NonNull T ctx; protected KeyAst(@NonNull T ctx) { this.ctx = ctx; @@ -225,6 +226,45 @@ public static JMLProofScript fromContext(JmlParser.AssertionProofContext ctx) { return new JMLProofScript(ctx); } } + + /** + * Collect all JML expressions in a script (and potentially sub-blocks) + * @param cmd the command to collect from + * @return a list in reverse(!) order of all expressions in cmd + */ + private static ImmutableList collectTerms(JmlParser.ProofCmdContext cmd) { + ImmutableList result = ImmutableList.of(); + for (JmlParser.ProofArgContext arg : cmd.proofArg()) { + JmlParser.ExpressionContext exp = arg.expression(); + if (exp != null && exp.start.getType() != JmlParser.STRING_LITERAL) { + result = result.prepend(exp); + } + } + for (JmlParser.ProofCmdContext childCmd : cmd.proofCmd()) { + result = result.prepend(collectTerms(childCmd)); + } + if (cmd.proofCmdCase() != null) { + for (JmlParser.ProofCmdCaseContext pcase : cmd.proofCmdCase()) { + for (JmlParser.ProofCmdContext childCmd : pcase.proofCmd()) { + result = result.prepend(collectTerms(childCmd)); + } + } + } + return result; + } + + /** + * returns a list of all term parse trees in this proof script. + * + * Todo: Consider caching the result if this is called very often. + */ + public @NonNull ImmutableList collectTerms() { + ImmutableList result = ImmutableList.of(); + for (JmlParser.ProofCmdContext cmd : ctx.proofCmd()) { + result.prepend(collectTerms(cmd)); + } + return result.reverse(); + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java b/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java index 236ba196b9d..802952a4f5d 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java +++ b/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java @@ -15,6 +15,7 @@ import de.uka.ilkd.key.logic.op.Transformer; import de.uka.ilkd.key.logic.op.UpdateApplication; import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.proof.mgt.SpecificationRepository; import de.uka.ilkd.key.speclang.jml.pretranslation.TextualJMLAssertStatement.Kind; import de.uka.ilkd.key.util.MiscTools; @@ -132,7 +133,7 @@ public IBuiltInRuleApp createApp(PosInOccurrence occurrence, TermServices servic final MethodFrame frame = JavaTools.getInnermostMethodFrame(target.javaBlock(), services); final JTerm self = MiscTools.getSelfTerm(frame, services); - final var spec = services.getSpecificationRepository().getStatementSpec(jmlAssert); + final SpecificationRepository.JmlStatementSpec spec = services.getSpecificationRepository().getStatementSpec(jmlAssert); if (spec == null) { throw new RuleAbortException( diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java index f8b283237a9..4e47d1bb3ab 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java @@ -1,38 +1,27 @@ -package de.uka.ilkd.key.macros.scripts; +package de.uka.ilkd.key.scripts; -import de.uka.ilkd.key.logic.Semisequent; -import de.uka.ilkd.key.logic.Sequent; -import de.uka.ilkd.key.logic.SequentFormula; -import de.uka.ilkd.key.logic.Term; -import de.uka.ilkd.key.macros.scripts.meta.Option; +import de.uka.ilkd.key.scripts.meta.Option; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Node; import de.uka.ilkd.key.proof.Proof; -import org.key_project.util.collection.ImmutableList; import java.util.ArrayList; import java.util.Deque; import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Stack; -import java.util.function.Function; -public class BranchesCommand extends AbstractCommand { +public class BranchesCommand extends AbstractCommand { public BranchesCommand() { super(Parameters.class); } @Override - public Parameters evaluateArguments(EngineState state, Map arguments) - throws Exception { - return state.getValueInjector().inject(this, new Parameters(), arguments); - } + public void execute(ScriptCommandAst arguments) throws ScriptException, InterruptedException { + var args = state().getValueInjector().inject(new BranchesCommand.Parameters(), arguments); - @Override - public void execute(Parameters args) throws ScriptException, InterruptedException { Stack stack = (Stack) state.getUserData("_branchStack"); if (stack == null) { stack = new Stack<>(); @@ -111,11 +100,11 @@ public String getName() { public static class Parameters { /** A formula defining the goal to select */ - @Option(value = "#2", required = true) + @Option(value = "#2") public String mode; - @Option(value = "branch", required = false) + @Option(value = "branch") public String branch; - @Option(value = "child", required = false) + @Option(value = "child") public int child; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Converter.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Converter.java index ec20894856f..209bd050b01 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Converter.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Converter.java @@ -6,16 +6,17 @@ /** * A {@link Converter} translates an instance of {@code R} to an instance of {@code T}. * - * @param + * @param the result type + * @param the source type * @author Alexander Weigl */ public interface Converter { /** - * Translates the textual representation given in {@code s} to an instance of {@code T}. + * Translates one representation given in {@code s} to an instance of {@code R}. * - * @param s a non-null string - * @return an corresponding instance of T - * @throws Exception if there is an error during the translation (format incorrent etc..) + * @param s a non-null argument to convert + * @return a corresponding instance of T after conversion + * @throws Exception if there is an error during the translation (format incorrect etc ...) */ R convert(T s) throws Exception; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java index 66c80aa8603..94e95310a9c 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java @@ -301,10 +301,11 @@ public void addConverter(Converter conv) { /** * Finds a converter for the given class. * - * @param an arbitrary type - * @param ret a non-null class - * @param arg - * @return null or a suitable converter (registered) converter for the requested class. + * @param the result type + * @param the source type + * @param ret the result type class + * @param arg the source type class + * @return a suitable converter (registered) converter for the requested class. null if no such converter is known. */ @SuppressWarnings("unchecked") public @Nullable Converter getConverter(Class ret, Class arg) { diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/translation/JMLSpecFactory.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/translation/JMLSpecFactory.java index 03bd1d3cafa..af6200a0604 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/translation/JMLSpecFactory.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/translation/JMLSpecFactory.java @@ -1530,10 +1530,10 @@ public void translateJmlAssertCondition(final JmlAssert jmlAssert, final IProgra .exceptionVariable(pv.excVar) .atPres(pv.atPres) .atBefore(pv.atBefores); - JTerm expr = io.translateTerm(jmlAssert.getCondition()); + ImmutableList terms = jmlAssert.collectTerms().map(io::translateTerm); services.getSpecificationRepository().addStatementSpec( jmlAssert, - new SpecificationRepository.JmlStatementSpec(pv, ImmutableList.of(expr))); + new SpecificationRepository.JmlStatementSpec(pv, terms)); } public @Nullable String checkSetStatementAssignee(JTerm assignee) { From 5f0e7d2589da7b43d314a2253ac0b08d2ee13175 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sat, 4 Feb 2023 16:06:14 +0100 Subject: [PATCH 05/90] more infrastructure for proof scripts (cherry-picked from earlier attempt to implement JML proof scripts) --- .../ilkd/key/macros/ApplyScriptsMacro.java | 3 ++ .../de/uka/ilkd/key/scripts/RuleCommand.java | 13 ++++--- .../de/uka/ilkd/key/scripts/SetCommand.java | 34 +++++++++++++++++-- .../ilkd/key/strategy/StrategyProperties.java | 3 +- .../prover/sequent/Semisequent.java | 6 ++++ 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index f032e190773..5e349551286 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -10,6 +10,9 @@ import de.uka.ilkd.key.logic.op.UpdateApplication; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.parser.Location; +import de.uka.ilkd.key.pp.LogicPrinter; +import de.uka.ilkd.key.pp.NotationInfo; +import de.uka.ilkd.key.pp.ProgramPrinter; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.rule.JmlAssertBuiltInRuleApp; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java index 8c2b4796c40..70592408954 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java @@ -249,7 +249,7 @@ private IBuiltInRuleApp builtInRuleApp(Parameters p, EngineState state, BuiltInR return matchingApps.get(0); } else { if (p.occ >= matchingApps.size()) { - throw new ScriptException("Occurence " + p.occ + throw new ScriptException("Occurrence " + p.occ + " has been specified, but there are only " + matchingApps.size() + " hits."); } @@ -290,7 +290,7 @@ private ImmutableList findBuiltInRuleApps(Parameters p, EngineS ImmutableList allApps = ImmutableSLList.nil(); for (SequentFormula sf : g.node().sequent().antecedent()) { - if (!isFormulaSearchedFor(p, sf, services)) { + if (!isSequentFormulaSearchedFor(p, sf, services)) { continue; } @@ -299,7 +299,7 @@ private ImmutableList findBuiltInRuleApps(Parameters p, EngineS } for (SequentFormula sf : g.node().sequent().succedent()) { - if (!isFormulaSearchedFor(p, sf, services)) { + if (!isSequentFormulaSearchedFor(p, sf, services)) { continue; } @@ -321,7 +321,7 @@ private ImmutableList findAllTacletApps(Parameters p, EngineState sta ImmutableList allApps = ImmutableSLList.nil(); for (SequentFormula sf : g.node().sequent().antecedent()) { - if (!isFormulaSearchedFor(p, sf, services)) { + if (!isSequentFormulaSearchedFor(p, sf, services)) { continue; } @@ -330,7 +330,7 @@ private ImmutableList findAllTacletApps(Parameters p, EngineState sta } for (SequentFormula sf : g.node().sequent().succedent()) { - if (!isFormulaSearchedFor(p, sf, services)) { + if (!isSequentFormulaSearchedFor(p, sf, services)) { continue; } @@ -350,8 +350,7 @@ private ImmutableList findAllTacletApps(Parameters p, EngineState sta * @param sf The {@link SequentFormula} to check. * @return true if sf matches. */ - private boolean isFormulaSearchedFor(Parameters p, - SequentFormula sf, Services services) + private boolean isSequentFormulaSearchedFor(Parameters p, SequentFormula sf, Services services) throws ScriptException { Term term = sf.formula(); final boolean satisfiesFormulaParameter = diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java index c0943975128..e32b7602b1f 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java @@ -6,6 +6,8 @@ import java.util.HashMap; import java.util.Map; +import java.util.Properties; +import java.util.Stack; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Proof; @@ -34,7 +36,7 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup final Proof proof = state.getProof(); - final StrategyProperties newProps = + StrategyProperties newProps = proof.getSettings().getStrategySettings().getActiveStrategyProperties(); if (args.oneStepSimplification != null) { @@ -43,6 +45,31 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup : StrategyProperties.OSS_OFF); Strategy.updateStrategySettings(proof, newProps); OneStepSimplifier.refreshOSS(proof); + } else if (args.proofSteps != null) { + state.setMaxAutomaticSteps(args.proofSteps); + } else if (args.key != null) { + newProps.setProperty(args.key, args.value); + updateStrategySettings(newProps); + } else if (args.stackAction != null) { + Stack stack = (Stack) state.getUserData("settingsStack"); + if (stack == null) { + stack = new Stack<>(); + state.putUserData("settingsStack", stack); + } + switch(args.stackAction) { + case "push": + stack.push(newProps.clone()); + break; + case "pop": + // TODO sensible error if empty + newProps = stack.pop(); + updateStrategySettings(newProps); + break; + default: + throw new IllegalArgumentException("stack must be either push or pop."); + } + } else { + throw new IllegalArgumentException("You have to set oss, steps, stack, or key and value."); } if (args.proofSteps != null) { state.setMaxAutomaticSteps(args.proofSteps); @@ -116,8 +143,11 @@ public static class Parameters { @Option(value = "steps") public @Nullable Integer proofSteps; - /***/ + /** key-value pairs to set */ @OptionalVarargs public Map settings = HashMap.newHashMap(0); + + @Option(value = "stack", required = false) + public String stackAction; } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/strategy/StrategyProperties.java b/key.core/src/main/java/de/uka/ilkd/key/strategy/StrategyProperties.java index 99e5dde1538..3ccc76b424e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/strategy/StrategyProperties.java +++ b/key.core/src/main/java/de/uka/ilkd/key/strategy/StrategyProperties.java @@ -432,9 +432,8 @@ public void write(Properties p) { } } - @Override - public synchronized Object clone() { + public synchronized StrategyProperties clone() { final Properties p = (Properties) super.clone(); final StrategyProperties sp = new StrategyProperties(); sp.putAll(p); diff --git a/key.ncore.calculus/src/main/java/org/key_project/prover/sequent/Semisequent.java b/key.ncore.calculus/src/main/java/org/key_project/prover/sequent/Semisequent.java index 88b152e1c46..64a099b7bd7 100644 --- a/key.ncore.calculus/src/main/java/org/key_project/prover/sequent/Semisequent.java +++ b/key.ncore.calculus/src/main/java/org/key_project/prover/sequent/Semisequent.java @@ -190,6 +190,12 @@ public SequentFormula getFirst() { return seqList.head(); } + /// @return the last [SequentFormula] of this [Semisequent] + public SequentFormula getLast() { + return seqList.last(); + // or return seqList.take(seqList.size() - 1).head(); + } + /// Returns iterator about the formulas contained in this [Semisequent] /// /// @return iterator about the formulas contained in this [Semisequent] From 8caad23c9b5c811c802dad7fddfe45d22645b26a Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Mon, 8 Sep 2025 20:54:32 +0200 Subject: [PATCH 06/90] more infrastructure for proof scripts (cherry-picked from earlier attempt to implement JML proof scripts) # Conflicts: # key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java --- .../ilkd/key/macros/ApplyScriptsMacro.java | 71 +++++++++++++------ .../java/de/uka/ilkd/key/nparser/KeyAst.java | 4 +- .../uka/ilkd/key/scripts/BranchesCommand.java | 6 +- .../de/uka/ilkd/key/scripts/SetCommand.java | 28 ++++---- .../uka/ilkd/key/scripts/meta/Argument.java | 2 +- .../org/key_project/util/java/StringUtil.java | 17 +++++ 6 files changed, 86 insertions(+), 42 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 5e349551286..e5edea309a0 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -3,35 +3,34 @@ import de.uka.ilkd.key.control.AbstractUserInterfaceControl; import de.uka.ilkd.key.control.UserInterfaceControl; import de.uka.ilkd.key.java.JavaTools; -import de.uka.ilkd.key.java.Position; +import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.java.SourceElement; import de.uka.ilkd.key.java.statement.JmlAssert; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.op.UpdateApplication; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.parser.Location; -import de.uka.ilkd.key.pp.LogicPrinter; -import de.uka.ilkd.key.pp.NotationInfo; -import de.uka.ilkd.key.pp.ProgramPrinter; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.proof.mgt.SpecificationRepository; import de.uka.ilkd.key.rule.JmlAssertBuiltInRuleApp; import de.uka.ilkd.key.scripts.ProofScriptEngine; import de.uka.ilkd.key.scripts.ScriptCommandAst; +import de.uka.ilkd.key.speclang.njml.JmlParser; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofArgContext; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdCaseContext; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdContext; +import org.antlr.v4.runtime.ParserRuleContext; import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; +import org.key_project.logic.op.Function; import org.key_project.prover.engine.ProverTaskListener; import org.key_project.prover.rules.RuleApp; import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.util.collection.ImmutableList; +import org.key_project.util.java.StringUtil; -import java.net.URI; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public class ApplyScriptsMacro extends AbstractProofMacro { @@ -62,7 +61,7 @@ public boolean canApplyTo(Proof proof, ImmutableList<@NonNull Goal> goals, PosIn || goals.exists(g -> getScript(g) != null); } - private static KeyAst.JMLProofScript getScript(Goal goal) { + private static JmlAssert getScript(Goal goal) { RuleApp ruleApp = goal.node().parent().getAppliedRuleApp(); if (ruleApp instanceof JmlAssertBuiltInRuleApp) { JTerm target = (JTerm) ruleApp.posInOccurrence().subTerm(); @@ -71,7 +70,7 @@ private static KeyAst.JMLProofScript getScript(Goal goal) { } final SourceElement activeStatement = JavaTools.getActiveStatement(target.javaBlock()); if (activeStatement instanceof JmlAssert jmlAssert) { - return jmlAssert.getAssertionProof(); + return jmlAssert; } } return null; @@ -85,14 +84,15 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, if (Thread.interrupted()) { throw new InterruptedException(); } - KeyAst.JMLProofScript proofScript = getScript(goal); - if (proofScript == null) { + JmlAssert jmlAssert = getScript(goal); + if (jmlAssert == null || jmlAssert.getAssertionProof() == null) { + // no script found, use fallback macro fallBackMacro.applyTo(uic, proof, ImmutableList.of(goal), posInOcc, listener); continue; } - List renderedProof = renderProof(proofScript); - // TODO get this location from the jmlAssertion statement ... - Location loc = new Location(new URI("unknown:XXX"), Position.UNDEFINED); + KeyAst.JMLProofScript proofScript = jmlAssert.getAssertionProof(); + Map termMap = getTermMap(jmlAssert, proof.getServices()); + List renderedProof = renderProof(proofScript, termMap, proof.getServices()); ProofScriptEngine pse = new ProofScriptEngine(renderedProof, goal); System.out.println("---- Script"); System.out.println(renderedProof); @@ -101,15 +101,31 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, return new ProofMacroFinishedInfo(this, proof); } - private static List renderProof(KeyAst.JMLProofScript script) { + private Map getTermMap(JmlAssert jmlAssert, Services services) { + SpecificationRepository.@Nullable JmlStatementSpec jmlspec = services.getSpecificationRepository().getStatementSpec(jmlAssert); + if(jmlspec == null) { + throw new IllegalStateException("No specification found for JML assert statement at " + jmlAssert); + } + ImmutableList terms = jmlspec.terms().tail(); + ImmutableList jmlExprs = jmlAssert.collectTerms().tail(); + Map result = new IdentityHashMap<>(); + assert terms.size() == jmlExprs.size(); + for(int i = 0; i < terms.size(); i++) { + // TODO build a map from jmlExprs.get(i) to terms.get(i) + result.put(jmlExprs.get(i), terms.get(i)); + } + return result; + } + + private static List renderProof(KeyAst.JMLProofScript script, Map termMap, Services services) { List result = new ArrayList<>(); for (ProofCmdContext proofCmdContext : script.ctx.proofCmd()) { - result.addAll(renderProofCmd(proofCmdContext)); + result.addAll(renderProofCmd(proofCmdContext, termMap, services)); } return result; } - private static List renderProofCmd(ProofCmdContext ctx) { + private static List renderProofCmd(ProofCmdContext ctx, Map termMap, Services services) { List result = new ArrayList<>(); // Push the current branch context @@ -119,8 +135,13 @@ private static List renderProofCmd(ProofCmdContext ctx) { Map named = new HashMap<>(); List positional = new ArrayList<>(); for (ProofArgContext argContext : ctx.proofArg()) { - // FIXME: actually render the argument value. This is just a placeholder - var value = argContext.expression().getText(); + Object value; + JmlParser.ExpressionContext exp = argContext.expression(); + if(isStringLiteral(exp)) { + value = StringUtil.stripQuotes(exp.getText()); + } else { + value = termMap.get(exp); + } if (argContext.argLabel != null) { named.put(argContext.argLabel.getText(), value); } else { @@ -133,7 +154,7 @@ private static List renderProofCmd(ProofCmdContext ctx) { if(!ctx.proofCmd().isEmpty()) { result.add(new ScriptCommandAst("branches", Map.of("child", 0), List.of("select"))); for (ProofCmdContext proofCmdContext : ctx.proofCmd()) { - result.addAll(renderProofCmd(proofCmdContext)); + result.addAll(renderProofCmd(proofCmdContext, termMap, services)); } } @@ -141,7 +162,7 @@ private static List renderProofCmd(ProofCmdContext ctx) { for (ProofCmdCaseContext pcase : ctx.proofCmdCase()) { result.add(new ScriptCommandAst("branches", Map.of("name", pcase.label), List.of("select"))); for (ProofCmdContext proofCmdContext : pcase.proofCmd()) { - result.addAll(renderProofCmd(proofCmdContext)); + result.addAll(renderProofCmd(proofCmdContext, termMap, services)); } } @@ -150,4 +171,8 @@ private static List renderProofCmd(ProofCmdContext ctx) { return result; } + + private static boolean isStringLiteral(JmlParser.ExpressionContext ctx) { + return ctx.start == ctx.stop && ctx.start.getType() == JmlParser.STRING_LITERAL; + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java b/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java index 6a1ebc43ba0..0effcc39953 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java +++ b/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java @@ -236,7 +236,7 @@ private static ImmutableList collectTerms(JmlParser.ProofCmdC ImmutableList result = ImmutableList.of(); for (JmlParser.ProofArgContext arg : cmd.proofArg()) { JmlParser.ExpressionContext exp = arg.expression(); - if (exp != null && exp.start.getType() != JmlParser.STRING_LITERAL) { + if (exp != null) { result = result.prepend(exp); } } @@ -261,7 +261,7 @@ private static ImmutableList collectTerms(JmlParser.ProofCmdC public @NonNull ImmutableList collectTerms() { ImmutableList result = ImmutableList.of(); for (JmlParser.ProofCmdContext cmd : ctx.proofCmd()) { - result.prepend(collectTerms(cmd)); + result = result.prepend(collectTerms(cmd)); } return result.reverse(); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java index 4e47d1bb3ab..a54fcb75d02 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java @@ -1,9 +1,11 @@ package de.uka.ilkd.key.scripts; +import de.uka.ilkd.key.scripts.meta.Argument; import de.uka.ilkd.key.scripts.meta.Option; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Node; import de.uka.ilkd.key.proof.Proof; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.Deque; @@ -100,10 +102,10 @@ public String getName() { public static class Parameters { /** A formula defining the goal to select */ - @Option(value = "#2") + @Argument public String mode; @Option(value = "branch") - public String branch; + public @Nullable String branch; @Option(value = "child") public int child; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java index e32b7602b1f..b206d18a0bf 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java @@ -19,8 +19,7 @@ import de.uka.ilkd.key.strategy.Strategy; import de.uka.ilkd.key.strategy.StrategyFactory; import de.uka.ilkd.key.strategy.StrategyProperties; - -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; public class SetCommand extends AbstractCommand { public SetCommand() { @@ -31,8 +30,13 @@ public SetCommand() { public void execute(ScriptCommandAst arguments) throws ScriptException, InterruptedException { var args = state.getValueInjector().inject(new Parameters(), arguments); + if(args.settings.isEmpty()) { + throw new IllegalArgumentException("You have to set oss, steps, stack, or key(s) and value(s)."); + } + args.settings.remove("oss"); args.settings.remove("steps"); + args.settings.remove("stack"); final Proof proof = state.getProof(); @@ -45,12 +49,9 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup : StrategyProperties.OSS_OFF); Strategy.updateStrategySettings(proof, newProps); OneStepSimplifier.refreshOSS(proof); - } else if (args.proofSteps != null) { - state.setMaxAutomaticSteps(args.proofSteps); - } else if (args.key != null) { - newProps.setProperty(args.key, args.value); - updateStrategySettings(newProps); - } else if (args.stackAction != null) { + } + + if (args.stackAction != null) { Stack stack = (Stack) state.getUserData("settingsStack"); if (stack == null) { stack = new Stack<>(); @@ -62,15 +63,14 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup break; case "pop": // TODO sensible error if empty - newProps = stack.pop(); - updateStrategySettings(newProps); + var resetProps = stack.pop(); + updateStrategySettings(state, resetProps); break; default: throw new IllegalArgumentException("stack must be either push or pop."); } - } else { - throw new IllegalArgumentException("You have to set oss, steps, stack, or key and value."); } + if (args.proofSteps != null) { state.setMaxAutomaticSteps(args.proofSteps); } @@ -147,7 +147,7 @@ public static class Parameters { @OptionalVarargs public Map settings = HashMap.newHashMap(0); - @Option(value = "stack", required = false) - public String stackAction; + @Option(value = "stack") + public @Nullable String stackAction; } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Argument.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Argument.java index ef796c2c62e..a3e82ea5856 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Argument.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Argument.java @@ -16,6 +16,6 @@ /// Position of this argument in the positional argument list. /// /// @return a non-null string - /// @see ScriptCommandAst#positionalArgs() + /// @see de.uka.ilkd.key.scripts.ScriptCommandAst#positionalArgs() int value() default 0; } diff --git a/key.util/src/main/java/org/key_project/util/java/StringUtil.java b/key.util/src/main/java/org/key_project/util/java/StringUtil.java index fccb010cca1..79666319cb4 100644 --- a/key.util/src/main/java/org/key_project/util/java/StringUtil.java +++ b/key.util/src/main/java/org/key_project/util/java/StringUtil.java @@ -572,4 +572,21 @@ public static void appendRepeated(StringBuilder b, char value, int count) { b.append(value); } } + + /** + * If the given text starts and ends with quotes (single or double), they will be stripped. + * + * @param text The text to check. + * @return The text without leading and trailing quotes or the original text if no quotes were + * present. + */ + public static String stripQuotes(String text) { + if(text.length() >= 2 && text.startsWith("\"") && text.endsWith("\"")) { + return text.substring(1, text.length() - 1); + } + if(text.length() >= 2 && text.startsWith("'") && text.endsWith("'")) { + return text.substring(1, text.length() - 1); + } + return text; + } } From f2a3716af7f6ab5de1494ef13682ac5360836843 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Mon, 8 Sep 2025 21:16:26 +0200 Subject: [PATCH 07/90] adapting to new Script AST nodes --- .../src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index e5edea309a0..85ba49f1b6e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -148,7 +148,7 @@ private static List renderProofCmd(ProofCmdContext ctx, Map Date: Mon, 8 Sep 2025 21:46:49 +0200 Subject: [PATCH 08/90] renaming AssertCommand to FailUnlessCommand --- .../{AssertCommand.java => FailUnlessCommand.java} | 11 ++++------- .../de.uka.ilkd.key.scripts.ProofScriptCommand | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) rename key.core/src/main/java/de/uka/ilkd/key/scripts/{AssertCommand.java => FailUnlessCommand.java} (80%) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/FailUnlessCommand.java similarity index 80% rename from key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java rename to key.core/src/main/java/de/uka/ilkd/key/scripts/FailUnlessCommand.java index 287f892cb49..2d2616e2fb1 100755 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/FailUnlessCommand.java @@ -8,18 +8,18 @@ import de.uka.ilkd.key.scripts.meta.Option; /** - * Halts the script if some condition is not met. + * Halts the script if the expected number of open and enabled goals is not met. *

* See exported documentation at {@link Parameters} at the end of this file. * * @author lanzinger */ -public class AssertCommand extends AbstractCommand { +public class FailUnlessCommand extends AbstractCommand { /** * Instantiates a new assert command. */ - public AssertCommand() { + public FailUnlessCommand() { super(Parameters.class); } @@ -49,10 +49,7 @@ public String getName() { The assert command checks if the number of open and enabled goals is equal to the given number. If not, the script is halted with an error message. - Deprecated: This command is deprecated and should not be used in new scripts. - The name of this command is likely to change since "assert" will - be used for a more general purpose. You may find that this is called - "failUnless". + Note: This command was called "assert" originally. """) public static class Parameters { /** diff --git a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand index 30a721a0e15..2f9905a2541 100644 --- a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand +++ b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand @@ -28,7 +28,7 @@ de.uka.ilkd.key.scripts.SkipCommand de.uka.ilkd.key.scripts.AxiomCommand de.uka.ilkd.key.scripts.AssumeCommand # does not exist? # de.uka.ilkd.key.macros.scripts.SettingsCommand -de.uka.ilkd.key.scripts.AssertCommand +de.uka.ilkd.key.scripts.FailUnlessCommand de.uka.ilkd.key.scripts.RewriteCommand de.uka.ilkd.key.scripts.AllCommand de.uka.ilkd.key.scripts.HideCommand From 4eebfa95e2771a5ab5db767dfb538b1ed21e54ee Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Mon, 8 Sep 2025 21:47:09 +0200 Subject: [PATCH 09/90] correcting the grammar for nested \by clauses --- key.core/src/main/antlr4/JmlParser.g4 | 2 +- .../de/uka/ilkd/key/speclang/njml/JmlFacade.java | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/key.core/src/main/antlr4/JmlParser.g4 b/key.core/src/main/antlr4/JmlParser.g4 index b3512d7c7c6..e3e5d25b637 100644 --- a/key.core/src/main/antlr4/JmlParser.g4 +++ b/key.core/src/main/antlr4/JmlParser.g4 @@ -213,7 +213,7 @@ assert_statement: (ASSERT expression | UNREACHABLE) (SEMI_TOPLEVEL | assertionPr assertionProof: BY (proofCmd | LBRACE ( proofCmd )+ RBRACE) ; proofCmd: cmd=IDENT ( proofArg )* - ( SEMI | BY proofCmd | LBRACE (proofCmd+ | proofCmdCase+) RBRACE ) + ( SEMI | BY ( proofCmd | LBRACE (proofCmd+ | proofCmdCase+) RBRACE )) ; proofCmdCase: CASE ( label=STRING_LITERAL )? COLON ( proofCmd )* diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlFacade.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlFacade.java index 0099c13e0bf..35fba582253 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlFacade.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlFacade.java @@ -10,10 +10,7 @@ import de.uka.ilkd.key.speclang.PositionedString; import de.uka.ilkd.key.util.parsing.SyntaxErrorReporter; -import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.CommonTokenStream; -import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.*; import org.jspecify.annotations.NonNull; /** @@ -116,9 +113,15 @@ private static ParserRuleContext getExpressionContext(JmlLexer lexer) { return ctx; } + // FIXME Make sure this is removed. For testing only! public static void main(String[] args) throws IOException { String input = new String(System.in.readAllBytes()); - var parser = createParser(createLexer(input)); + JmlLexer lexer = createLexer(input); + for (Token t : lexer.getAllTokens()) { + System.out.println(t.getText() + " " + t); + } + lexer = createLexer(input); + var parser = createParser(lexer); var tree = parser.methodlevel_comment(); System.out.println(tree.toStringTree(parser)); } From a8cd36e9ccb51310402fb18d316493d155888b2c Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Wed, 10 Sep 2025 19:10:12 +0200 Subject: [PATCH 10/90] improving the value injector --- .../java/de/uka/ilkd/key/proof/Proof.java | 18 ++++++- .../uka/ilkd/key/scripts/AssertCommand.java | 29 +++++++++++ .../meta/UnknownArgumentException.java | 15 ++++++ .../ilkd/key/scripts/meta/ValueInjector.java | 51 +++++++++++++++---- .../key_project/util/java/IntegerUtil.java | 24 +++++++++ 5 files changed, 125 insertions(+), 12 deletions(-) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java create mode 100644 key.core/src/main/java/de/uka/ilkd/key/scripts/meta/UnknownArgumentException.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/Proof.java b/key.core/src/main/java/de/uka/ilkd/key/proof/Proof.java index 5e1ecaa95ec..da56981ece3 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/Proof.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/Proof.java @@ -975,7 +975,7 @@ public boolean isOpenGoal(Node node) { * * @return the goal that belongs to the given node or null if the node is an inner one */ - public Goal getOpenGoal(Node node) { + public @Nullable Goal getOpenGoal(Node node) { for (final Goal result : openGoals) { if (result.node() == node) { return result; @@ -1000,7 +1000,7 @@ public boolean isClosedGoal(Node node) { * @return the closed goal that belongs to the given node or null if the node is an inner one or * an open goal */ - public Goal getClosedGoal(Node node) { + public @Nullable Goal getClosedGoal(Node node) { for (final Goal result : closedGoals) { if (result.node() == node) { return result; @@ -1009,6 +1009,20 @@ public Goal getClosedGoal(Node node) { return null; } + /** + * Get the goal (open or closed) belonging to the given node if it exists. + * + * @param node the Node where a corresponding goal is searched + * @return the goal that belongs to the given node or null if the node is an inner one + */ + public @Nullable Goal getGoal(Node node) { + Goal g = getOpenGoal(node); + if (g == null) { + g = getClosedGoal(node); + } + return g; + } + /** * returns the list of goals of the subtree starting with node. * diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java new file mode 100644 index 00000000000..01867794b05 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java @@ -0,0 +1,29 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.scripts; + +/** + * An assertion which essentially performs a cut. + * + * The only difference is that this implementation tampers with the labels of the resulting goals to allow them to be + * better recognized in the script engine. + * + * (Unlike in other systems, in KeY the assertion does not remove the original goal formula since that is not well-defined in sequent calculus.) + */ +public class AssertCommand extends CutCommand { + + @Override + public String getName() { + return "assert"; + } + + @Override + public void execute(ScriptCommandAst arguments) throws ScriptException, InterruptedException { + var args = state().getValueInjector().inject(new Parameters(), arguments); + var node = state().getFirstOpenAutomaticGoal().node(); + execute(state(), args); + node.proof().getGoal(node.child(0)).setBranchLabel("use"); + node.proof().getGoal(node.child(1)).setBranchLabel("show"); + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/UnknownArgumentException.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/UnknownArgumentException.java new file mode 100644 index 00000000000..957d9cdbbd0 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/UnknownArgumentException.java @@ -0,0 +1,15 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.scripts.meta; + +/** + * Signals if an unknown/unexpected argument has been provided for injection. + * + * @author Mattias Ulbrich + */ +public class UnknownArgumentException extends InjectionException { + public UnknownArgumentException(String message) { + super(message); + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java index 94e95310a9c..98235875574 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java @@ -12,6 +12,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; +import org.key_project.util.java.IntegerUtil; /** * @author Alexander Weigl @@ -46,6 +47,10 @@ private record ConverterKey( Class source, Class target) { } + interface VerifyableParameters { + void verifyParameters() throws IllegalArgumentException; + } + /** * Injects the given {@code arguments} in the {@code obj}. For more details see * {@link #inject(Object, ScriptCommandAst)} @@ -61,8 +66,7 @@ private record ConverterKey( * @throws ConversionException an converter could not translate the given value in arguments */ public static T injection(ProofScriptCommand command, @NonNull T obj, - ScriptCommandAst arguments) throws ArgumentRequiredException, - InjectionReflectionException, NoSpecifiedConverterException, ConversionException { + ScriptCommandAst arguments) throws InjectionException { return getInstance().inject(obj, arguments); } @@ -122,12 +126,35 @@ public static ValueInjector createDefault() { * @see Flag */ public T inject(T obj, ScriptCommandAst arguments) - throws ConversionException, InjectionReflectionException, NoSpecifiedConverterException, - ArgumentRequiredException { + throws InjectionException { List meta = ArgumentsLifter.inferScriptArguments(obj.getClass()); + Set handledOptions = new HashSet<>(); for (ProofScriptArgument arg : meta) { - injectIntoField(arg, arguments, obj); + handledOptions.addAll(injectIntoField(arg, arguments, obj)); + } + + Optional unhandled = arguments.namedArgs().keySet().stream() + .filter(it -> !handledOptions.contains(it)) + .findAny(); + if(unhandled.isPresent()) { + throw new UnknownArgumentException(String.format( + "Unknown argument %s (with value %s) was provided. For command class: '%s'", + unhandled.get(), + arguments.namedArgs().get(unhandled.get()), + obj.getClass().getName())); + } + + Optional unhandledPos = IntegerUtil.indexRangeOf(arguments.positionalArgs()) + .stream() + .filter(it -> !handledOptions.contains(it)) + .findAny(); + if(unhandledPos.isPresent()) { + long count = handledOptions.stream().filter(it -> it instanceof Integer).count(); + throw new UnknownArgumentException(String.format( + "Unexpected positional argument at index %d was provided. " + + "Expected (at most) %d positional arguments. For command class: '%s'", + unhandledPos.get(), count, obj.getClass().getName())); } return obj; @@ -149,19 +176,22 @@ private Map getStringMap(Object obj, ProofScriptArgument vararg) } } - private void injectIntoField(ProofScriptArgument meta, ScriptCommandAst args, Object obj) + private List injectIntoField(ProofScriptArgument meta, ScriptCommandAst args, Object obj) throws InjectionReflectionException, ArgumentRequiredException, ConversionException, NoSpecifiedConverterException { Object val = null; + List handled = List.of(); if (meta.isPositional()) { final var idx = meta.getArgumentPosition(); if (idx < args.positionalArgs().size()) { val = args.positionalArgs().get(idx); + handled = List.of(idx); } } if (meta.isPositionalVarArgs()) { val = args.positionalArgs(); + handled = IntegerUtil.indexRangeOf(args.positionalArgs()); } if (meta.isOptionalVarArgs()) { @@ -175,15 +205,16 @@ private void injectIntoField(ProofScriptArgument meta, ScriptCommandAst args, Ob } } val = result; + handled = new ArrayList<>(result.keySet()); } if (meta.isOption()) { val = args.namedArgs().get(meta.getName()); + handled = List.of(meta.getName()); } if (meta.isFlag()) { val = args.namedArgs().get(meta.getName()); - System.out.println("X" + val + " " + args.namedArgs() + " " + meta.getName()); if (val == null) { // can also be given w/o colon or equal sign, e.g., "command hide;" var stringStream = args.positionalArgs().stream() @@ -196,8 +227,8 @@ private void injectIntoField(ProofScriptArgument meta, ScriptCommandAst args, Ob }); // val == true iff the name of the flag appear as a positional argument. val = stringStream.anyMatch(it -> Objects.equals(it, meta.getName())); - System.out.println(val); } + handled = List.of(meta.getName()); } try { @@ -216,7 +247,7 @@ private void injectIntoField(ProofScriptArgument meta, ScriptCommandAst args, Ob } catch (IllegalAccessException e) { throw new InjectionReflectionException("Could not inject values via reflection", e); } - + return handled; } private Object convert(ProofScriptArgument meta, Object val) @@ -309,7 +340,7 @@ public void addConverter(Converter conv) { */ @SuppressWarnings("unchecked") public @Nullable Converter getConverter(Class ret, Class arg) { - if (ret == arg) { + if (ret.isAssignableFrom(arg)) { return (T it) -> (R) it; } return (Converter) converters.get(new ConverterKey<>(ret, arg)); diff --git a/key.util/src/main/java/org/key_project/util/java/IntegerUtil.java b/key.util/src/main/java/org/key_project/util/java/IntegerUtil.java index 240bc59f135..b15033ed43d 100644 --- a/key.util/src/main/java/org/key_project/util/java/IntegerUtil.java +++ b/key.util/src/main/java/org/key_project/util/java/IntegerUtil.java @@ -4,6 +4,9 @@ package org.key_project.util.java; +import java.util.Collection; +import java.util.List; + public final class IntegerUtil { /** * Forbid instances. @@ -28,4 +31,25 @@ public static int factorial(int n) { return factorial; } } + + /** + * Creates a list of integers from {@code 0} (inclusive) to the size of the given collection (exclusive). + */ + public static List indexRangeOf(Collection coll) { + return rangeUntil(coll.size()); + } + + /** + * Creates a list of integers from {@code 0} (inclusive) to {@code size} (exclusive). + */ + private static List rangeUntil(int size) { + return range(0, size); + } + + /** + * Creates a list of integers from {@code from} (inclusive) to {@code untilExclusive} (exclusive). + */ + private static List range(int from, int untilExclusive) { + return java.util.stream.IntStream.range(from, untilExclusive).boxed().toList(); + } } From 27f86c3ba530175d8c0ccd459ad4762f8216b2b8 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Wed, 10 Sep 2025 19:11:38 +0200 Subject: [PATCH 11/90] renaming a deprecated command "assert" --> "assertOpenGoals" --- .../{FailUnlessCommand.java => AssertOpenGoalsCommand.java} | 6 +++--- .../services/de.uka.ilkd.key.scripts.ProofScriptCommand | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) rename key.core/src/main/java/de/uka/ilkd/key/scripts/{FailUnlessCommand.java => AssertOpenGoalsCommand.java} (92%) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/FailUnlessCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertOpenGoalsCommand.java similarity index 92% rename from key.core/src/main/java/de/uka/ilkd/key/scripts/FailUnlessCommand.java rename to key.core/src/main/java/de/uka/ilkd/key/scripts/AssertOpenGoalsCommand.java index 2d2616e2fb1..ba84205242e 100755 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/FailUnlessCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertOpenGoalsCommand.java @@ -14,12 +14,12 @@ * * @author lanzinger */ -public class FailUnlessCommand extends AbstractCommand { +public class AssertOpenGoalsCommand extends AbstractCommand { /** * Instantiates a new assert command. */ - public FailUnlessCommand() { + public AssertOpenGoalsCommand() { super(Parameters.class); } @@ -39,7 +39,7 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup @Override public String getName() { - return "assert"; + return "assertOpenGoals"; } /** diff --git a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand index 2f9905a2541..a332100ffa2 100644 --- a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand +++ b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand @@ -7,6 +7,7 @@ de.uka.ilkd.key.scripts.EchoCommand de.uka.ilkd.key.scripts.FocusCommand de.uka.ilkd.key.scripts.AutoCommand de.uka.ilkd.key.scripts.CutCommand +de.uka.ilkd.key.scripts.AssertCommand de.uka.ilkd.key.scripts.SetCommand de.uka.ilkd.key.scripts.SetEchoCommand de.uka.ilkd.key.scripts.SetFailOnClosedCommand @@ -28,7 +29,7 @@ de.uka.ilkd.key.scripts.SkipCommand de.uka.ilkd.key.scripts.AxiomCommand de.uka.ilkd.key.scripts.AssumeCommand # does not exist? # de.uka.ilkd.key.macros.scripts.SettingsCommand -de.uka.ilkd.key.scripts.FailUnlessCommand +de.uka.ilkd.key.scripts.AssertOpenGoalsCommand de.uka.ilkd.key.scripts.RewriteCommand de.uka.ilkd.key.scripts.AllCommand de.uka.ilkd.key.scripts.HideCommand From e6649efbd4a55d67c19016e4667ad0268a5faf44 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 12 Sep 2025 11:58:11 +0200 Subject: [PATCH 12/90] spotless --- .../key/java/ast/statement/JmlAssert.java | 21 +++--- .../ilkd/key/macros/ApplyScriptsMacro.java | 53 ++++++++++----- .../ilkd/key/macros/ScriptAwareAutoMacro.java | 3 + .../uka/ilkd/key/macros/ScriptAwareMacro.java | 3 + .../java/de/uka/ilkd/key/nparser/KeyAst.java | 6 +- .../de/uka/ilkd/key/rule/JmlAssertRule.java | 3 +- .../uka/ilkd/key/scripts/AssertCommand.java | 10 +-- .../uka/ilkd/key/scripts/BranchesCommand.java | 66 +++++++++++-------- .../de/uka/ilkd/key/scripts/SetCommand.java | 12 ++-- .../ilkd/key/scripts/meta/ValueInjector.java | 24 +++---- .../TextualJMLAssertStatement.java | 4 +- .../key/speclang/njml/TextualTranslator.java | 4 +- .../key_project/util/java/IntegerUtil.java | 6 +- .../org/key_project/util/java/StringUtil.java | 4 +- 14 files changed, 130 insertions(+), 89 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java index 0fd8208357b..4859cb421c2 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java @@ -8,21 +8,15 @@ import de.uka.ilkd.key.java.ast.PositionInfo; import de.uka.ilkd.key.java.ast.ProgramElement; import de.uka.ilkd.key.java.visitor.Visitor; -import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.speclang.jml.pretranslation.TextualJMLAssertStatement; -import de.uka.ilkd.key.speclang.njml.LabeledParserRuleContext; + +import org.key_project.util.ExtList; +import org.key_project.util.collection.ImmutableList; + import org.antlr.v4.runtime.ParserRuleContext; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; -import org.key_project.util.ExtList; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import de.uka.ilkd.key.nparser.KeyAst; -import org.key_project.util.collection.ImmutableList; -import org.key_project.util.collection.Immutables; /** * A JML assert statement. @@ -56,7 +50,8 @@ public class JmlAssert extends JavaStatement { * @param assertionProof the optional proof for an assert statement (not for assume) * @param positionInfo the position information for this statement */ - public JmlAssert(TextualJMLAssertStatement.Kind kind, KeyAst.Expression condition, KeyAst.@Nullable JMLProofScript assertionProof, + public JmlAssert(TextualJMLAssertStatement.Kind kind, KeyAst.Expression condition, + KeyAst.@Nullable JMLProofScript assertionProof, PositionInfo positionInfo) { super(positionInfo); this.kind = kind; @@ -196,7 +191,7 @@ public void visit(Visitor v) { */ public @NonNull ImmutableList collectTerms() { ImmutableList result = ImmutableList.of(); - if(assertionProof != null) { + if (assertionProof != null) { result = result.prepend(assertionProof.collectTerms()); } result = result.prepend(condition.ctx); diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 85ba49f1b6e..3fc280a059e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -1,5 +1,10 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.macros; +import java.util.*; + import de.uka.ilkd.key.control.AbstractUserInterfaceControl; import de.uka.ilkd.key.control.UserInterfaceControl; import de.uka.ilkd.key.java.JavaTools; @@ -20,17 +25,16 @@ import de.uka.ilkd.key.speclang.njml.JmlParser.ProofArgContext; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdCaseContext; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdContext; -import org.antlr.v4.runtime.ParserRuleContext; -import org.jspecify.annotations.NonNull; -import org.jspecify.annotations.Nullable; -import org.key_project.logic.op.Function; + import org.key_project.prover.engine.ProverTaskListener; import org.key_project.prover.rules.RuleApp; import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.util.collection.ImmutableList; import org.key_project.util.java.StringUtil; -import java.util.*; +import org.antlr.v4.runtime.ParserRuleContext; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; public class ApplyScriptsMacro extends AbstractProofMacro { @@ -56,7 +60,8 @@ public String getDescription() { } @Override - public boolean canApplyTo(Proof proof, ImmutableList<@NonNull Goal> goals, PosInOccurrence posInOcc) { + public boolean canApplyTo(Proof proof, ImmutableList<@NonNull Goal> goals, + PosInOccurrence posInOcc) { return fallBackMacro.canApplyTo(proof, goals, posInOcc) || goals.exists(g -> getScript(g) != null); } @@ -92,8 +97,10 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, } KeyAst.JMLProofScript proofScript = jmlAssert.getAssertionProof(); Map termMap = getTermMap(jmlAssert, proof.getServices()); - List renderedProof = renderProof(proofScript, termMap, proof.getServices()); + List renderedProof = + renderProof(proofScript, termMap, proof.getServices()); ProofScriptEngine pse = new ProofScriptEngine(renderedProof, goal); + addConverters(pse, proof.getServices()); System.out.println("---- Script"); System.out.println(renderedProof); pse.execute((AbstractUserInterfaceControl) uic, proof); @@ -101,23 +108,31 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, return new ProofMacroFinishedInfo(this, proof); } + private void addConverters(ProofScriptEngine pse, Services services) { + // pse. + + } + private Map getTermMap(JmlAssert jmlAssert, Services services) { - SpecificationRepository.@Nullable JmlStatementSpec jmlspec = services.getSpecificationRepository().getStatementSpec(jmlAssert); - if(jmlspec == null) { - throw new IllegalStateException("No specification found for JML assert statement at " + jmlAssert); + SpecificationRepository.@Nullable JmlStatementSpec jmlspec = + services.getSpecificationRepository().getStatementSpec(jmlAssert); + if (jmlspec == null) { + throw new IllegalStateException( + "No specification found for JML assert statement at " + jmlAssert); } ImmutableList terms = jmlspec.terms().tail(); ImmutableList jmlExprs = jmlAssert.collectTerms().tail(); Map result = new IdentityHashMap<>(); assert terms.size() == jmlExprs.size(); - for(int i = 0; i < terms.size(); i++) { + for (int i = 0; i < terms.size(); i++) { // TODO build a map from jmlExprs.get(i) to terms.get(i) result.put(jmlExprs.get(i), terms.get(i)); } return result; } - private static List renderProof(KeyAst.JMLProofScript script, Map termMap, Services services) { + private static List renderProof(KeyAst.JMLProofScript script, + Map termMap, Services services) { List result = new ArrayList<>(); for (ProofCmdContext proofCmdContext : script.ctx.proofCmd()) { result.addAll(renderProofCmd(proofCmdContext, termMap, services)); @@ -125,7 +140,8 @@ private static List renderProof(KeyAst.JMLProofScript script, return result; } - private static List renderProofCmd(ProofCmdContext ctx, Map termMap, Services services) { + private static List renderProofCmd(ProofCmdContext ctx, + Map termMap, Services services) { List result = new ArrayList<>(); // Push the current branch context @@ -137,7 +153,7 @@ private static List renderProofCmd(ProofCmdContext ctx, Map renderProofCmd(ProofCmdContext ctx, Map renderProofCmd(ProofCmdContext ctx, Map collectTerms(JmlParser.ProofCmdContext cmd) { + private static ImmutableList collectTerms( + JmlParser.ProofCmdContext cmd) { ImmutableList result = ImmutableList.of(); for (JmlParser.ProofArgContext arg : cmd.proofArg()) { JmlParser.ExpressionContext exp = arg.expression(); diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java b/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java index 802952a4f5d..db4bcbada48 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java +++ b/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java @@ -133,7 +133,8 @@ public IBuiltInRuleApp createApp(PosInOccurrence occurrence, TermServices servic final MethodFrame frame = JavaTools.getInnermostMethodFrame(target.javaBlock(), services); final JTerm self = MiscTools.getSelfTerm(frame, services); - final SpecificationRepository.JmlStatementSpec spec = services.getSpecificationRepository().getStatementSpec(jmlAssert); + final SpecificationRepository.JmlStatementSpec spec = + services.getSpecificationRepository().getStatementSpec(jmlAssert); if (spec == null) { throw new RuleAbortException( diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java index 01867794b05..b9a96d91c0d 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java @@ -6,10 +6,12 @@ /** * An assertion which essentially performs a cut. * - * The only difference is that this implementation tampers with the labels of the resulting goals to allow them to be + * The only difference is that this implementation tampers with the labels of the resulting goals to + * allow them to be * better recognized in the script engine. * - * (Unlike in other systems, in KeY the assertion does not remove the original goal formula since that is not well-defined in sequent calculus.) + * (Unlike in other systems, in KeY the assertion does not remove the original goal formula since + * that is not well-defined in sequent calculus.) */ public class AssertCommand extends CutCommand { @@ -23,7 +25,7 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup var args = state().getValueInjector().inject(new Parameters(), arguments); var node = state().getFirstOpenAutomaticGoal().node(); execute(state(), args); - node.proof().getGoal(node.child(0)).setBranchLabel("use"); - node.proof().getGoal(node.child(1)).setBranchLabel("show"); + node.proof().getGoal(node.child(0)).setBranchLabel("Validity"); + node.proof().getGoal(node.child(1)).setBranchLabel("Usage"); } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java index a54fcb75d02..35853fe5e91 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java @@ -1,12 +1,8 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.scripts; -import de.uka.ilkd.key.scripts.meta.Argument; -import de.uka.ilkd.key.scripts.meta.Option; -import de.uka.ilkd.key.proof.Goal; -import de.uka.ilkd.key.proof.Node; -import de.uka.ilkd.key.proof.Proof; -import org.jspecify.annotations.Nullable; - import java.util.ArrayList; import java.util.Deque; import java.util.Iterator; @@ -15,6 +11,14 @@ import java.util.Optional; import java.util.Stack; +import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.proof.Node; +import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Option; + +import org.jspecify.annotations.Nullable; + public class BranchesCommand extends AbstractCommand { public BranchesCommand() { super(Parameters.class); @@ -31,30 +35,34 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup } switch (args.mode) { - case "push": - Node node = state.getFirstOpenAutomaticGoal().node(); - // this is the first goal. The parent is the decision point - node = node.parent(); - stack.push(node.serialNr()); - break; - case "pop": - stack.pop(); - break; - case "select": - Node root = findNodeByNumber(proof, stack.peek()); - Goal goal; - if (args.branch == null) { - goal = findGoalByNode(state.getProof(), root.child(args.child)); - } else { - goal = findGoalByName(root, args.branch); - } - state.setGoal(goal); - break; - default: - throw new ScriptException(); + case "push": + ensureSingleGoal(); + Node node = state.getFirstOpenAutomaticGoal().node(); + // this is the first goal. The parent is the decision point + stack.push(node.serialNr()); + break; + case "pop": + stack.pop(); + break; + case "select": + Node root = findNodeByNumber(proof, stack.peek()); + Goal goal; + if (args.branch == null) { + goal = findGoalByNode(state.getProof(), root.child(args.child)); + } else { + goal = findGoalByName(root, args.branch); + } + state.setGoal(goal); + break; + default: + throw new ScriptException(); } } + private void ensureSingleGoal() { + state. + } + private Goal findGoalByName(Node root, String branch) throws ScriptException { Iterator it = root.childrenIterator(); List knownBranchLabels = new ArrayList<>(); @@ -107,7 +115,7 @@ public static class Parameters { @Option(value = "branch") public @Nullable String branch; @Option(value = "child") - public int child; + public @Nullable Integer child; } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java index b206d18a0bf..dfcd9026e6c 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java @@ -6,7 +6,6 @@ import java.util.HashMap; import java.util.Map; -import java.util.Properties; import java.util.Stack; import de.uka.ilkd.key.proof.Goal; @@ -19,6 +18,7 @@ import de.uka.ilkd.key.strategy.Strategy; import de.uka.ilkd.key.strategy.StrategyFactory; import de.uka.ilkd.key.strategy.StrategyProperties; + import org.jspecify.annotations.Nullable; public class SetCommand extends AbstractCommand { @@ -30,8 +30,9 @@ public SetCommand() { public void execute(ScriptCommandAst arguments) throws ScriptException, InterruptedException { var args = state.getValueInjector().inject(new Parameters(), arguments); - if(args.settings.isEmpty()) { - throw new IllegalArgumentException("You have to set oss, steps, stack, or key(s) and value(s)."); + if (args.settings.isEmpty()) { + throw new IllegalArgumentException( + "You have to set oss, steps, stack, or key(s) and value(s)."); } args.settings.remove("oss"); @@ -52,12 +53,13 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup } if (args.stackAction != null) { - Stack stack = (Stack) state.getUserData("settingsStack"); + Stack stack = + (Stack) state.getUserData("settingsStack"); if (stack == null) { stack = new Stack<>(); state.putUserData("settingsStack", stack); } - switch(args.stackAction) { + switch (args.stackAction) { case "push": stack.push(newProps.clone()); break; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java index 98235875574..f5881b9e57a 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java @@ -9,10 +9,11 @@ import de.uka.ilkd.key.scripts.ProofScriptCommand; import de.uka.ilkd.key.scripts.ScriptCommandAst; +import org.key_project.util.java.IntegerUtil; + import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; -import org.key_project.util.java.IntegerUtil; /** * @author Alexander Weigl @@ -137,24 +138,24 @@ public T inject(T obj, ScriptCommandAst arguments) Optional unhandled = arguments.namedArgs().keySet().stream() .filter(it -> !handledOptions.contains(it)) .findAny(); - if(unhandled.isPresent()) { + if (unhandled.isPresent()) { throw new UnknownArgumentException(String.format( - "Unknown argument %s (with value %s) was provided. For command class: '%s'", - unhandled.get(), - arguments.namedArgs().get(unhandled.get()), - obj.getClass().getName())); + "Unknown argument %s (with value %s) was provided. For command class: '%s'", + unhandled.get(), + arguments.namedArgs().get(unhandled.get()), + obj.getClass().getName())); } Optional unhandledPos = IntegerUtil.indexRangeOf(arguments.positionalArgs()) .stream() .filter(it -> !handledOptions.contains(it)) .findAny(); - if(unhandledPos.isPresent()) { + if (unhandledPos.isPresent()) { long count = handledOptions.stream().filter(it -> it instanceof Integer).count(); throw new UnknownArgumentException(String.format( - "Unexpected positional argument at index %d was provided. " + - "Expected (at most) %d positional arguments. For command class: '%s'", - unhandledPos.get(), count, obj.getClass().getName())); + "Unexpected positional argument at index %d was provided. " + + "Expected (at most) %d positional arguments. For command class: '%s'", + unhandledPos.get(), count, obj.getClass().getName())); } return obj; @@ -336,7 +337,8 @@ public void addConverter(Converter conv) { * @param the source type * @param ret the result type class * @param arg the source type class - * @return a suitable converter (registered) converter for the requested class. null if no such converter is known. + * @return a suitable converter (registered) converter for the requested class. null if no such + * converter is known. */ @SuppressWarnings("unchecked") public @Nullable Converter getConverter(Class ret, Class arg) { diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/pretranslation/TextualJMLAssertStatement.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/pretranslation/TextualJMLAssertStatement.java index 192d8595f14..79b41ec577b 100755 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/pretranslation/TextualJMLAssertStatement.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/pretranslation/TextualJMLAssertStatement.java @@ -4,9 +4,11 @@ package de.uka.ilkd.key.speclang.jml.pretranslation; import de.uka.ilkd.key.nparser.KeyAst; + +import org.key_project.util.collection.ImmutableSLList; + import org.antlr.v4.runtime.RuleContext; import org.jspecify.annotations.Nullable; -import org.key_project.util.collection.ImmutableSLList; /** * A JML assert/assume statement. diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java index 5360451a5a4..7786453f633 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java @@ -552,8 +552,8 @@ public Object visitAssume_statement(JmlParser.Assume_statementContext ctx) { public Object visitAssert_statement(JmlParser.Assert_statementContext ctx) { TextualJMLAssertStatement b = new TextualJMLAssertStatement(TextualJMLAssertStatement.Kind.ASSERT, - new KeyAst.Expression(ctx.expression()), - KeyAst.JMLProofScript.fromContext(ctx.assertionProof())); + new KeyAst.Expression(ctx.expression()), + KeyAst.JMLProofScript.fromContext(ctx.assertionProof())); finishConstruct(b); return null; } diff --git a/key.util/src/main/java/org/key_project/util/java/IntegerUtil.java b/key.util/src/main/java/org/key_project/util/java/IntegerUtil.java index b15033ed43d..bd6e6306659 100644 --- a/key.util/src/main/java/org/key_project/util/java/IntegerUtil.java +++ b/key.util/src/main/java/org/key_project/util/java/IntegerUtil.java @@ -33,7 +33,8 @@ public static int factorial(int n) { } /** - * Creates a list of integers from {@code 0} (inclusive) to the size of the given collection (exclusive). + * Creates a list of integers from {@code 0} (inclusive) to the size of the given collection + * (exclusive). */ public static List indexRangeOf(Collection coll) { return rangeUntil(coll.size()); @@ -47,7 +48,8 @@ private static List rangeUntil(int size) { } /** - * Creates a list of integers from {@code from} (inclusive) to {@code untilExclusive} (exclusive). + * Creates a list of integers from {@code from} (inclusive) to {@code untilExclusive} + * (exclusive). */ private static List range(int from, int untilExclusive) { return java.util.stream.IntStream.range(from, untilExclusive).boxed().toList(); diff --git a/key.util/src/main/java/org/key_project/util/java/StringUtil.java b/key.util/src/main/java/org/key_project/util/java/StringUtil.java index 79666319cb4..d16c10c5037 100644 --- a/key.util/src/main/java/org/key_project/util/java/StringUtil.java +++ b/key.util/src/main/java/org/key_project/util/java/StringUtil.java @@ -581,10 +581,10 @@ public static void appendRepeated(StringBuilder b, char value, int count) { * present. */ public static String stripQuotes(String text) { - if(text.length() >= 2 && text.startsWith("\"") && text.endsWith("\"")) { + if (text.length() >= 2 && text.startsWith("\"") && text.endsWith("\"")) { return text.substring(1, text.length() - 1); } - if(text.length() >= 2 && text.startsWith("'") && text.endsWith("'")) { + if (text.length() >= 2 && text.startsWith("'") && text.endsWith("'")) { return text.substring(1, text.length() - 1); } return text; From 414607af414ebfc15374b6917e5d031ac6204ca4 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Wed, 24 Sep 2025 18:05:53 +0200 Subject: [PATCH 13/90] working version of scripts --- .../ilkd/key/macros/ApplyScriptsMacro.java | 47 ++++++++++++------ .../uka/ilkd/key/scripts/AssertCommand.java | 4 +- .../uka/ilkd/key/scripts/BranchesCommand.java | 49 ++++++++++++++++--- .../ilkd/key/scripts/ProofScriptEngine.java | 3 ++ .../key/scripts/meta/InjectionException.java | 9 ++++ .../ilkd/key/scripts/meta/ValueInjector.java | 10 +++- 6 files changed, 97 insertions(+), 25 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 3fc280a059e..055ece5053d 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -4,6 +4,7 @@ package de.uka.ilkd.key.macros; import java.util.*; +import java.util.stream.Collectors; import de.uka.ilkd.key.control.AbstractUserInterfaceControl; import de.uka.ilkd.key.control.UserInterfaceControl; @@ -26,6 +27,7 @@ import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdCaseContext; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdContext; +import org.key_project.logic.Term; import org.key_project.prover.engine.ProverTaskListener; import org.key_project.prover.rules.RuleApp; import org.key_project.prover.sequent.PosInOccurrence; @@ -35,9 +37,13 @@ import org.antlr.v4.runtime.ParserRuleContext; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ApplyScriptsMacro extends AbstractProofMacro { + private static final Logger LOGGER = LoggerFactory.getLogger(ApplyScriptsMacro.class); + private final ProofMacro fallBackMacro; public ApplyScriptsMacro(ProofMacro fallBackMacro) { @@ -81,6 +87,15 @@ private static JmlAssert getScript(Goal goal) { return null; } + private static @Nullable JTerm getUpdate(Goal goal) { + RuleApp ruleApp = goal.node().parent().getAppliedRuleApp(); + Term appliedOn = ruleApp.posInOccurrence().subTerm(); + if(appliedOn.op() instanceof UpdateApplication) { + return UpdateApplication.getUpdate((JTerm) appliedOn); + } + return null; + } + @Override public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, ImmutableList goals, PosInOccurrence posInOcc, ProverTaskListener listener) @@ -97,21 +112,19 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, } KeyAst.JMLProofScript proofScript = jmlAssert.getAssertionProof(); Map termMap = getTermMap(jmlAssert, proof.getServices()); + JTerm update = getUpdate(goal); List renderedProof = - renderProof(proofScript, termMap, proof.getServices()); + renderProof(proofScript, termMap, update, proof.getServices()); ProofScriptEngine pse = new ProofScriptEngine(renderedProof, goal); - addConverters(pse, proof.getServices()); - System.out.println("---- Script"); - System.out.println(renderedProof); + LOGGER.debug("---- Script"); + LOGGER.debug(renderedProof.stream().map(ScriptCommandAst::asCommandLine).collect(Collectors.joining("\n"))); + LOGGER.debug("---- End Script"); + pse.execute((AbstractUserInterfaceControl) uic, proof); } return new ProofMacroFinishedInfo(this, proof); } - private void addConverters(ProofScriptEngine pse, Services services) { - // pse. - - } private Map getTermMap(JmlAssert jmlAssert, Services services) { SpecificationRepository.@Nullable JmlStatementSpec jmlspec = @@ -131,17 +144,17 @@ private Map getTermMap(JmlAssert jmlAssert, Services s return result; } - private static List renderProof(KeyAst.JMLProofScript script, - Map termMap, Services services) { + private static List renderProof(KeyAst.JMLProofScript script, + Map termMap, JTerm update, Services services) { List result = new ArrayList<>(); for (ProofCmdContext proofCmdContext : script.ctx.proofCmd()) { - result.addAll(renderProofCmd(proofCmdContext, termMap, services)); + result.addAll(renderProofCmd(proofCmdContext, termMap, update, services)); } return result; } private static List renderProofCmd(ProofCmdContext ctx, - Map termMap, Services services) { + Map termMap, JTerm update, Services services) { List result = new ArrayList<>(); // Push the current branch context @@ -157,6 +170,10 @@ private static List renderProofCmd(ProofCmdContext ctx, value = StringUtil.stripQuotes(exp.getText()); } else { value = termMap.get(exp); + if(update != null) { + // Wrap in update application if an update is present + value = services.getTermBuilder().apply(update, (JTerm)value); + } } if (argContext.argLabel != null) { named.put(argContext.argLabel.getText(), value); @@ -169,9 +186,9 @@ private static List renderProofCmd(ProofCmdContext ctx, // handle proofCmd if present if (!ctx.proofCmd().isEmpty()) { - result.add(new ScriptCommandAst("branches", Map.of("child", 0), List.of("select"))); + result.add(new ScriptCommandAst("branches", Map.of(), List.of("single"))); for (ProofCmdContext proofCmdContext : ctx.proofCmd()) { - result.addAll(renderProofCmd(proofCmdContext, termMap, services)); + result.addAll(renderProofCmd(proofCmdContext, termMap, update, services)); } } @@ -181,7 +198,7 @@ private static List renderProofCmd(ProofCmdContext ctx, result.add(new ScriptCommandAst("branches", Map.of("branch", label), List.of("select"))); for (ProofCmdContext proofCmdContext : pcase.proofCmd()) { - result.addAll(renderProofCmd(proofCmdContext, termMap, services)); + result.addAll(renderProofCmd(proofCmdContext, termMap, update, services)); } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java index b9a96d91c0d..2bc1f27d91e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java @@ -25,7 +25,7 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup var args = state().getValueInjector().inject(new Parameters(), arguments); var node = state().getFirstOpenAutomaticGoal().node(); execute(state(), args); - node.proof().getGoal(node.child(0)).setBranchLabel("Validity"); - node.proof().getGoal(node.child(1)).setBranchLabel("Usage"); + // node.proof().getGoal(node.child(0)).setBranchLabel("Validity"); + // node.proof().getGoal(node.child(1)).setBranchLabel("Usage"); } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java index 35853fe5e91..33500c34591 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java @@ -11,19 +11,31 @@ import java.util.Optional; import java.util.Stack; +import de.uka.ilkd.key.logic.op.SortDependingFunction; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Node; import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.rule.TacletApp; import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.InjectionException; import de.uka.ilkd.key.scripts.meta.Option; +import de.uka.ilkd.key.scripts.meta.ValueInjector; import org.jspecify.annotations.Nullable; +import org.key_project.prover.rules.tacletbuilder.TacletGoalTemplate; +import org.key_project.util.collection.ImmutableList; public class BranchesCommand extends AbstractCommand { + public BranchesCommand() { super(Parameters.class); } + @Override + public String getName() { + return "branches"; + } + @Override public void execute(ScriptCommandAst arguments) throws ScriptException, InterruptedException { var args = state().getValueInjector().inject(new BranchesCommand.Parameters(), arguments); @@ -34,6 +46,10 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup state.putUserData("_branchStack", stack); } + if(args.mode == null) { + throw new ScriptException("For 'branches', a mode must be specified"); + } + switch (args.mode) { case "push": ensureSingleGoal(); @@ -53,14 +69,38 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup goal = findGoalByName(root, args.branch); } state.setGoal(goal); + case "single": + root = findNodeByNumber(proof, stack.peek()); + TacletApp ta = (TacletApp) root.getAppliedRuleApp(); + ImmutableList templates = ta.taclet().goalTemplates(); + + int no = 0; + int found = -1; + for (TacletGoalTemplate template : templates) { + if(!"main".equals(template.tag())) { + if(found != -1) { + throw new ScriptException("More than one non-main goal found"); + } + found = no; + } + no++; + } + if (found == -1) { + throw new ScriptException("No single non-main goal found"); + } + + // For some reason, the child index is reversed between the node and the templates + found = templates.size() - 1 - found; + goal = findGoalByNode(proof, root.child(found)); + state.setGoal(goal); break; default: - throw new ScriptException(); + throw new ScriptException("Unknown mode " + args.mode + " for the 'branches' command" ); } } private void ensureSingleGoal() { - state. + //state. } private Goal findGoalByName(Node root, String branch) throws ScriptException { @@ -103,11 +143,6 @@ private Node findNodeByNumber(Proof proof, int serial) throws ScriptException { throw new ScriptException(); } - @Override - public String getName() { - return "branches"; - } - public static class Parameters { /** A formula defining the goal to select */ @Argument diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java index 59a0eb98bf2..f0a611a5826 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java @@ -180,6 +180,9 @@ public void execute(AbstractUserInterfaceControl uiControl, List LOGGER.debug("{}", g.sequent())); + LOGGER.debug("Commands: {}", commands.stream() + .map(ScriptCommandAst::asCommandLine) + .collect(Collectors.joining("\n"))); throw new ScriptException( String.format("Error while executing script: %s%n%nCommand: %s%nPosition: %s%n", diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/InjectionException.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/InjectionException.java index 41b3735fe3e..d81dab26722 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/InjectionException.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/InjectionException.java @@ -30,4 +30,13 @@ public InjectionException(String message) { public InjectionException(String message, Throwable cause) { super(message, cause); } + + /** + * An injection exception with a cause to be displayed. + * + * @param cause the cause of the exception. + */ + public InjectionException(Throwable cause) { + super(cause); + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java index f5881b9e57a..4f4d93eee73 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java @@ -49,7 +49,7 @@ private record ConverterKey( } interface VerifyableParameters { - void verifyParameters() throws IllegalArgumentException; + void verifyParameters() throws IllegalArgumentException, InjectionException; } /** @@ -158,6 +158,14 @@ public T inject(T obj, ScriptCommandAst arguments) unhandledPos.get(), count, obj.getClass().getName())); } + if(obj instanceof VerifyableParameters vp) { + try { + vp.verifyParameters(); + } catch(IllegalArgumentException e) { + throw new InjectionException(e); + } + } + return obj; } From a2f5f2d0bc04a2b7f7e775f3427665cae09231d9 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Wed, 24 Sep 2025 22:09:03 +0200 Subject: [PATCH 14/90] Position info for scripts with url=null, run scripted goals before other goals, print position manual cherry-pick From 547ce291d230a59b3abf4596ccede2e6a64aed75 Mon Sep 17 00:00:00 2001 From: Julian Wiesler Date: Wed, 22 Feb 2023 13:57:21 +0100 Subject: [PATCH] Position info for scripts with url=null, run scripted goals before other goals, print position information --- .../ilkd/key/macros/ApplyScriptsMacro.java | 40 +++++++++++++------ .../java/de/uka/ilkd/key/parser/Location.java | 12 ++++++ .../ilkd/key/scripts/ProofScriptEngine.java | 2 +- .../uka/ilkd/key/scripts/ScriptException.java | 4 +- 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 055ece5053d..c32ab6e43f9 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -3,6 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.macros; +import java.io.IOException; import java.util.*; import java.util.stream.Collectors; @@ -17,11 +18,14 @@ import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.parser.Location; import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.proof.Node; import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.proof.mgt.SpecificationRepository; +import de.uka.ilkd.key.prover.impl.DefaultTaskStartedInfo; import de.uka.ilkd.key.rule.JmlAssertBuiltInRuleApp; import de.uka.ilkd.key.scripts.ProofScriptEngine; import de.uka.ilkd.key.scripts.ScriptCommandAst; +import de.uka.ilkd.key.scripts.ScriptException; import de.uka.ilkd.key.speclang.njml.JmlParser; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofArgContext; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdCaseContext; @@ -29,6 +33,7 @@ import org.key_project.logic.Term; import org.key_project.prover.engine.ProverTaskListener; +import org.key_project.prover.engine.TaskStartedInfo; import org.key_project.prover.rules.RuleApp; import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.util.collection.ImmutableList; @@ -52,35 +57,35 @@ public ApplyScriptsMacro(ProofMacro fallBackMacro) { @Override public String getName() { - return "null"; + return "Apply scripts macro"; } @Override public String getCategory() { - return "null"; + return null; } @Override public String getDescription() { - return "null"; + return "Apply scripts"; } @Override public boolean canApplyTo(Proof proof, ImmutableList<@NonNull Goal> goals, PosInOccurrence posInOcc) { return fallBackMacro.canApplyTo(proof, goals, posInOcc) - || goals.exists(g -> getScript(g) != null); + || goals.exists(g -> getJmlAssert(g.node()) != null); } - private static JmlAssert getScript(Goal goal) { - RuleApp ruleApp = goal.node().parent().getAppliedRuleApp(); + private static JmlAssert getJmlAssert(Node node) { + RuleApp ruleApp = node.parent().getAppliedRuleApp(); if (ruleApp instanceof JmlAssertBuiltInRuleApp) { JTerm target = (JTerm) ruleApp.posInOccurrence().subTerm(); if (target.op() instanceof UpdateApplication) { target = UpdateApplication.getTarget(target); } final SourceElement activeStatement = JavaTools.getActiveStatement(target.javaBlock()); - if (activeStatement instanceof JmlAssert jmlAssert) { + if (activeStatement instanceof JmlAssert jmlAssert && jmlAssert.getAssertionProof() != null) { return jmlAssert; } } @@ -99,17 +104,21 @@ private static JmlAssert getScript(Goal goal) { @Override public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, ImmutableList goals, PosInOccurrence posInOcc, ProverTaskListener listener) - throws InterruptedException, Exception { + throws Exception { + ArrayList laterGoals = new ArrayList<>(goals.size()); for (Goal goal : goals) { if (Thread.interrupted()) { throw new InterruptedException(); } - JmlAssert jmlAssert = getScript(goal); - if (jmlAssert == null || jmlAssert.getAssertionProof() == null) { - // no script found, use fallback macro - fallBackMacro.applyTo(uic, proof, ImmutableList.of(goal), posInOcc, listener); + + JmlAssert jmlAssert = getJmlAssert(goal.node()); + if (jmlAssert == null) { + laterGoals.add(goal); continue; } + + listener.taskStarted(new DefaultTaskStartedInfo(TaskStartedInfo.TaskKind.Other, "Running attached script from goal " + goal.node().serialNr(), 0)); + KeyAst.JMLProofScript proofScript = jmlAssert.getAssertionProof(); Map termMap = getTermMap(jmlAssert, proof.getServices()); JTerm update = getUpdate(goal); @@ -122,6 +131,13 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, pse.execute((AbstractUserInterfaceControl) uic, proof); } + listener.taskStarted(new DefaultTaskStartedInfo(TaskStartedInfo.TaskKind.Other, "Running fallback macro on the remaining goals", 0)); + for (Goal goal : laterGoals) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + fallBackMacro.applyTo(uic, proof, ImmutableList.of(goal), posInOcc, listener); + } return new ProofMacroFinishedInfo(this, proof); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/parser/Location.java b/key.core/src/main/java/de/uka/ilkd/key/parser/Location.java index 6620e3e08a3..c15faea35db 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/parser/Location.java +++ b/key.core/src/main/java/de/uka/ilkd/key/parser/Location.java @@ -6,11 +6,13 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.util.Comparator; import java.util.Objects; import java.util.Optional; import de.uka.ilkd.key.java.Position; +import de.uka.ilkd.key.java.PositionInfo; import de.uka.ilkd.key.util.MiscTools; import com.github.javaparser.ast.CompilationUnit; @@ -79,6 +81,16 @@ public static Location fromNode(Node n) { public Position getPosition() { return position; } + public static Location fromPositionInfo(PositionInfo info) { + Optional uri = info.getURI(); + if(uri.isEmpty()) { + return UNDEFINED; + } else { + Position pos = info.getStartPosition(); + return new Location(uri.get(), pos); + } + } + /** * Internal string representation. Do not rely on format! */ diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java index f0a611a5826..229fa298489 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java @@ -145,7 +145,7 @@ public void execute(AbstractUserInterfaceControl uiControl, List Date: Wed, 24 Sep 2025 22:25:07 +0200 Subject: [PATCH 15/90] Use AutoPilotPrepareProofMacro instead of FinishSymbolicExecutionMacro since it splits a lot better # Conflicts: # key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java --- .../src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java | 1 - .../src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index c32ab6e43f9..d5b22dd4d3a 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -25,7 +25,6 @@ import de.uka.ilkd.key.rule.JmlAssertBuiltInRuleApp; import de.uka.ilkd.key.scripts.ProofScriptEngine; import de.uka.ilkd.key.scripts.ScriptCommandAst; -import de.uka.ilkd.key.scripts.ScriptException; import de.uka.ilkd.key.speclang.njml.JmlParser; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofArgContext; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdCaseContext; diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java index 107c3cbd028..a2992ef312e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java @@ -34,7 +34,7 @@ */ public class ScriptAwareMacro extends SequentialProofMacro { - private final ProofMacro autoMacro = new FinishSymbolicExecutionMacro(); + private final ProofMacro autoMacro = new AutoPilotPrepareProofMacro(); private final ApplyScriptsMacro applyMacro = new ApplyScriptsMacro(new TryCloseMacro()); @Override From 7eaed81ac103355b271d7963da268a4e29748815 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Wed, 24 Sep 2025 22:30:55 +0200 Subject: [PATCH 16/90] working on improved script commands manual cherry-picking commit 886588af1c72cdbf86b7a624a4045c6b7fb6a4b2 Author: Mattias Ulbrich Date: Sun Feb 5 13:14:29 2023 +0100 --- .../main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index d5b22dd4d3a..683338ce92c 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -162,9 +162,13 @@ private Map getTermMap(JmlAssert jmlAssert, Services s private static List renderProof(KeyAst.JMLProofScript script, Map termMap, JTerm update, Services services) { List result = new ArrayList<>(); + // Push current settings onto the settings stack + result.add(new ScriptCommandAst("set", Map.of("stack", "push"), List.of())); for (ProofCmdContext proofCmdContext : script.ctx.proofCmd()) { result.addAll(renderProofCmd(proofCmdContext, termMap, update, services)); } + // Pop settings stack to restore old settings + result.add(new ScriptCommandAst("set", Map.of("stack", "pop"), List.of())); return result; } From 3acf95f69507b219c3eaa2c1e3c599e4b7030140 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 26 May 2023 22:46:36 +0200 Subject: [PATCH 17/90] advancing the immutable list interface --- .../util/collection/ImmutableList.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/key.util/src/main/java/org/key_project/util/collection/ImmutableList.java b/key.util/src/main/java/org/key_project/util/collection/ImmutableList.java index 8fb6d4e5f86..d6e3b4c4038 100644 --- a/key.util/src/main/java/org/key_project/util/collection/ImmutableList.java +++ b/key.util/src/main/java/org/key_project/util/collection/ImmutableList.java @@ -356,20 +356,25 @@ default T last() { remainder = remainder.tail(); } T result = remainder.head(); + // MU: I wonder why this is required. T may be a nullable type ... assert result != null : "@AssumeAssertion(nullness): this should never be null"; return result; } /** - * Get the n-th element of this list. + * Returns the element at the specified position in this list. * - * @param idx the 0-based index of the element - * @return the element at index idx. - * @throws IndexOutOfBoundsException if idx is less than 0 or at - * least {@link #size()}. - */ - default T get(int idx) { - return take(idx).head(); + * @param index index of the element to return + * @return the element at the specified position in this list + * @throws IndexOutOfBoundsException if the index is out of range + * ({@code index < 0 || index >= size()}) + */ + default T get(int index) { + if(index < 0 || index >= size()) { + throw new IndexOutOfBoundsException(); + } else { + return take(index).head(); + } } } From 291d00a6e22068031bfda56905524c5dd8d7ead5 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Wed, 24 Sep 2025 23:04:01 +0200 Subject: [PATCH 18/90] reorganisation of proof script commands manually cherry-picked from old branch --- .../prover/impl/DepthFirstGoalChooser.java | 3 + .../uka/ilkd/key/scripts/AssertCommand.java | 62 +++++++++---------- .../de/uka/ilkd/key/scripts/CutCommand.java | 8 +++ .../de/uka/ilkd/key/scripts/LetCommand.java | 11 +++- .../de/uka/ilkd/key/scripts/RuleCommand.java | 28 +++------ ...de.uka.ilkd.key.scripts.ProofScriptCommand | 2 +- 6 files changed, 62 insertions(+), 52 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/prover/impl/DepthFirstGoalChooser.java b/key.core/src/main/java/de/uka/ilkd/key/prover/impl/DepthFirstGoalChooser.java index d76de6e176b..8e11b4ebd22 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/prover/impl/DepthFirstGoalChooser.java +++ b/key.core/src/main/java/de/uka/ilkd/key/prover/impl/DepthFirstGoalChooser.java @@ -74,6 +74,9 @@ protected void updateGoalListHelp(Object node, ImmutableList newGoals) { nextGoals = ImmutableSLList.nil(); + // Only consider automatic goals + newGoals = newGoals.filter(Goal::isAutomatic); + // Remove "node" and goals contained within "newGoals" while (!selectedList.isEmpty()) { final @NonNull Goal goal = selectedList.head(); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java index 2bc1f27d91e..a800bddd913 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java @@ -1,31 +1,31 @@ -/* This file is part of KeY - https://key-project.org - * KeY is licensed under the GNU General Public License Version 2 - * SPDX-License-Identifier: GPL-2.0-only */ -package de.uka.ilkd.key.scripts; - -/** - * An assertion which essentially performs a cut. - * - * The only difference is that this implementation tampers with the labels of the resulting goals to - * allow them to be - * better recognized in the script engine. - * - * (Unlike in other systems, in KeY the assertion does not remove the original goal formula since - * that is not well-defined in sequent calculus.) - */ -public class AssertCommand extends CutCommand { - - @Override - public String getName() { - return "assert"; - } - - @Override - public void execute(ScriptCommandAst arguments) throws ScriptException, InterruptedException { - var args = state().getValueInjector().inject(new Parameters(), arguments); - var node = state().getFirstOpenAutomaticGoal().node(); - execute(state(), args); - // node.proof().getGoal(node.child(0)).setBranchLabel("Validity"); - // node.proof().getGoal(node.child(1)).setBranchLabel("Usage"); - } -} +///* This file is part of KeY - https://key-project.org +// * KeY is licensed under the GNU General Public License Version 2 +// * SPDX-License-Identifier: GPL-2.0-only */ +//package de.uka.ilkd.key.scripts; +// +///** +// * An assertion which essentially performs a cut. +// * +// * The only difference is that this implementation tampers with the labels of the resulting goals to +// * allow them to be +// * better recognized in the script engine. +// * +// * (Unlike in other systems, in KeY the assertion does not remove the original goal formula since +// * that is not well-defined in sequent calculus.) +// */ +//public class AssertCommand extends CutCommand { +// +// @Override +// public String getName() { +// return "assert"; +// } +// +// @Override +// public void execute(ScriptCommandAst arguments) throws ScriptException, InterruptedException { +// var args = state().getValueInjector().inject(new Parameters(), arguments); +// var node = state().getFirstOpenAutomaticGoal().node(); +// execute(state(), args); +// // node.proof().getGoal(node.child(0)).setBranchLabel("Validity"); +// // node.proof().getGoal(node.child(1)).setBranchLabel("Usage"); +// } +//} diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java index 454393b0fb8..e1809d37167 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java @@ -14,6 +14,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import java.util.List; + /** * The command object CutCommand has as scriptcommand name "cut" As parameters: a formula with the * id "#2" @@ -30,6 +32,12 @@ public String getName() { return "cut"; } + // From within JML scripts, "assert" is more common than "cut" + @Override + public List getAliases() { + return List.of(getName(), "assert"); + } + @Override public String getDocumentation() { return """ diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java index 14a4aa1cfe9..25c3977ab0a 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java @@ -10,6 +10,7 @@ import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.nparser.KeYParser; import de.uka.ilkd.key.pp.AbbrevMap; +import de.uka.ilkd.key.scripts.meta.Documentation; import de.uka.ilkd.key.scripts.meta.ProofScriptArgument; import org.jspecify.annotations.NullMarked; @@ -32,13 +33,21 @@ /// * Apr,2025 (weigl): remove {@code force} in favor of {@code letf}. /// * Jan,2025 (weigl): add new parameter {@code force} to override bindings. @NullMarked +@Documentation(""" + The let command lets you introduce entries to the abbreviation table. + let @abbrev1=term1 ... @abbrev2=term2; + or + letf @abbrev1=term1 ... @abbrev2=term2; + One or more key-value pairs are supported where key starts is @ followed by an identifier and + value is a term. + If letf if used instead of let, the let bindings are overridden otherwise conflicts results into an exception.""") public class LetCommand implements ProofScriptCommand { + @Override public List getArguments() { return List.of(); } - @Override public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst args, EngineState stateMap) throws ScriptException, InterruptedException { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java index 70592408954..5b94519673f 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java @@ -14,6 +14,7 @@ import de.uka.ilkd.key.proof.RuleAppIndex; import de.uka.ilkd.key.rule.*; import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Documentation; import de.uka.ilkd.key.scripts.meta.Option; import de.uka.ilkd.key.scripts.meta.OptionalVarargs; @@ -58,23 +59,6 @@ public String getName() { return "rule"; } - @Override - public String getDocumentation() { - return """ - Command that applies a calculus rule. - All parameters are passed as strings and converted by the command. - - The parameters are: -
    -
  1. #2 = rule name
  2. -
  3. on= key.core.logic.Term on which the rule should be applied to as String (find part of the rule)
  4. -
  5. formula= toplevel formula in which term appears in
  6. -
  7. occ = occurrence number
  8. -
  9. inst_= instantiation
  10. -
- """; - } - @Override public void execute(ScriptCommandAst params) throws ScriptException, InterruptedException { @@ -241,7 +225,7 @@ private IBuiltInRuleApp builtInRuleApp(Parameters p, EngineState state, BuiltInR throw new ScriptException("No matching applications."); } - if (p.occ < 0) { + if (p.occ == null || p.occ < 0) { if (matchingApps.size() > 1) { throw new ScriptException("More than one applicable occurrence"); } @@ -405,18 +389,23 @@ private List filterList(Parameters p, ImmutableList list) return matchingApps; } + @Documentation("Command that applies a calculus rule.") public static class Parameters { @Argument + @Documentation("Name of the rule to be applied.") public @MonotonicNonNull String rulename; @Option(value = "on") + @Documentation("Term on which the rule should be applied to (matching the 'find' clause of the rule).") public @Nullable JTerm on; @Option(value = "formula") + @Documentation("Top-level formula in which the term appears.") public @Nullable JTerm formula; @Option(value = "occ") - public @Nullable int occ = -1; + @Documentation("Occurrence number if more than one occurrence matches.") + public @Nullable Integer occ = -1; /** * Represents a part of a formula (may use Java regular expressions as long as supported by @@ -426,6 +415,7 @@ public static class Parameters { public @Nullable String matches = null; @OptionalVarargs(as = JTerm.class, prefix = "inst_") + @Documentation("Instantiations for schema variables used in the rule.") public Map instantiations = new HashMap<>(); } diff --git a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand index a332100ffa2..3684914fe08 100644 --- a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand +++ b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand @@ -7,7 +7,7 @@ de.uka.ilkd.key.scripts.EchoCommand de.uka.ilkd.key.scripts.FocusCommand de.uka.ilkd.key.scripts.AutoCommand de.uka.ilkd.key.scripts.CutCommand -de.uka.ilkd.key.scripts.AssertCommand +# de.uka.ilkd.key.scripts.AssertCommand # it is an alias for CutCommand now de.uka.ilkd.key.scripts.SetCommand de.uka.ilkd.key.scripts.SetEchoCommand de.uka.ilkd.key.scripts.SetFailOnClosedCommand From ccf4441e548ed94108355efc1790d6f9195e149b Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 26 May 2023 22:46:58 +0200 Subject: [PATCH 19/90] advancing for scripts from JML --- .../ilkd/key/macros/ApplyScriptsMacro.java | 7 ++ .../uka/ilkd/key/scripts/AbstractCommand.java | 11 +- .../ilkd/key/scripts/ExpandDefCommand.java | 116 ++++++++++++++++++ .../key/scripts/SetFailOnClosedCommand.java | 3 + ...de.uka.ilkd.key.scripts.ProofScriptCommand | 1 + .../org/key_project/util/java/IOUtil.java | 31 +++++ 6 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 683338ce92c..b50790ed207 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -44,6 +44,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; + public class ApplyScriptsMacro extends AbstractProofMacro { private static final Logger LOGGER = LoggerFactory.getLogger(ApplyScriptsMacro.class); @@ -136,7 +138,9 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, throw new InterruptedException(); } fallBackMacro.applyTo(uic, proof, ImmutableList.of(goal), posInOcc, listener); + } + return new ProofMacroFinishedInfo(this, proof); } @@ -162,6 +166,9 @@ private Map getTermMap(JmlAssert jmlAssert, Services s private static List renderProof(KeyAst.JMLProofScript script, Map termMap, JTerm update, Services services) { List result = new ArrayList<>(); + // Do not fail on open proofs + // TODO Migrate into SetCommand + result.add(new ScriptCommandAst("failonopen", Map.of(), List.of("off"))); // Push current settings onto the settings stack result.add(new ScriptCommandAst("set", Map.of("stack", "push"), List.of())); for (ProofCmdContext proofCmdContext : script.ctx.proofCmd()) { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AbstractCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AbstractCommand.java index d0badbb8e88..84648d2198e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AbstractCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AbstractCommand.java @@ -13,6 +13,7 @@ import de.uka.ilkd.key.scripts.meta.ArgumentsLifter; import de.uka.ilkd.key.scripts.meta.ProofScriptArgument; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -39,12 +40,16 @@ public abstract class AbstractCommand implements ProofScriptCommand { */ protected @Nullable String documentation = null; - protected final EngineState state() { + /** + * The state object of this engine. + */ + protected final @NonNull EngineState state() { return Objects.requireNonNull(state); } /** - * ... + * The POJO class of the parameter object, or null if this command does not take any parameters via + * a POJO. */ private final @Nullable Class parameterClazz; @@ -81,6 +86,8 @@ public final void execute(AbstractUserInterfaceControl uiControl, ScriptCommandA /// Executes the command logic with the given parameters `args`. /// + /// This is usually overridden by subclasses. + /// /// @param args an instance of the parameters /// @throws ScriptException if something happened during execution /// @throws InterruptedException if thread was interrupted during execution diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java new file mode 100644 index 00000000000..e45bbb003fb --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java @@ -0,0 +1,116 @@ +package de.uka.ilkd.key.scripts; + +import de.uka.ilkd.key.control.AbstractUserInterfaceControl; +import de.uka.ilkd.key.java.Services; +import de.uka.ilkd.key.scripts.meta.Option; +import de.uka.ilkd.key.pp.LogicPrinter; +import de.uka.ilkd.key.proof.BuiltInRuleAppIndex; +import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.proof.Node; +import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.proof.RuleAppIndex; +import de.uka.ilkd.key.rule.BuiltInRule; +import de.uka.ilkd.key.rule.FindTaclet; +import de.uka.ilkd.key.rule.IBuiltInRuleApp; +import de.uka.ilkd.key.rule.MatchConditions; +import de.uka.ilkd.key.rule.NoFindTaclet; +import de.uka.ilkd.key.rule.NoPosTacletApp; +import de.uka.ilkd.key.rule.PosTacletApp; +import de.uka.ilkd.key.rule.TacletApp; +import de.uka.ilkd.key.scripts.meta.Option; +import org.jspecify.annotations.Nullable; +import org.key_project.logic.PosInTerm; +import org.key_project.logic.Term; +import org.key_project.prover.proof.rulefilter.TacletFilter; +import org.key_project.prover.rules.Taclet; +import org.key_project.prover.sequent.PosInOccurrence; +import org.key_project.prover.sequent.SequentFormula; +import org.key_project.util.collection.ImmutableList; +import org.key_project.util.collection.ImmutableSLList; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +public class ExpandDefCommand extends AbstractCommand { + + private static final ExpansionFilter FILTER = new ExpansionFilter(); + + public ExpandDefCommand() { + super(Parameters.class); + } + + @Override + public String getName() { + return "expand"; + } + + @Override + public void execute(ScriptCommandAst command) throws ScriptException, InterruptedException { + var args = state().getValueInjector().inject(new Parameters(), command); + Goal g = state().getFirstOpenAutomaticGoal(); + TacletApp theApp = makeRuleApp(args, state()); + + ImmutableList completions = theApp.findIfFormulaInstantiations(g.sequent(), g.proof().getServices()); + if(completions == null || completions.isEmpty()) { + throw new ScriptException("Cannot complete the rule app"); + } + + g.apply(completions.head()); + } + + private TacletApp makeRuleApp(Parameters p, EngineState state) throws ScriptException { + + Goal g = state.getFirstOpenAutomaticGoal(); + Proof proof = state.getProof(); + + ImmutableList apps = ImmutableList.of(); + for (SequentFormula anteForm : g.sequent().antecedent()) { + apps = apps.prepend(g.ruleAppIndex(). + getTacletAppAtAndBelow(FILTER, new PosInOccurrence(anteForm, PosInTerm.getTopLevel(), true), proof.getServices())); + } + + for (SequentFormula succForm : g.sequent().succedent()) { + apps = apps.prepend(g.ruleAppIndex(). + getTacletAppAtAndBelow(FILTER, new PosInOccurrence(succForm, PosInTerm.getTopLevel(), false), proof.getServices())); + } + + apps = apps.filter(it -> it instanceof PosTacletApp && it.posInOccurrence().subTerm().equals(p.on)); + + if(apps.isEmpty()) { + throw new ScriptException("There is no expansion rule app that matches 'on'"); + } else if(p.occ != null && p.occ >= 0) { + if(p.occ >= apps.size()) { + throw new ScriptException("The 'occ' parameter is beyond the number of occurrences."); + } + return apps.get(p.occ); + } else { + if(apps.size() != 1) { + throw new ScriptException("The 'on' parameter is not unique"); + } + return apps.head(); + } + + } + + public static class Parameters { + @Option(value = "on") + public @Nullable Term on; + @Option(value = "occ") + public @Nullable Integer occ; + } + + private static class ExpansionFilter extends TacletFilter { + + @Override + protected boolean filter(Taclet taclet) { + String name = taclet.name().toString(); + return name.startsWith("Class_invariant_axiom_for") || + name.startsWith("Definition_axiom_for"); + } + } + +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetFailOnClosedCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetFailOnClosedCommand.java index 34119c7b6de..85aeb15dbe6 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetFailOnClosedCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetFailOnClosedCommand.java @@ -15,7 +15,10 @@ * complexity in a try-and-error manner, etc.). * * @author Dominic Steinhoefel + * + * @deprecated This should be merged in the {@link SetCommand} with a parameter like "failonclosed". */ +@Deprecated public class SetFailOnClosedCommand extends AbstractCommand { public SetFailOnClosedCommand() { super(Parameters.class); diff --git a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand index 3684914fe08..f9d23e8f89c 100644 --- a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand +++ b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand @@ -29,6 +29,7 @@ de.uka.ilkd.key.scripts.SkipCommand de.uka.ilkd.key.scripts.AxiomCommand de.uka.ilkd.key.scripts.AssumeCommand # does not exist? # de.uka.ilkd.key.macros.scripts.SettingsCommand +de.uka.ilkd.key.scripts.ExpandDefCommand de.uka.ilkd.key.scripts.AssertOpenGoalsCommand de.uka.ilkd.key.scripts.RewriteCommand de.uka.ilkd.key.scripts.AllCommand diff --git a/key.util/src/main/java/org/key_project/util/java/IOUtil.java b/key.util/src/main/java/org/key_project/util/java/IOUtil.java index 800b88b9e63..c4357a636b4 100644 --- a/key.util/src/main/java/org/key_project/util/java/IOUtil.java +++ b/key.util/src/main/java/org/key_project/util/java/IOUtil.java @@ -737,6 +737,37 @@ public static boolean copy(InputStream source, OutputStream target) throws IOExc } } + public static URL makeMemoryURL(String data) { + try { + return new URL("memory", "", 0, String.format("/%x", System.identityHashCode(data)), new MemoryDataHandler(data)); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + private static final class MemoryDataHandler extends URLStreamHandler { + private final String data; + public MemoryDataHandler(String data) { + this.data = data; + } + @Override + protected URLConnection openConnection(URL u) throws IOException { + // perhaps check the hash code too? + if(!u.getProtocol().equals("memory")) { + throw new IOException("Unsupported protocol"); + } + return new URLConnection(u) { + @Override + public void connect() {} + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(data.getBytes()); + } + }; + } + } + /** * Returns the current directory. * From c941c6173bf6413b06355b107320a90b08f9e203 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 26 May 2023 23:37:55 +0200 Subject: [PATCH 20/90] slight adaptation of the macro used for scripting --- .../src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java index a2992ef312e..107c3cbd028 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java @@ -34,7 +34,7 @@ */ public class ScriptAwareMacro extends SequentialProofMacro { - private final ProofMacro autoMacro = new AutoPilotPrepareProofMacro(); + private final ProofMacro autoMacro = new FinishSymbolicExecutionMacro(); private final ApplyScriptsMacro applyMacro = new ApplyScriptsMacro(new TryCloseMacro()); @Override From 235bb0c88fd52c68d971b90764aa6b6d5034920d Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 2 Jun 2023 08:26:49 +0200 Subject: [PATCH 21/90] more script commands --- .../scripts/DependencyContractCommand.java | 107 ++++++++++++++++++ .../key/scripts/OneStepSimplifierCommand.java | 60 ++++++++++ ...de.uka.ilkd.key.scripts.ProofScriptCommand | 2 + 3 files changed, 169 insertions(+) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java create mode 100644 key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java new file mode 100644 index 00000000000..81e755d770e --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java @@ -0,0 +1,107 @@ +package de.uka.ilkd.key.scripts; + +import de.uka.ilkd.key.java.Services; +import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.scripts.meta.Option; +import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.rule.IBuiltInRuleApp; +import de.uka.ilkd.key.rule.UseDependencyContractApp; +import org.jspecify.annotations.Nullable; +import org.key_project.logic.PosInTerm; +import org.key_project.logic.Term; +import org.key_project.prover.sequent.PosInOccurrence; +import org.key_project.prover.sequent.Sequent; +import org.key_project.prover.sequent.SequentFormula; +import org.key_project.util.collection.ImmutableArray; +import org.key_project.util.collection.ImmutableList; + +import java.util.ArrayList; +import java.util.List; + +public class DependencyContractCommand extends AbstractCommand { + + public DependencyContractCommand() { + super(Parameters.class); + } + + @Override + public String getName() { + return "dependency"; + } + + @Override + public void execute(ScriptCommandAst command) throws ScriptException, InterruptedException { + + Parameters arguments = state().getValueInjector().inject(new Parameters(), command); + + final Goal goal = state.getFirstOpenAutomaticGoal(); + + if (arguments.heap == null) { + Services services = goal.proof().getServices(); + arguments.heap = services.getTermFactory().createTerm(services.getTypeConverter().getHeapLDT().getHeap()); + } + + List pios = find(arguments.on, goal.sequent()); + + if(pios.isEmpty()) { + throw new ScriptException("dependency contract not applicable."); + } else if (pios.size() > 1) { + throw new ScriptException("no unique application"); + } + + PosInOccurrence pio = pios.get(0); + ImmutableList builtins = goal.ruleAppIndex().getBuiltInRules(goal, pio); + for (IBuiltInRuleApp builtin : builtins) { + if (builtin instanceof UseDependencyContractApp) { + apply(goal, (UseDependencyContractApp) builtin, arguments); + } + } + + } + + private List find(JTerm term, Sequent sequent) { + List pios = new ArrayList<>(); + for (SequentFormula sf : sequent.antecedent()) { + PosInOccurrence pio = new PosInOccurrence(sf, PosInTerm.getTopLevel(), true); + find(pios, term, pio); + } + + for (SequentFormula sf : sequent.succedent()) { + PosInOccurrence pio = new PosInOccurrence(sf, PosInTerm.getTopLevel(), false); + find(pios, term, pio); + } + return pios; + } + + private void find(List pios, JTerm term, PosInOccurrence pio) { + Term subTerm = pio.subTerm(); + if (term.equals(subTerm)) { + pios.add(pio); + } else { + ImmutableArray subs = subTerm.subs(); + for (int i = 0; i < subs.size(); i++) { + find(pios, term, pio.down(i)); + } + } + } + + private void apply(Goal goal, UseDependencyContractApp ruleApp, Parameters arguments) { + JTerm on = arguments.on; + JTerm[] subs = on.subs().toArray(new JTerm[0]); + subs[0] = arguments.heap; + Services services = goal.proof().getServices(); + JTerm replaced = services.getTermFactory().createTerm(on.op(), subs, on.boundVars(), on.getLabels()); + List pios = find(replaced, goal.sequent()); + ruleApp = ruleApp.setStep(pios.get(0)); + ruleApp = ruleApp.tryToInstantiateContract(services); + goal.apply(ruleApp); + } + + public static class Parameters { + @Option(value = "on") + public JTerm on; + + @Option(value = "heap") + public @Nullable JTerm heap; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java new file mode 100644 index 00000000000..184116ca1f2 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java @@ -0,0 +1,60 @@ +package de.uka.ilkd.key.scripts; + +import de.uka.ilkd.key.scripts.meta.Option; +import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.rule.IBuiltInRuleApp; +import de.uka.ilkd.key.rule.OneStepSimplifierRuleApp; +import org.jspecify.annotations.Nullable; +import org.key_project.logic.PosInTerm; +import org.key_project.prover.sequent.PosInOccurrence; +import org.key_project.prover.sequent.SequentFormula; +import org.key_project.util.collection.ImmutableList; + +public class OneStepSimplifierCommand extends AbstractCommand { + + public OneStepSimplifierCommand() { + super(Parameters.class); + } + + @Override + public String getName() { + return "oss"; + } + + @Override + public void execute(ScriptCommandAst command) throws ScriptException, InterruptedException { + + var arguments = state().getValueInjector().inject(new Parameters(), command); + + final Goal goal = state.getFirstOpenAutomaticGoal(); + if(arguments.antecedent) { + for (SequentFormula sf : goal.sequent().antecedent()) { + ImmutableList builtins = goal.ruleAppIndex().getBuiltInRules(goal, new PosInOccurrence(sf, PosInTerm.getTopLevel(), true)); + for (IBuiltInRuleApp builtin : builtins) { + if (builtin instanceof OneStepSimplifierRuleApp) { + goal.apply(builtin); + } + } + } + } + + if(arguments.succedent) { + for (SequentFormula sf : goal.sequent().succedent()) { + ImmutableList builtins = goal.ruleAppIndex().getBuiltInRules(goal, new PosInOccurrence(sf, PosInTerm.getTopLevel(), false)); + for (IBuiltInRuleApp builtin : builtins) { + if (builtin instanceof OneStepSimplifierRuleApp) { + goal.apply(builtin); + } + } + } + } + } + + public static class Parameters { + @Option(value = "antecedent") + public @Nullable Boolean antecedent = Boolean.TRUE; + + @Option(value = "succedent") + public @Nullable Boolean succedent = Boolean.TRUE; + } +} diff --git a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand index f9d23e8f89c..bd47db5194b 100644 --- a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand +++ b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand @@ -26,6 +26,8 @@ de.uka.ilkd.key.scripts.SaveNewNameCommand de.uka.ilkd.key.scripts.SchemaVarCommand de.uka.ilkd.key.scripts.JavascriptCommand de.uka.ilkd.key.scripts.SkipCommand +de.uka.ilkd.key.scripts.OneStepSimplifierCommand +de.uka.ilkd.key.scripts.DependencyContractCommand de.uka.ilkd.key.scripts.AxiomCommand de.uka.ilkd.key.scripts.AssumeCommand # does not exist? # de.uka.ilkd.key.macros.scripts.SettingsCommand From fd92326d8cdb726c4747200a8851ebbf2d93738a Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Mon, 5 Jun 2023 09:19:30 +0200 Subject: [PATCH 22/90] improving/correcting script commands --- .../uka/ilkd/key/scripts/ExpandDefCommand.java | 17 ++++++++--------- .../de/uka/ilkd/key/scripts/SetCommand.java | 6 +++++- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java index e45bbb003fb..7eaff45ae7d 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java @@ -26,14 +26,6 @@ import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.prover.sequent.SequentFormula; import org.key_project.util.collection.ImmutableList; -import org.key_project.util.collection.ImmutableSLList; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; public class ExpandDefCommand extends AbstractCommand { @@ -59,7 +51,14 @@ public void execute(ScriptCommandAst command) throws ScriptException, Interrupte throw new ScriptException("Cannot complete the rule app"); } - g.apply(completions.head()); + TacletApp app = completions.head(); + app = app.tryToInstantiate(g.proof().getServices().getOverlay(g.getLocalNamespaces())); + if (app == null || !app.complete()) { + throw new ScriptException("Cannot complete the rule app"); + } + + g.apply(app); + } private TacletApp makeRuleApp(Parameters p, EngineState state) throws ScriptException { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java index dfcd9026e6c..ea5a1201960 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java @@ -50,7 +50,11 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup : StrategyProperties.OSS_OFF); Strategy.updateStrategySettings(proof, newProps); OneStepSimplifier.refreshOSS(proof); - } + } + + if (args.proofSteps != null) { + state.setMaxAutomaticSteps(args.proofSteps); + } if (args.stackAction != null) { Stack stack = From 6fde8b1cf17b1da4499637f093c67ce5505720aa Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Thu, 8 Jun 2023 14:47:20 +0200 Subject: [PATCH 23/90] settings for the auto command --- key.core/src/main/antlr4/JmlParser.g4 | 2 +- .../de/uka/ilkd/key/scripts/AutoCommand.java | 33 +++++++------------ 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/key.core/src/main/antlr4/JmlParser.g4 b/key.core/src/main/antlr4/JmlParser.g4 index e3e5d25b637..c75ff1a70ea 100644 --- a/key.core/src/main/antlr4/JmlParser.g4 +++ b/key.core/src/main/antlr4/JmlParser.g4 @@ -203,7 +203,7 @@ block_specification: method_specification; block_loop_specification: loop_contract_keyword spec_case ((also_keyword)+ loop_contract_keyword spec_case)*; loop_contract_keyword: LOOP_CONTRACT; -assert_statement: (ASSERT expression | UNREACHABLE) (SEMI_TOPLEVEL | assertionProof); +assert_statement: (ASSERT expression | UNREACHABLE) (assertionProof SEMI_TOPLEVEL? | SEMI_TOPLEVEL); //breaks_clause: BREAKS expression; //continues_clause: CONTINUES expression; //returns_clause: RETURNS expression; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java index 5a92b99b744..9dfa98d7358 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java @@ -12,9 +12,7 @@ import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.proof.init.Profile; import de.uka.ilkd.key.prover.impl.ApplyStrategy; -import de.uka.ilkd.key.scripts.meta.Documentation; -import de.uka.ilkd.key.scripts.meta.Flag; -import de.uka.ilkd.key.scripts.meta.Option; +import de.uka.ilkd.key.scripts.meta.*; import de.uka.ilkd.key.strategy.FocussedBreakpointRuleApplicationManager; import de.uka.ilkd.key.strategy.StrategyProperties; @@ -48,11 +46,6 @@ public String getName() { return "auto"; } - @Override - public String getDocumentation() { - return "The AutoCommand invokes the automatic strategy \"Auto\""; - } - @Override public void execute(ScriptCommandAst args) throws ScriptException, InterruptedException { var arguments = state().getValueInjector().inject(new AutoCommand.Parameters(), args); @@ -69,7 +62,7 @@ public void execute(ScriptCommandAst args) throws ScriptException, InterruptedEx goals = state().getProof().openGoals(); } else { final Goal goal = state().getFirstOpenAutomaticGoal(); - goals = ImmutableSLList.nil().prepend(goal); + goals = ImmutableList.of(goal); if (arguments.matches != null || arguments.breakpoint != null) { setupFocussedBreakpointStrategy( // @@ -79,8 +72,8 @@ public void execute(ScriptCommandAst args) throws ScriptException, InterruptedEx // set the max number of steps if given int oldNumberOfSteps = state().getMaxAutomaticSteps(); - if (arguments.getSteps() > 0) { - state().setMaxAutomaticSteps(arguments.getSteps()); + if (arguments.maxSteps > 0) { + state().setMaxAutomaticSteps(arguments.maxSteps); } // set model search if given @@ -134,7 +127,7 @@ private Map prepareOriginalValues() { res.put("classAxioms", new OriginalValue(CLASS_AXIOM_OPTIONS_KEY, CLASS_AXIOM_FREE, CLASS_AXIOM_OFF)); res.put("dependencies", new OriginalValue(DEP_OPTIONS_KEY, DEP_ON, DEP_OFF)); - // ... add further (boolean for the moment) setings here. + // ... add further (boolean for the moment) settings here. return res; } @@ -171,7 +164,7 @@ private void setupFocussedBreakpointStrategy(final String maybeMatchesRegEx, @Documentation(""" The AutoCommand is a command that invokes the automatic strategy "Auto" of KeY. It can be used to automatically prove a goal or a set of goals. - Use with care, as this command may leave the proof state in an unpredictable state + Use with care, as this command may leave the proof in a incomprehensible state with many open goals. Use the command with "close" to make sure the command succeeds for fails without @@ -202,34 +195,30 @@ public static class Parameters { @Flag(value = "modelsearch") @Documentation("Enable model search. Better for some types of arithmetic problems. Sometimes a lot worse") - public boolean modelSearch; + public @Nullable Boolean modelSearch; @Flag(value = "expandQueries") @Documentation("Expand queries by modalities.") - public boolean expandQueries; + public @Nullable Boolean expandQueries; @Flag(value = "classAxioms") @Documentation(""" Enable class axioms. This expands model methods and fields and invariants quite eagerly. \ May lead to divergence.""") - public boolean classAxioms; + public @Nullable Boolean classAxioms; @Flag(value = "dependencies") @Documentation(""" Enable dependency reasoning. In modular reasoning, the value of symbols may stay the same, \ without that its definition is known. May be an enabler, may be a showstopper.""") - public boolean dependencies; - - public int getSteps() { - return maxSteps; - } + public @Nullable Boolean dependencies; } private static final class OriginalValue { private final String settingName; private final String trueValue; private final String falseValue; - private String oldValue; + private @Nullable String oldValue; private OriginalValue(String settingName, String trueValue, String falseValue) { this.settingName = settingName; From 4abbb656448c2559a15df7ed962945bff12412ae Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 9 Jun 2023 13:20:59 +0200 Subject: [PATCH 24/90] propagating strategy settings to the strategy to be used (refers to code available in SetCommand) --- key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java index ea5a1201960..50c9ff965a9 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java @@ -101,7 +101,7 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup * quite complicated implementation, which is inspired by StrategySelectionView. */ - public static void updateStrategySettings(EngineState state, StrategyProperties p) { + protected static void updateStrategySettings(EngineState state, StrategyProperties p) { final Proof proof = state.getProof(); final Strategy strategy = getStrategy(state, p); From f75a8120a672dc6fb240df9da3c603ad4aeaeacf Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Thu, 15 Jun 2023 21:45:11 +0200 Subject: [PATCH 25/90] proof scripts: adding a CHEAT command --- .../de/uka/ilkd/key/scripts/CheatCommand.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java new file mode 100644 index 00000000000..c1f5222666d --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java @@ -0,0 +1,47 @@ +package de.uka.ilkd.key.scripts; + +import de.uka.ilkd.key.control.AbstractUserInterfaceControl; +import de.uka.ilkd.key.proof.calculus.JavaDLSequentKit; +import de.uka.ilkd.key.rule.NoFindTaclet; +import de.uka.ilkd.key.rule.NoPosTacletApp; +import de.uka.ilkd.key.rule.Taclet; +import de.uka.ilkd.key.rule.TacletApp; +import org.key_project.logic.ChoiceExpr; +import org.key_project.logic.Name; +import org.key_project.prover.rules.ApplicationRestriction; +import org.key_project.prover.rules.TacletApplPart; +import org.key_project.prover.rules.TacletAttributes; +import org.key_project.util.collection.DefaultImmutableMap; +import org.key_project.util.collection.ImmutableList; +import org.key_project.util.collection.ImmutableSet; + +public class CheatCommand extends NoArgumentCommand { + private static final Taclet CHEAT_TACLET; + + static { + TacletApplPart applPart = new TacletApplPart(JavaDLSequentKit.getInstance().getEmptySequent(), + ApplicationRestriction.NONE, ImmutableList.of(), ImmutableList.of(), ImmutableList.of(), ImmutableList.of()); + CHEAT_TACLET = new NoFindTaclet(new Name("CHEAT"), applPart, ImmutableList.of(), ImmutableList.of(), + new TacletAttributes("cheat", null), DefaultImmutableMap.nilMap(), ChoiceExpr.TRUE, ImmutableSet.empty()); + } + + @Override + public String getName() { + return "cheat"; + } + + @Override + public String getDocumentation() { + return "Use this to close a goal unconditionally. This is unsound and should only " + + "be used for testing and proof debugging purposes. It is similar to 'sorry' " + + "in Isabelle or 'admit' in Rocq."; + } + + @Override + public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst ast, + EngineState state) + throws ScriptException, InterruptedException { + TacletApp app = NoPosTacletApp.createNoPosTacletApp(CHEAT_TACLET); + state.getFirstOpenAutomaticGoal().apply(app); + } +} From f72da9a0096e32ba09b1dd5ff1bd97aa72df7d2e Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sat, 17 Jun 2023 20:24:37 +0200 Subject: [PATCH 26/90] updating a few of the script commands --- .../main/java/de/uka/ilkd/key/scripts/RuleCommand.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java index 5b94519673f..7f2ccabee0e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java @@ -144,9 +144,11 @@ private TacletApp instantiateTacletApp(final Parameters p, final EngineState sta ImmutableList assumesCandidates = theApp .findIfFormulaInstantiations(state.getFirstOpenAutomaticGoal().sequent(), services); - assumesCandidates = ImmutableList.fromList(filterList(p, assumesCandidates)); + assumesCandidates = ImmutableList.fromList(filterList(services, p, assumesCandidates)); - if (assumesCandidates.size() != 1) { + if (assumesCandidates.size() == 0) { + throw new ScriptException("No \\assumes instantiation"); + } else if (assumesCandidates.size() != 1) { throw new ScriptException("Not a unique \\assumes instantiation"); } @@ -244,7 +246,7 @@ private IBuiltInRuleApp builtInRuleApp(Parameters p, EngineState state, BuiltInR private TacletApp findTacletApp(Parameters p, EngineState state) throws ScriptException { ImmutableList allApps = findAllTacletApps(p, state); - List matchingApps = filterList(p, allApps); + List matchingApps = filterList(state.getProof().getServices(), p, allApps); if (matchingApps.isEmpty()) { throw new ScriptException("No matching applications."); @@ -363,7 +365,7 @@ private static String formatTermString(String str) { /* * Filter those apps from a list that are according to the parameters. */ - private List filterList(Parameters p, ImmutableList list) { + private List filterList(Services services, Parameters p, ImmutableList list) { List matchingApps = new ArrayList<>(); for (TacletApp tacletApp : list) { if (tacletApp instanceof PosTacletApp pta) { From 078115eb06465f63a53d0d5a967916a984614946 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Mon, 19 Jun 2023 10:17:34 +0200 Subject: [PATCH 27/90] introducing the script-aware prep macro --- .../ilkd/key/macros/ApplyScriptsMacro.java | 8 ++- .../uka/ilkd/key/macros/ScriptAwareMacro.java | 8 +-- .../ilkd/key/macros/ScriptAwarePrepMacro.java | 61 +++++++++++++++++++ .../de.uka.ilkd.key.macros.ProofMacro | 1 + 4 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwarePrepMacro.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index b50790ed207..fb529ddb3ae 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -50,7 +50,7 @@ public class ApplyScriptsMacro extends AbstractProofMacro { private static final Logger LOGGER = LoggerFactory.getLogger(ApplyScriptsMacro.class); - private final ProofMacro fallBackMacro; + private final @Nullable ProofMacro fallBackMacro; public ApplyScriptsMacro(ProofMacro fallBackMacro) { this.fallBackMacro = fallBackMacro; @@ -74,7 +74,7 @@ public String getDescription() { @Override public boolean canApplyTo(Proof proof, ImmutableList<@NonNull Goal> goals, PosInOccurrence posInOcc) { - return fallBackMacro.canApplyTo(proof, goals, posInOcc) + return fallBackMacro != null && fallBackMacro.canApplyTo(proof, goals, posInOcc) || goals.exists(g -> getJmlAssert(g.node()) != null); } @@ -137,7 +137,9 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, if (Thread.interrupted()) { throw new InterruptedException(); } - fallBackMacro.applyTo(uic, proof, ImmutableList.of(goal), posInOcc, listener); + if(fallBackMacro != null) { + fallBackMacro.applyTo(uic, proof, ImmutableList.of(goal), posInOcc, listener); + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java index 107c3cbd028..320e7d57c22 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java @@ -18,19 +18,19 @@ /** * This class captures a proof macro which is meant to fully automise KeY proof - * workflow. + * workflow if scripts are present in the JML code. * * It is experimental. * * It performs the following steps: *
    *
  1. Finish symbolic execution - *
  2. >Separate proof obligations" + - *
  3. Expand invariant definitions - *
  4. Try to close all proof obligations + *
  5. Apply macros + *
  6. Try to close provable goals *
* * @author mattias ulbrich + * @see ScriptAwarePrepMacro */ public class ScriptAwareMacro extends SequentialProofMacro { diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwarePrepMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwarePrepMacro.java new file mode 100644 index 00000000000..f9d5c8dceac --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwarePrepMacro.java @@ -0,0 +1,61 @@ +// This file is part of KeY - Integrated Deductive Software Design +// +// Copyright (C) 2001-2011 Universitaet Karlsruhe (TH), Germany +// Universitaet Koblenz-Landau, Germany +// Chalmers University of Technology, Sweden +// Copyright (C) 2011-2014 Karlsruhe Institute of Technology, Germany +// Technical University Darmstadt, Germany +// Chalmers University of Technology, Sweden +// +// The KeY system is protected by the GNU General +// Public License. See LICENSE.TXT for details. +// + +package de.uka.ilkd.key.macros; + +/** + * This class captures a proof macro which is meant to automise KeY proof + * workflow if scripts are present in the JML code. + * + * It is experimental. + * + * It performs the following steps: + *
    + *
  1. Finish symbolic execution + *
  2. Apply macros + *
  3. It does not try to close provable goals + *
+ * + * @author mattias ulbrich + * @see ScriptAwareMacro + */ +public class ScriptAwarePrepMacro extends SequentialProofMacro { + + private final ProofMacro autoMacro = new FinishSymbolicExecutionMacro(); + private final ApplyScriptsMacro applyMacro = new ApplyScriptsMacro(null); + + @Override + public String getScriptCommandName() { + return "script-prep-auto"; + } + + @Override + public String getName() { + return "Script-aware Prep Auto"; + } + + @Override + public String getCategory() { + return "Auto Pilot"; + } + + @Override + public String getDescription() { + return "TODO"; + } + + @Override + protected ProofMacro[] createProofMacroArray() { + return new ProofMacro[] { autoMacro, applyMacro }; + } +} diff --git a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.macros.ProofMacro b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.macros.ProofMacro index 1cc703bb83b..d910282338a 100644 --- a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.macros.ProofMacro +++ b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.macros.ProofMacro @@ -23,3 +23,4 @@ de.uka.ilkd.key.macros.OneStepProofMacro de.uka.ilkd.key.macros.UpdateSimplificationMacro de.uka.ilkd.key.macros.TranscendentalFloatSMTMacro de.uka.ilkd.key.macros.ScriptAwareMacro +de.uka.ilkd.key.macros.ScriptAwarePrepMacro From 8e57525552e6fff1302191070c67034b46c4e45c Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Mon, 19 Jun 2023 14:36:11 +0200 Subject: [PATCH 28/90] issue dialog: skip spaces for squiggly lines --- key.ui/src/main/java/de/uka/ilkd/key/gui/IssueDialog.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/key.ui/src/main/java/de/uka/ilkd/key/gui/IssueDialog.java b/key.ui/src/main/java/de/uka/ilkd/key/gui/IssueDialog.java index e7784b14dd0..2c51fd078b7 100644 --- a/key.ui/src/main/java/de/uka/ilkd/key/gui/IssueDialog.java +++ b/key.ui/src/main/java/de/uka/ilkd/key/gui/IssueDialog.java @@ -788,6 +788,9 @@ private void addHighlights(DefaultHighlighter dh, PositionedString ps) { } String source = txtSource.getText(); int offset = getOffsetFromLineColumn(source, pos); + while(offset < source.length() && Character.isWhitespace(source.charAt(offset))) { + offset ++; + } int end = offset; while (end < source.length() && !Character.isWhitespace(source.charAt(end))) { end++; From 26e9e4ceac76c787183c8037c6b6ed01877e33a6 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Mon, 19 Jun 2023 15:17:31 +0200 Subject: [PATCH 29/90] value injector: towards reporting unknown arguments --- .../meta/UnknownArgumentException.java | 6 +++ .../key/scripts/meta/ValueInjectorTest.java | 41 +++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/UnknownArgumentException.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/UnknownArgumentException.java index 957d9cdbbd0..3cbd1035958 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/UnknownArgumentException.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/UnknownArgumentException.java @@ -9,6 +9,12 @@ * @author Mattias Ulbrich */ public class UnknownArgumentException extends InjectionException { + + /** + * An argument required exception with no cause (to display). + * + * @param message the respective String message to be passed. + */ public UnknownArgumentException(String message) { super(message); } diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/ValueInjectorTest.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/ValueInjectorTest.java index cbb384d3b48..3ddbf9239c4 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/ValueInjectorTest.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/ValueInjectorTest.java @@ -58,6 +58,38 @@ public void testRequired() { () -> ValueInjector.injection(new PPCommand(), pp, ast)); } + // copied from old jmlScript branch ... possibly needs adaptation + @Test + public void testUnknownArguments() { + PP pp = new PP(); + Map args = new HashMap<>(); + ScriptCommandAst ast = new ScriptCommandAst("pp", args, new LinkedList<>(), + null); + args.put("i", "42"); + args.put("b", "true"); + args.put("unknownParameter", "unknownValue"); + assertThrows(UnknownArgumentException.class, + () -> ValueInjector.injection(new PPCommand(), pp, ast)); + } + + // copied from old jmlScript branch ... possibly needs adaptation + @Test + public void testVarargsOld() throws Exception { + PP pp = new PP(); + Map args = new HashMap<>(); + ScriptCommandAst ast = new ScriptCommandAst("pp", args, new LinkedList<>(), + null); + args.put("#literal", "here goes the entire string..."); + args.put("i", "42"); + args.put("b", "true"); + args.put("var_21", "21"); + args.put("var_other", "otherString"); + ValueInjector.injection(new PPCommand(), pp, ast); + assertEquals("21", pp.varargs.get("21")); + assertEquals("otherString", pp.varargs.get("other")); + assertEquals(2, pp.varargs.size()); + } + @Test public void testInferScriptArguments() throws NoSuchFieldException { List meta = ArgumentsLifter.inferScriptArguments(PP.class); @@ -90,8 +122,7 @@ public void testInferScriptArguments() throws NoSuchFieldException { } @Test - public void testFlag() throws ConversionException, ArgumentRequiredException, - InjectionReflectionException, NoSpecifiedConverterException { + public void testFlag() throws Exception { class Options { @Flag boolean a; @@ -110,8 +141,7 @@ class Options { @Test - public void testVarargs() throws ConversionException, ArgumentRequiredException, - InjectionReflectionException, NoSpecifiedConverterException { + public void testVarargs() throws InjectionException { class Varargs { @OptionalVarargs(prefix = "a", as = Boolean.class) Map a; @@ -149,6 +179,9 @@ public static class PP { @Option("q") @MonotonicNonNull String required; + + @OptionalVarargs(prefix = "var_") + Map varargs; } @NullMarked From 874948de9622c611f9be4581fad33377eaec1a83 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Mon, 19 Jun 2023 15:20:50 +0200 Subject: [PATCH 30/90] improved script error reporting --- .../de/uka/ilkd/key/scripts/RuleCommand.java | 2 +- .../de/uka/ilkd/key/scripts/SetCommand.java | 27 ++++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java index 7f2ccabee0e..a09032a0891 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java @@ -391,7 +391,7 @@ private List filterList(Services services, Parameters p, ImmutableLis return matchingApps; } - @Documentation("Command that applies a calculus rule.") + @Documentation("This command can be used to apply a calculus rule to the currently active open goal.") public static class Parameters { @Argument @Documentation("Name of the rule to be applied.") diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java index 50c9ff965a9..cd472ae4d2c 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java @@ -12,6 +12,7 @@ import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.proof.init.Profile; import de.uka.ilkd.key.rule.OneStepSimplifier; +import de.uka.ilkd.key.scripts.meta.Documentation; import de.uka.ilkd.key.scripts.meta.Option; import de.uka.ilkd.key.scripts.meta.OptionalVarargs; import de.uka.ilkd.key.settings.ProofSettings; @@ -75,6 +76,15 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup default: throw new IllegalArgumentException("stack must be either push or pop."); } + } else if(args.userKey != null) { + String[] kv = args.userKey.split(":", 2); + if(kv.length != 2) { + throw new IllegalArgumentException("userData must be of the form key:value. Use userData:\"myKey:myValue\"."); + } + state.putUserData("user." + kv[0], kv[1]); + } else { + throw new IllegalArgumentException( + "You have to set oss, steps, stack, or key(s) and value(s)."); } if (args.proofSteps != null) { @@ -137,23 +147,26 @@ public String getName() { } public static class Parameters { - /** - * One Step Simplification parameter - */ + + @Documentation("Enable/disable one-step simplification") @Option(value = "oss") public @Nullable Boolean oneStepSimplification; - /** - * Maximum number of proof steps parameter - */ + @Documentation("Maximum number of proof steps") @Option(value = "steps") public @Nullable Integer proofSteps; - /** key-value pairs to set */ + @Documentation("key-value pairs to set") @OptionalVarargs public Map settings = HashMap.newHashMap(0); + @Documentation("Push or pop the current settings to/from a stack of settings (mostly used internally)") @Option(value = "stack") public @Nullable String stackAction; + + @Documentation("Set user-defined key-value pair (Syntax: userData:\"key:value\")") + @Option(value = "userData") + public @Nullable String userKey; + } } From 40af0b65332047032ec30bd52e7af39a43d65ebc Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 23 Jun 2023 16:25:09 +0200 Subject: [PATCH 31/90] documentation for script commands --- .../java/de/uka/ilkd/key/scripts/EngineState.java | 4 ++-- .../uka/ilkd/key/scripts/InstantiateCommand.java | 15 +++++++++++---- .../java/de/uka/ilkd/key/scripts/RuleCommand.java | 1 + 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java index 275ea656e7b..969b3ea95b1 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java @@ -366,11 +366,11 @@ public ExprEvaluator getEvaluator() { return exprEvaluator; } - public void putUserData(String key, Object val) { + public void putUserData(String key, @Nullable Object val) { userData.put(key, val); } - public Object getUserData(String key) { + public @Nullable Object getUserData(String key) { return userData.get(key); } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java index a4f795b1011..6a4aa215974 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java @@ -15,9 +15,11 @@ import de.uka.ilkd.key.proof.RuleAppIndex; import de.uka.ilkd.key.rule.PosTacletApp; import de.uka.ilkd.key.rule.TacletApp; +import de.uka.ilkd.key.scripts.meta.Documentation; import de.uka.ilkd.key.scripts.meta.Flag; import de.uka.ilkd.key.scripts.meta.Option; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.key_project.logic.Name; import org.key_project.logic.PosInTerm; import org.key_project.logic.Term; @@ -229,26 +231,31 @@ public String getDocumentation() { """; } - /** - * - */ + @Documentation("Instantiate a universally quantified formula (or an existentially quantified formula in succedent) by a term." + + "One of 'var' or 'formula' must be specified. If 'var' is given, the formula is determined by looking for a particular occurrence of a quantifier over that variable name.\n" + + "'with' must be specified.") public static class Parameters { + @Documentation("The toplevel quantified formula to instantiate. Either this or 'var' must be given.") @Option(value = "formula") @Nullable public JTerm formula; + @Documentation("The name of the bound variable to instantiate. Either this or 'formula' must be given.") @Option(value = "var") @Nullable public String var; + @Documentation("The occurrence number of the quantifier over 'var' in the sequent. Default is 1 (the first).") @Option(value = "occ") public @Nullable int occ = 1; + @Documentation("If given, the rule used for instantiation is the one that hides the instantiated formula.") @Flag("hide") public boolean hide; + @Documentation("The term to instantiate the bound variable with. Must be given.") @Option(value = "with") - public @Nullable JTerm with; + public @MonotonicNonNull JTerm with; } private static class TacletNameFilter extends TacletFilter { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java index a09032a0891..e1455d3688c 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java @@ -413,6 +413,7 @@ public static class Parameters { * Represents a part of a formula (may use Java regular expressions as long as supported by * proof script parser). Rule is applied to the sequent formula which matches that string. */ + @Documentation("Instead of giving the toplevl formula completely, a regular expression can be specified to match the toplevel formula.") @Option(value = "matches") public @Nullable String matches = null; From 8c751cf9f024c8df4af7dcfeb88c2cabf9e1c628 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 30 Jun 2023 14:37:37 +0200 Subject: [PATCH 32/90] better error messaging in ScriptLineParser --- .../java/de/uka/ilkd/key/scripts/ScriptLineParser.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptLineParser.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptLineParser.java index c853a825023..f9ac398f56a 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptLineParser.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptLineParser.java @@ -15,9 +15,9 @@ import org.jspecify.annotations.Nullable; /** + * This class was used to parse script lines before the parsing was integrated into the general ANTLR parser for KeY files. * * @author mattias ulbrich - * */ class ScriptLineParser { @@ -248,7 +248,10 @@ private void exc(int c) throws ScriptException { } private Location getLocation() { - return new Location(fileURI, Position.newOneBased(line, col)); + Position pos = line >= 1 ? + Position.newOneBased(line, col) : + Position.UNDEFINED; + return new Location(fileURI, pos); } public int getOffset() { From 60f646c8c66d39f1c118053adf38d67cc05bcf26 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 30 Jun 2023 15:05:07 +0200 Subject: [PATCH 33/90] implement a recall mechanism --- key.core/src/main/antlr4/JmlParser.g4 | 2 +- .../key/java/ast/statement/JmlAssert.java | 16 ++++++++++++++-- .../key/java/visitor/CreatingASTVisitor.java | 1 + .../de/uka/ilkd/key/rule/JmlAssertRule.java | 19 +++++++++++++++++-- .../TextualJMLAssertStatement.java | 10 ++++++++-- .../key/speclang/njml/TextualTranslator.java | 3 ++- 6 files changed, 43 insertions(+), 8 deletions(-) diff --git a/key.core/src/main/antlr4/JmlParser.g4 b/key.core/src/main/antlr4/JmlParser.g4 index c75ff1a70ea..f2d3412e0ab 100644 --- a/key.core/src/main/antlr4/JmlParser.g4 +++ b/key.core/src/main/antlr4/JmlParser.g4 @@ -203,7 +203,7 @@ block_specification: method_specification; block_loop_specification: loop_contract_keyword spec_case ((also_keyword)+ loop_contract_keyword spec_case)*; loop_contract_keyword: LOOP_CONTRACT; -assert_statement: (ASSERT expression | UNREACHABLE) (assertionProof SEMI_TOPLEVEL? | SEMI_TOPLEVEL); +assert_statement: (ASSERT (label=IDENT COLON)? expression | UNREACHABLE) (assertionProof SEMI_TOPLEVEL? | SEMI_TOPLEVEL); //breaks_clause: BREAKS expression; //continues_clause: CONTINUES expression; //returns_clause: RETURNS expression; diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java index 4859cb421c2..3eb8eff45cc 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java @@ -34,6 +34,12 @@ public class JmlAssert extends JavaStatement { */ private final TextualJMLAssertStatement.Kind kind; + /* + * Temporary solution until full jml labels are there ... + * (To be clarified if compatible still) + */ + private final String optLabel; + /** * the condition in parse tree form */ @@ -50,11 +56,12 @@ public class JmlAssert extends JavaStatement { * @param assertionProof the optional proof for an assert statement (not for assume) * @param positionInfo the position information for this statement */ - public JmlAssert(TextualJMLAssertStatement.Kind kind, KeyAst.Expression condition, + public JmlAssert(TextualJMLAssertStatement.Kind kind, String label, KeyAst.Expression condition, KeyAst.@Nullable JMLProofScript assertionProof, PositionInfo positionInfo) { super(positionInfo); this.kind = kind; + this.optLabel = label; this.condition = condition; this.assertionProof = assertionProof; } @@ -66,13 +73,14 @@ public JmlAssert(TextualJMLAssertStatement.Kind kind, KeyAst.Expression conditio public JmlAssert(ExtList children) { super(children); this.kind = Objects.requireNonNull(children.get(TextualJMLAssertStatement.Kind.class)); + this.optLabel = children.get(String.class); this.condition = Objects.requireNonNull(children.get(KeyAst.Expression.class)); // script may be null this.assertionProof = children.get(KeyAst.JMLProofScript.class); } public JmlAssert(JmlAssert other) { - this(other.kind, other.condition, other.assertionProof, other.getPositionInfo()); + this(other.kind, other.optLabel, other.condition, other.assertionProof, other.getPositionInfo()); } public TextualJMLAssertStatement.Kind getKind() { @@ -197,4 +205,8 @@ public void visit(Visitor v) { result = result.prepend(condition.ctx); return result; } + + public String getOptLabel() { + return optLabel; + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java b/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java index 684b641f125..de756e25907 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java @@ -1520,6 +1520,7 @@ ProgramElement createNewElement(ExtList changeList) { changeList.add(x.getKind()); changeList.add(x.getCondition()); changeList.add(x.getAssertionProof()); + changeList.add(x.getOptLabel()); return new JmlAssert(changeList); } }; diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java b/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java index db4bcbada48..72e9260c812 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java +++ b/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java @@ -15,7 +15,11 @@ import de.uka.ilkd.key.logic.op.Transformer; import de.uka.ilkd.key.logic.op.UpdateApplication; import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.proof.calculus.JavaDLSequentKit; import de.uka.ilkd.key.proof.mgt.SpecificationRepository; +import de.uka.ilkd.key.rule.inst.SVInstantiations; +import de.uka.ilkd.key.rule.tacletbuilder.AntecSuccTacletGoalTemplate; +import de.uka.ilkd.key.rule.tacletbuilder.NoFindTacletBuilder; import de.uka.ilkd.key.speclang.jml.pretranslation.TextualJMLAssertStatement.Kind; import de.uka.ilkd.key.util.MiscTools; @@ -24,6 +28,8 @@ import org.key_project.prover.rules.RuleAbortException; import org.key_project.prover.rules.RuleApp; import org.key_project.prover.sequent.PosInOccurrence; +import org.key_project.prover.sequent.Semisequent; +import org.key_project.prover.sequent.Sequent; import org.key_project.prover.sequent.SequentFormula; import org.key_project.util.collection.ImmutableList; @@ -148,6 +154,8 @@ public IBuiltInRuleApp createApp(PosInOccurrence occurrence, TermServices servic kind == Kind.ASSERT ? OriginTermLabel.SpecType.ASSERT : OriginTermLabel.SpecType.ASSUME)); + final String label = jmlAssert.getOptLabel(); + final ImmutableList result; if (kind == Kind.ASSERT) { result = goal.split(2); @@ -158,7 +166,7 @@ public IBuiltInRuleApp createApp(PosInOccurrence occurrence, TermServices servic throw new RuleAbortException( String.format("Unknown assertion type %s", jmlAssert.getKind())); } - setUpUsageGoal(result.head(), occurrence, update, target, condition, tb, services); + setUpUsageGoal(result.head(), label, occurrence, update, target, condition, tb, services); return result; } @@ -170,7 +178,7 @@ private void setUpValidityRule(Goal goal, goal.changeFormula(new SequentFormula(tb.apply(update, condition)), occurrence); } - private void setUpUsageGoal(Goal goal, PosInOccurrence occurrence, + private void setUpUsageGoal(Goal goal, String label, PosInOccurrence occurrence, JTerm update, JTerm target, JTerm condition, TermBuilder tb, Services services) { goal.setBranchLabel("Usage"); @@ -180,6 +188,13 @@ private void setUpUsageGoal(Goal goal, PosInOccurrence occurrence, tb.prog(((Modality) target.op()).kind(), javaBlock, target.sub(0), null))); goal.changeFormula(new SequentFormula(newTerm), occurrence); + if (label != null) { + NoFindTacletBuilder bld = new NoFindTacletBuilder(); + Sequent ante = JavaDLSequentKit.createAnteSequent(ImmutableList.of(new SequentFormula(tb.apply(update, condition)))); + bld.addTacletGoalTemplate(new AntecSuccTacletGoalTemplate(ante, ImmutableList.of(), JavaDLSequentKit.getInstance().getEmptySequent())); + bld.setName(new Name("recall_" + label)); + goal.addTaclet(bld.getNoFindTaclet(), SVInstantiations.EMPTY_SVINSTANTIATIONS, false); + } } @Override diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/pretranslation/TextualJMLAssertStatement.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/pretranslation/TextualJMLAssertStatement.java index 79b41ec577b..a1856d07fbc 100755 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/pretranslation/TextualJMLAssertStatement.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/pretranslation/TextualJMLAssertStatement.java @@ -15,19 +15,21 @@ */ public class TextualJMLAssertStatement extends TextualJMLConstruct { private final KeyAst.Expression context; + private final String optLabel; private final KeyAst.@Nullable JMLProofScript assertionProof; private final Kind kind; public TextualJMLAssertStatement(Kind kind, KeyAst.Expression clause) { - this(kind, clause, null); + this(kind, clause, null, null); } public TextualJMLAssertStatement(Kind kind, KeyAst.Expression clause, - KeyAst.@Nullable JMLProofScript assertionProof) { + KeyAst.@Nullable JMLProofScript assertionProof, String optLabel) { super(ImmutableSLList.nil(), kind.toString() + " " + clause); this.kind = kind; this.context = clause; this.assertionProof = assertionProof; + this.optLabel = optLabel; } public KeyAst.Expression getContext() { @@ -71,6 +73,10 @@ public Kind getKind() { return kind; } + public String getOptLabel() { + return optLabel; + } + public enum Kind { ASSERT("assert"), ASSUME("assume"); diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java index 7786453f633..42238f7b713 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java @@ -553,7 +553,8 @@ public Object visitAssert_statement(JmlParser.Assert_statementContext ctx) { TextualJMLAssertStatement b = new TextualJMLAssertStatement(TextualJMLAssertStatement.Kind.ASSERT, new KeyAst.Expression(ctx.expression()), - KeyAst.JMLProofScript.fromContext(ctx.assertionProof())); + KeyAst.JMLProofScript.fromContext(ctx.assertionProof()), + ctx.label == null ? null : ctx.label.getText()); finishConstruct(b); return null; } From ea660ccccb8645167fbcde910f9d0ec517d2926f Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 7 Jul 2023 22:39:44 +0200 Subject: [PATCH 34/90] allow let commands without "@" --- .../src/main/java/de/uka/ilkd/key/scripts/LetCommand.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java index 25c3977ab0a..5e6e9a47225 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java @@ -62,13 +62,11 @@ public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst arg continue; } - if (!key.startsWith("@")) { - throw new ScriptException("Unexpected parameter to let, only @var allowed: " + key); + if (key.startsWith("@")) { + // get rid of @ + key = key.substring(1); } - // get rid of @ - key = key.substring(1); - if (abbrMap.containsAbbreviation(key) && !force) { throw new ScriptException(key + " is already fixed in this script"); } From a678f9ff062697e9f71c3332d83d13b40679a3ee Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sat, 22 Jul 2023 12:44:53 +0200 Subject: [PATCH 35/90] a formula parameter for the expand command --- .../ilkd/key/scripts/ExpandDefCommand.java | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java index 7eaff45ae7d..2d685013d05 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java @@ -1,23 +1,12 @@ package de.uka.ilkd.key.scripts; -import de.uka.ilkd.key.control.AbstractUserInterfaceControl; -import de.uka.ilkd.key.java.Services; +import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.logic.equality.TermLabelsProperty; import de.uka.ilkd.key.scripts.meta.Option; -import de.uka.ilkd.key.pp.LogicPrinter; -import de.uka.ilkd.key.proof.BuiltInRuleAppIndex; import de.uka.ilkd.key.proof.Goal; -import de.uka.ilkd.key.proof.Node; import de.uka.ilkd.key.proof.Proof; -import de.uka.ilkd.key.proof.RuleAppIndex; -import de.uka.ilkd.key.rule.BuiltInRule; -import de.uka.ilkd.key.rule.FindTaclet; -import de.uka.ilkd.key.rule.IBuiltInRuleApp; -import de.uka.ilkd.key.rule.MatchConditions; -import de.uka.ilkd.key.rule.NoFindTaclet; -import de.uka.ilkd.key.rule.NoPosTacletApp; import de.uka.ilkd.key.rule.PosTacletApp; import de.uka.ilkd.key.rule.TacletApp; -import de.uka.ilkd.key.scripts.meta.Option; import org.jspecify.annotations.Nullable; import org.key_project.logic.PosInTerm; import org.key_project.logic.Term; @@ -77,7 +66,18 @@ private TacletApp makeRuleApp(Parameters p, EngineState state) throws ScriptExce getTacletAppAtAndBelow(FILTER, new PosInOccurrence(succForm, PosInTerm.getTopLevel(), false), proof.getServices())); } - apps = apps.filter(it -> it instanceof PosTacletApp && it.posInOccurrence().subTerm().equals(p.on)); + if (p.on != null) { + apps = apps.filter( + it -> it instanceof PosTacletApp && + ((JTerm)it.posInOccurrence().subTerm()).equalsModProperty(p.on, TermLabelsProperty.TERM_LABELS_PROPERTY)); + } else if (p.formula != null) { + apps = apps.filter( + it -> it instanceof PosTacletApp && + ((JTerm)it.posInOccurrence().sequentFormula().formula()).equalsModProperty(p.formula, TermLabelsProperty.TERM_LABELS_PROPERTY)); + } else { + throw new ScriptException("Either 'formula' or 'on' must be specified"); + } + if(apps.isEmpty()) { throw new ScriptException("There is no expansion rule app that matches 'on'"); @@ -100,6 +100,9 @@ public static class Parameters { public @Nullable Term on; @Option(value = "occ") public @Nullable Integer occ; + @Option(value = "formula") + public @Nullable JTerm formula; + } private static class ExpansionFilter extends TacletFilter { From 00f91ae77ccf7e61e8f7a92de0bdb7755b08baf3 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sat, 27 Sep 2025 08:31:16 +0200 Subject: [PATCH 36/90] spotlessing after a lengthy cherry-pick session from an old branch --- .../key/java/ast/statement/JmlAssert.java | 3 +- .../ilkd/key/macros/ApplyScriptsMacro.java | 30 ++++++------ .../ilkd/key/macros/ScriptAwarePrepMacro.java | 3 ++ .../java/de/uka/ilkd/key/parser/Location.java | 3 +- .../de/uka/ilkd/key/rule/JmlAssertRule.java | 7 +-- .../uka/ilkd/key/scripts/AbstractCommand.java | 3 +- .../uka/ilkd/key/scripts/AssertCommand.java | 40 ++++++++-------- .../uka/ilkd/key/scripts/BranchesCommand.java | 17 ++++--- .../de/uka/ilkd/key/scripts/CheatCommand.java | 22 ++++++--- .../de/uka/ilkd/key/scripts/CutCommand.java | 4 +- .../scripts/DependencyContractCommand.java | 21 ++++++--- .../ilkd/key/scripts/ExpandDefCommand.java | 46 +++++++++++-------- .../ilkd/key/scripts/InstantiateCommand.java | 2 +- .../de/uka/ilkd/key/scripts/LetCommand.java | 14 +++--- .../key/scripts/OneStepSimplifierCommand.java | 19 +++++--- .../de/uka/ilkd/key/scripts/RuleCommand.java | 3 +- .../ilkd/key/scripts/ScriptLineParser.java | 7 ++- .../de/uka/ilkd/key/scripts/SetCommand.java | 11 +++-- .../ilkd/key/scripts/meta/ValueInjector.java | 4 +- .../key/scripts/meta/ValueInjectorTest.java | 6 +-- .../java/de/uka/ilkd/key/gui/IssueDialog.java | 4 +- .../util/collection/ImmutableList.java | 2 +- .../org/key_project/util/java/IOUtil.java | 7 ++- 23 files changed, 162 insertions(+), 116 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java index 3eb8eff45cc..535d4e98816 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java @@ -80,7 +80,8 @@ public JmlAssert(ExtList children) { } public JmlAssert(JmlAssert other) { - this(other.kind, other.optLabel, other.condition, other.assertionProof, other.getPositionInfo()); + this(other.kind, other.optLabel, other.condition, other.assertionProof, + other.getPositionInfo()); } public TextualJMLAssertStatement.Kind getKind() { diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index fb529ddb3ae..9e825416b5d 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -3,8 +3,8 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.macros; -import java.io.IOException; import java.util.*; +import java.util.ArrayList; import java.util.stream.Collectors; import de.uka.ilkd.key.control.AbstractUserInterfaceControl; @@ -44,8 +44,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; - public class ApplyScriptsMacro extends AbstractProofMacro { private static final Logger LOGGER = LoggerFactory.getLogger(ApplyScriptsMacro.class); @@ -86,7 +84,8 @@ private static JmlAssert getJmlAssert(Node node) { target = UpdateApplication.getTarget(target); } final SourceElement activeStatement = JavaTools.getActiveStatement(target.javaBlock()); - if (activeStatement instanceof JmlAssert jmlAssert && jmlAssert.getAssertionProof() != null) { + if (activeStatement instanceof JmlAssert jmlAssert + && jmlAssert.getAssertionProof() != null) { return jmlAssert; } } @@ -96,7 +95,7 @@ private static JmlAssert getJmlAssert(Node node) { private static @Nullable JTerm getUpdate(Goal goal) { RuleApp ruleApp = goal.node().parent().getAppliedRuleApp(); Term appliedOn = ruleApp.posInOccurrence().subTerm(); - if(appliedOn.op() instanceof UpdateApplication) { + if (appliedOn.op() instanceof UpdateApplication) { return UpdateApplication.getUpdate((JTerm) appliedOn); } return null; @@ -118,7 +117,8 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, continue; } - listener.taskStarted(new DefaultTaskStartedInfo(TaskStartedInfo.TaskKind.Other, "Running attached script from goal " + goal.node().serialNr(), 0)); + listener.taskStarted(new DefaultTaskStartedInfo(TaskStartedInfo.TaskKind.Other, + "Running attached script from goal " + goal.node().serialNr(), 0)); KeyAst.JMLProofScript proofScript = jmlAssert.getAssertionProof(); Map termMap = getTermMap(jmlAssert, proof.getServices()); @@ -127,17 +127,19 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, renderProof(proofScript, termMap, update, proof.getServices()); ProofScriptEngine pse = new ProofScriptEngine(renderedProof, goal); LOGGER.debug("---- Script"); - LOGGER.debug(renderedProof.stream().map(ScriptCommandAst::asCommandLine).collect(Collectors.joining("\n"))); + LOGGER.debug(renderedProof.stream().map(ScriptCommandAst::asCommandLine) + .collect(Collectors.joining("\n"))); LOGGER.debug("---- End Script"); pse.execute((AbstractUserInterfaceControl) uic, proof); } - listener.taskStarted(new DefaultTaskStartedInfo(TaskStartedInfo.TaskKind.Other, "Running fallback macro on the remaining goals", 0)); + listener.taskStarted(new DefaultTaskStartedInfo(TaskStartedInfo.TaskKind.Other, + "Running fallback macro on the remaining goals", 0)); for (Goal goal : laterGoals) { if (Thread.interrupted()) { throw new InterruptedException(); } - if(fallBackMacro != null) { + if (fallBackMacro != null) { fallBackMacro.applyTo(uic, proof, ImmutableList.of(goal), posInOcc, listener); } @@ -165,8 +167,8 @@ private Map getTermMap(JmlAssert jmlAssert, Services s return result; } - private static List renderProof(KeyAst.JMLProofScript script, - Map termMap, JTerm update, Services services) { + private static List renderProof(KeyAst.JMLProofScript script, + Map termMap, JTerm update, Services services) { List result = new ArrayList<>(); // Do not fail on open proofs // TODO Migrate into SetCommand @@ -182,7 +184,7 @@ private static List renderProof(KeyAst.JMLProofScript script, } private static List renderProofCmd(ProofCmdContext ctx, - Map termMap, JTerm update, Services services) { + Map termMap, JTerm update, Services services) { List result = new ArrayList<>(); // Push the current branch context @@ -198,9 +200,9 @@ private static List renderProofCmd(ProofCmdContext ctx, value = StringUtil.stripQuotes(exp.getText()); } else { value = termMap.get(exp); - if(update != null) { + if (update != null) { // Wrap in update application if an update is present - value = services.getTermBuilder().apply(update, (JTerm)value); + value = services.getTermBuilder().apply(update, (JTerm) value); } } if (argContext.argLabel != null) { diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwarePrepMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwarePrepMacro.java index f9d5c8dceac..175f5fa8e51 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwarePrepMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwarePrepMacro.java @@ -1,3 +1,6 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ // This file is part of KeY - Integrated Deductive Software Design // // Copyright (C) 2001-2011 Universitaet Karlsruhe (TH), Germany diff --git a/key.core/src/main/java/de/uka/ilkd/key/parser/Location.java b/key.core/src/main/java/de/uka/ilkd/key/parser/Location.java index c15faea35db..8eed306f16b 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/parser/Location.java +++ b/key.core/src/main/java/de/uka/ilkd/key/parser/Location.java @@ -6,7 +6,6 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; import java.util.Comparator; import java.util.Objects; import java.util.Optional; @@ -83,7 +82,7 @@ public static Location fromNode(Node n) { public static Location fromPositionInfo(PositionInfo info) { Optional uri = info.getURI(); - if(uri.isEmpty()) { + if (uri.isEmpty()) { return UNDEFINED; } else { Position pos = info.getStartPosition(); diff --git a/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java b/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java index 72e9260c812..15f2f3e8c35 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java +++ b/key.core/src/main/java/de/uka/ilkd/key/rule/JmlAssertRule.java @@ -28,7 +28,6 @@ import org.key_project.prover.rules.RuleAbortException; import org.key_project.prover.rules.RuleApp; import org.key_project.prover.sequent.PosInOccurrence; -import org.key_project.prover.sequent.Semisequent; import org.key_project.prover.sequent.Sequent; import org.key_project.prover.sequent.SequentFormula; import org.key_project.util.collection.ImmutableList; @@ -190,8 +189,10 @@ private void setUpUsageGoal(Goal goal, String label, PosInOccurrence occurrence, goal.changeFormula(new SequentFormula(newTerm), occurrence); if (label != null) { NoFindTacletBuilder bld = new NoFindTacletBuilder(); - Sequent ante = JavaDLSequentKit.createAnteSequent(ImmutableList.of(new SequentFormula(tb.apply(update, condition)))); - bld.addTacletGoalTemplate(new AntecSuccTacletGoalTemplate(ante, ImmutableList.of(), JavaDLSequentKit.getInstance().getEmptySequent())); + Sequent ante = JavaDLSequentKit.createAnteSequent( + ImmutableList.of(new SequentFormula(tb.apply(update, condition)))); + bld.addTacletGoalTemplate(new AntecSuccTacletGoalTemplate(ante, ImmutableList.of(), + JavaDLSequentKit.getInstance().getEmptySequent())); bld.setName(new Name("recall_" + label)); goal.addTaclet(bld.getNoFindTaclet(), SVInstantiations.EMPTY_SVINSTANTIATIONS, false); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AbstractCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AbstractCommand.java index 84648d2198e..55fe6f0777e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AbstractCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AbstractCommand.java @@ -48,7 +48,8 @@ public abstract class AbstractCommand implements ProofScriptCommand { } /** - * The POJO class of the parameter object, or null if this command does not take any parameters via + * The POJO class of the parameter object, or null if this command does not take any parameters + * via * a POJO. */ private final @Nullable Class parameterClazz; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java index a800bddd913..8e5d9ba5e40 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertCommand.java @@ -1,31 +1,35 @@ -///* This file is part of KeY - https://key-project.org +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +/// * This file is part of KeY - https://key-project.org // * KeY is licensed under the GNU General Public License Version 2 // * SPDX-License-Identifier: GPL-2.0-only */ -//package de.uka.ilkd.key.scripts; +// package de.uka.ilkd.key.scripts; // -///** +/// ** // * An assertion which essentially performs a cut. // * -// * The only difference is that this implementation tampers with the labels of the resulting goals to +// * The only difference is that this implementation tampers with the labels of the resulting goals +/// to // * allow them to be // * better recognized in the script engine. // * // * (Unlike in other systems, in KeY the assertion does not remove the original goal formula since // * that is not well-defined in sequent calculus.) // */ -//public class AssertCommand extends CutCommand { +// public class AssertCommand extends CutCommand { // -// @Override -// public String getName() { -// return "assert"; -// } +// @Override +// public String getName() { +// return "assert"; +// } // -// @Override -// public void execute(ScriptCommandAst arguments) throws ScriptException, InterruptedException { -// var args = state().getValueInjector().inject(new Parameters(), arguments); -// var node = state().getFirstOpenAutomaticGoal().node(); -// execute(state(), args); -// // node.proof().getGoal(node.child(0)).setBranchLabel("Validity"); -// // node.proof().getGoal(node.child(1)).setBranchLabel("Usage"); -// } -//} +// @Override +// public void execute(ScriptCommandAst arguments) throws ScriptException, InterruptedException { +// var args = state().getValueInjector().inject(new Parameters(), arguments); +// var node = state().getFirstOpenAutomaticGoal().node(); +// execute(state(), args); +// // node.proof().getGoal(node.child(0)).setBranchLabel("Validity"); +// // node.proof().getGoal(node.child(1)).setBranchLabel("Usage"); +// } +// } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java index 33500c34591..23696ecb000 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java @@ -11,20 +11,18 @@ import java.util.Optional; import java.util.Stack; -import de.uka.ilkd.key.logic.op.SortDependingFunction; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Node; import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.rule.TacletApp; import de.uka.ilkd.key.scripts.meta.Argument; -import de.uka.ilkd.key.scripts.meta.InjectionException; import de.uka.ilkd.key.scripts.meta.Option; -import de.uka.ilkd.key.scripts.meta.ValueInjector; -import org.jspecify.annotations.Nullable; import org.key_project.prover.rules.tacletbuilder.TacletGoalTemplate; import org.key_project.util.collection.ImmutableList; +import org.jspecify.annotations.Nullable; + public class BranchesCommand extends AbstractCommand { public BranchesCommand() { @@ -46,7 +44,7 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup state.putUserData("_branchStack", stack); } - if(args.mode == null) { + if (args.mode == null) { throw new ScriptException("For 'branches', a mode must be specified"); } @@ -77,8 +75,8 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup int no = 0; int found = -1; for (TacletGoalTemplate template : templates) { - if(!"main".equals(template.tag())) { - if(found != -1) { + if (!"main".equals(template.tag())) { + if (found != -1) { throw new ScriptException("More than one non-main goal found"); } found = no; @@ -95,12 +93,13 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup state.setGoal(goal); break; default: - throw new ScriptException("Unknown mode " + args.mode + " for the 'branches' command" ); + throw new ScriptException( + "Unknown mode " + args.mode + " for the 'branches' command"); } } private void ensureSingleGoal() { - //state. + // state. } private Goal findGoalByName(Node root, String branch) throws ScriptException { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java index c1f5222666d..8805210e2c9 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java @@ -1,3 +1,6 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.scripts; import de.uka.ilkd.key.control.AbstractUserInterfaceControl; @@ -6,6 +9,7 @@ import de.uka.ilkd.key.rule.NoPosTacletApp; import de.uka.ilkd.key.rule.Taclet; import de.uka.ilkd.key.rule.TacletApp; + import org.key_project.logic.ChoiceExpr; import org.key_project.logic.Name; import org.key_project.prover.rules.ApplicationRestriction; @@ -19,10 +23,14 @@ public class CheatCommand extends NoArgumentCommand { private static final Taclet CHEAT_TACLET; static { - TacletApplPart applPart = new TacletApplPart(JavaDLSequentKit.getInstance().getEmptySequent(), - ApplicationRestriction.NONE, ImmutableList.of(), ImmutableList.of(), ImmutableList.of(), ImmutableList.of()); - CHEAT_TACLET = new NoFindTaclet(new Name("CHEAT"), applPart, ImmutableList.of(), ImmutableList.of(), - new TacletAttributes("cheat", null), DefaultImmutableMap.nilMap(), ChoiceExpr.TRUE, ImmutableSet.empty()); + TacletApplPart applPart = + new TacletApplPart(JavaDLSequentKit.getInstance().getEmptySequent(), + ApplicationRestriction.NONE, ImmutableList.of(), ImmutableList.of(), + ImmutableList.of(), ImmutableList.of()); + CHEAT_TACLET = + new NoFindTaclet(new Name("CHEAT"), applPart, ImmutableList.of(), ImmutableList.of(), + new TacletAttributes("cheat", null), DefaultImmutableMap.nilMap(), ChoiceExpr.TRUE, + ImmutableSet.empty()); } @Override @@ -33,13 +41,13 @@ public String getName() { @Override public String getDocumentation() { return "Use this to close a goal unconditionally. This is unsound and should only " + - "be used for testing and proof debugging purposes. It is similar to 'sorry' " + - "in Isabelle or 'admit' in Rocq."; + "be used for testing and proof debugging purposes. It is similar to 'sorry' " + + "in Isabelle or 'admit' in Rocq."; } @Override public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst ast, - EngineState state) + EngineState state) throws ScriptException, InterruptedException { TacletApp app = NoPosTacletApp.createNoPosTacletApp(CHEAT_TACLET); state.getFirstOpenAutomaticGoal().apply(app); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java index e1809d37167..465c90e41a9 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java @@ -3,6 +3,8 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.scripts; +import java.util.List; + import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.rule.NoPosTacletApp; import de.uka.ilkd.key.rule.Taclet; @@ -14,8 +16,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import java.util.List; - /** * The command object CutCommand has as scriptcommand name "cut" As parameters: a formula with the * id "#2" diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java index 81e755d770e..99c1deaaf5a 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java @@ -1,12 +1,18 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.scripts; +import java.util.ArrayList; +import java.util.List; + import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; -import de.uka.ilkd.key.scripts.meta.Option; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.rule.IBuiltInRuleApp; import de.uka.ilkd.key.rule.UseDependencyContractApp; -import org.jspecify.annotations.Nullable; +import de.uka.ilkd.key.scripts.meta.Option; + import org.key_project.logic.PosInTerm; import org.key_project.logic.Term; import org.key_project.prover.sequent.PosInOccurrence; @@ -15,8 +21,7 @@ import org.key_project.util.collection.ImmutableArray; import org.key_project.util.collection.ImmutableList; -import java.util.ArrayList; -import java.util.List; +import org.jspecify.annotations.Nullable; public class DependencyContractCommand extends AbstractCommand { @@ -38,12 +43,13 @@ public void execute(ScriptCommandAst command) throws ScriptException, Interrupte if (arguments.heap == null) { Services services = goal.proof().getServices(); - arguments.heap = services.getTermFactory().createTerm(services.getTypeConverter().getHeapLDT().getHeap()); + arguments.heap = services.getTermFactory() + .createTerm(services.getTypeConverter().getHeapLDT().getHeap()); } List pios = find(arguments.on, goal.sequent()); - if(pios.isEmpty()) { + if (pios.isEmpty()) { throw new ScriptException("dependency contract not applicable."); } else if (pios.size() > 1) { throw new ScriptException("no unique application"); @@ -90,7 +96,8 @@ private void apply(Goal goal, UseDependencyContractApp ruleApp, Parameters argum JTerm[] subs = on.subs().toArray(new JTerm[0]); subs[0] = arguments.heap; Services services = goal.proof().getServices(); - JTerm replaced = services.getTermFactory().createTerm(on.op(), subs, on.boundVars(), on.getLabels()); + JTerm replaced = + services.getTermFactory().createTerm(on.op(), subs, on.boundVars(), on.getLabels()); List pios = find(replaced, goal.sequent()); ruleApp = ruleApp.setStep(pios.get(0)); ruleApp = ruleApp.tryToInstantiateContract(services); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java index 2d685013d05..113d4b56650 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java @@ -1,13 +1,16 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.scripts; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.equality.TermLabelsProperty; -import de.uka.ilkd.key.scripts.meta.Option; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.rule.PosTacletApp; import de.uka.ilkd.key.rule.TacletApp; -import org.jspecify.annotations.Nullable; +import de.uka.ilkd.key.scripts.meta.Option; + import org.key_project.logic.PosInTerm; import org.key_project.logic.Term; import org.key_project.prover.proof.rulefilter.TacletFilter; @@ -16,6 +19,8 @@ import org.key_project.prover.sequent.SequentFormula; import org.key_project.util.collection.ImmutableList; +import org.jspecify.annotations.Nullable; + public class ExpandDefCommand extends AbstractCommand { private static final ExpansionFilter FILTER = new ExpansionFilter(); @@ -35,8 +40,9 @@ public void execute(ScriptCommandAst command) throws ScriptException, Interrupte Goal g = state().getFirstOpenAutomaticGoal(); TacletApp theApp = makeRuleApp(args, state()); - ImmutableList completions = theApp.findIfFormulaInstantiations(g.sequent(), g.proof().getServices()); - if(completions == null || completions.isEmpty()) { + ImmutableList completions = + theApp.findIfFormulaInstantiations(g.sequent(), g.proof().getServices()); + if (completions == null || completions.isEmpty()) { throw new ScriptException("Cannot complete the rule app"); } @@ -57,37 +63,41 @@ private TacletApp makeRuleApp(Parameters p, EngineState state) throws ScriptExce ImmutableList apps = ImmutableList.of(); for (SequentFormula anteForm : g.sequent().antecedent()) { - apps = apps.prepend(g.ruleAppIndex(). - getTacletAppAtAndBelow(FILTER, new PosInOccurrence(anteForm, PosInTerm.getTopLevel(), true), proof.getServices())); + apps = apps.prepend(g.ruleAppIndex().getTacletAppAtAndBelow(FILTER, + new PosInOccurrence(anteForm, PosInTerm.getTopLevel(), true), proof.getServices())); } for (SequentFormula succForm : g.sequent().succedent()) { - apps = apps.prepend(g.ruleAppIndex(). - getTacletAppAtAndBelow(FILTER, new PosInOccurrence(succForm, PosInTerm.getTopLevel(), false), proof.getServices())); + apps = apps.prepend(g.ruleAppIndex().getTacletAppAtAndBelow(FILTER, + new PosInOccurrence(succForm, PosInTerm.getTopLevel(), false), + proof.getServices())); } if (p.on != null) { apps = apps.filter( - it -> it instanceof PosTacletApp && - ((JTerm)it.posInOccurrence().subTerm()).equalsModProperty(p.on, TermLabelsProperty.TERM_LABELS_PROPERTY)); + it -> it instanceof PosTacletApp && + ((JTerm) it.posInOccurrence().subTerm()).equalsModProperty(p.on, + TermLabelsProperty.TERM_LABELS_PROPERTY)); } else if (p.formula != null) { apps = apps.filter( - it -> it instanceof PosTacletApp && - ((JTerm)it.posInOccurrence().sequentFormula().formula()).equalsModProperty(p.formula, TermLabelsProperty.TERM_LABELS_PROPERTY)); + it -> it instanceof PosTacletApp && + ((JTerm) it.posInOccurrence().sequentFormula().formula()).equalsModProperty( + p.formula, TermLabelsProperty.TERM_LABELS_PROPERTY)); } else { throw new ScriptException("Either 'formula' or 'on' must be specified"); } - if(apps.isEmpty()) { + if (apps.isEmpty()) { throw new ScriptException("There is no expansion rule app that matches 'on'"); - } else if(p.occ != null && p.occ >= 0) { - if(p.occ >= apps.size()) { - throw new ScriptException("The 'occ' parameter is beyond the number of occurrences."); + } else if (p.occ != null && p.occ >= 0) { + if (p.occ >= apps.size()) { + throw new ScriptException( + "The 'occ' parameter is beyond the number of occurrences."); } return apps.get(p.occ); } else { - if(apps.size() != 1) { + if (apps.size() != 1) { throw new ScriptException("The 'on' parameter is not unique"); } return apps.head(); @@ -110,7 +120,7 @@ private static class ExpansionFilter extends TacletFilter { @Override protected boolean filter(Taclet taclet) { String name = taclet.name().toString(); - return name.startsWith("Class_invariant_axiom_for") || + return name.startsWith("Class_invariant_axiom_for") || name.startsWith("Definition_axiom_for"); } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java index 6a4aa215974..6dd0c2e7f96 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java @@ -19,7 +19,6 @@ import de.uka.ilkd.key.scripts.meta.Flag; import de.uka.ilkd.key.scripts.meta.Option; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.key_project.logic.Name; import org.key_project.logic.PosInTerm; import org.key_project.logic.Term; @@ -32,6 +31,7 @@ import org.key_project.util.collection.ImmutableList; import org.key_project.util.collection.ImmutableSLList; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.jspecify.annotations.Nullable; import static de.uka.ilkd.key.logic.equality.RenamingTermProperty.RENAMING_TERM_PROPERTY; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java index 5e6e9a47225..b061d0aba42 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java @@ -34,13 +34,13 @@ /// * Jan,2025 (weigl): add new parameter {@code force} to override bindings. @NullMarked @Documentation(""" - The let command lets you introduce entries to the abbreviation table. - let @abbrev1=term1 ... @abbrev2=term2; - or - letf @abbrev1=term1 ... @abbrev2=term2; - One or more key-value pairs are supported where key starts is @ followed by an identifier and - value is a term. - If letf if used instead of let, the let bindings are overridden otherwise conflicts results into an exception.""") + The let command lets you introduce entries to the abbreviation table. + let @abbrev1=term1 ... @abbrev2=term2; + or + letf @abbrev1=term1 ... @abbrev2=term2; + One or more key-value pairs are supported where key starts is @ followed by an identifier and + value is a term. + If letf if used instead of let, the let bindings are overridden otherwise conflicts results into an exception.""") public class LetCommand implements ProofScriptCommand { @Override diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java index 184116ca1f2..741b6eeee58 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java @@ -1,15 +1,20 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.scripts; -import de.uka.ilkd.key.scripts.meta.Option; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.rule.IBuiltInRuleApp; import de.uka.ilkd.key.rule.OneStepSimplifierRuleApp; -import org.jspecify.annotations.Nullable; +import de.uka.ilkd.key.scripts.meta.Option; + import org.key_project.logic.PosInTerm; import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.prover.sequent.SequentFormula; import org.key_project.util.collection.ImmutableList; +import org.jspecify.annotations.Nullable; + public class OneStepSimplifierCommand extends AbstractCommand { public OneStepSimplifierCommand() { @@ -27,9 +32,10 @@ public void execute(ScriptCommandAst command) throws ScriptException, Interrupte var arguments = state().getValueInjector().inject(new Parameters(), command); final Goal goal = state.getFirstOpenAutomaticGoal(); - if(arguments.antecedent) { + if (arguments.antecedent) { for (SequentFormula sf : goal.sequent().antecedent()) { - ImmutableList builtins = goal.ruleAppIndex().getBuiltInRules(goal, new PosInOccurrence(sf, PosInTerm.getTopLevel(), true)); + ImmutableList builtins = goal.ruleAppIndex().getBuiltInRules(goal, + new PosInOccurrence(sf, PosInTerm.getTopLevel(), true)); for (IBuiltInRuleApp builtin : builtins) { if (builtin instanceof OneStepSimplifierRuleApp) { goal.apply(builtin); @@ -38,9 +44,10 @@ public void execute(ScriptCommandAst command) throws ScriptException, Interrupte } } - if(arguments.succedent) { + if (arguments.succedent) { for (SequentFormula sf : goal.sequent().succedent()) { - ImmutableList builtins = goal.ruleAppIndex().getBuiltInRules(goal, new PosInOccurrence(sf, PosInTerm.getTopLevel(), false)); + ImmutableList builtins = goal.ruleAppIndex().getBuiltInRules(goal, + new PosInOccurrence(sf, PosInTerm.getTopLevel(), false)); for (IBuiltInRuleApp builtin : builtins) { if (builtin instanceof OneStepSimplifierRuleApp) { goal.apply(builtin); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java index e1455d3688c..b96b6609476 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java @@ -365,7 +365,8 @@ private static String formatTermString(String str) { /* * Filter those apps from a list that are according to the parameters. */ - private List filterList(Services services, Parameters p, ImmutableList list) { + private List filterList(Services services, Parameters p, + ImmutableList list) { List matchingApps = new ArrayList<>(); for (TacletApp tacletApp : list) { if (tacletApp instanceof PosTacletApp pta) { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptLineParser.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptLineParser.java index f9ac398f56a..e4f7d40c5f7 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptLineParser.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptLineParser.java @@ -15,7 +15,8 @@ import org.jspecify.annotations.Nullable; /** - * This class was used to parse script lines before the parsing was integrated into the general ANTLR parser for KeY files. + * This class was used to parse script lines before the parsing was integrated into the general + * ANTLR parser for KeY files. * * @author mattias ulbrich */ @@ -248,9 +249,7 @@ private void exc(int c) throws ScriptException { } private Location getLocation() { - Position pos = line >= 1 ? - Position.newOneBased(line, col) : - Position.UNDEFINED; + Position pos = line >= 1 ? Position.newOneBased(line, col) : Position.UNDEFINED; return new Location(fileURI, pos); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java index cd472ae4d2c..addd3beead8 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java @@ -51,11 +51,11 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup : StrategyProperties.OSS_OFF); Strategy.updateStrategySettings(proof, newProps); OneStepSimplifier.refreshOSS(proof); - } + } if (args.proofSteps != null) { state.setMaxAutomaticSteps(args.proofSteps); - } + } if (args.stackAction != null) { Stack stack = @@ -76,10 +76,11 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup default: throw new IllegalArgumentException("stack must be either push or pop."); } - } else if(args.userKey != null) { + } else if (args.userKey != null) { String[] kv = args.userKey.split(":", 2); - if(kv.length != 2) { - throw new IllegalArgumentException("userData must be of the form key:value. Use userData:\"myKey:myValue\"."); + if (kv.length != 2) { + throw new IllegalArgumentException( + "userData must be of the form key:value. Use userData:\"myKey:myValue\"."); } state.putUserData("user." + kv[0], kv[1]); } else { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java index 4f4d93eee73..a875ce6c731 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java @@ -158,10 +158,10 @@ public T inject(T obj, ScriptCommandAst arguments) unhandledPos.get(), count, obj.getClass().getName())); } - if(obj instanceof VerifyableParameters vp) { + if (obj instanceof VerifyableParameters vp) { try { vp.verifyParameters(); - } catch(IllegalArgumentException e) { + } catch (IllegalArgumentException e) { throw new InjectionException(e); } } diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/ValueInjectorTest.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/ValueInjectorTest.java index 3ddbf9239c4..991dc9c3af4 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/ValueInjectorTest.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/ValueInjectorTest.java @@ -64,12 +64,12 @@ public void testUnknownArguments() { PP pp = new PP(); Map args = new HashMap<>(); ScriptCommandAst ast = new ScriptCommandAst("pp", args, new LinkedList<>(), - null); + null); args.put("i", "42"); args.put("b", "true"); args.put("unknownParameter", "unknownValue"); assertThrows(UnknownArgumentException.class, - () -> ValueInjector.injection(new PPCommand(), pp, ast)); + () -> ValueInjector.injection(new PPCommand(), pp, ast)); } // copied from old jmlScript branch ... possibly needs adaptation @@ -78,7 +78,7 @@ public void testVarargsOld() throws Exception { PP pp = new PP(); Map args = new HashMap<>(); ScriptCommandAst ast = new ScriptCommandAst("pp", args, new LinkedList<>(), - null); + null); args.put("#literal", "here goes the entire string..."); args.put("i", "42"); args.put("b", "true"); diff --git a/key.ui/src/main/java/de/uka/ilkd/key/gui/IssueDialog.java b/key.ui/src/main/java/de/uka/ilkd/key/gui/IssueDialog.java index 2c51fd078b7..146c3be1e09 100644 --- a/key.ui/src/main/java/de/uka/ilkd/key/gui/IssueDialog.java +++ b/key.ui/src/main/java/de/uka/ilkd/key/gui/IssueDialog.java @@ -788,8 +788,8 @@ private void addHighlights(DefaultHighlighter dh, PositionedString ps) { } String source = txtSource.getText(); int offset = getOffsetFromLineColumn(source, pos); - while(offset < source.length() && Character.isWhitespace(source.charAt(offset))) { - offset ++; + while (offset < source.length() && Character.isWhitespace(source.charAt(offset))) { + offset++; } int end = offset; while (end < source.length() && !Character.isWhitespace(source.charAt(end))) { diff --git a/key.util/src/main/java/org/key_project/util/collection/ImmutableList.java b/key.util/src/main/java/org/key_project/util/collection/ImmutableList.java index d6e3b4c4038..00f0532c9f9 100644 --- a/key.util/src/main/java/org/key_project/util/collection/ImmutableList.java +++ b/key.util/src/main/java/org/key_project/util/collection/ImmutableList.java @@ -370,7 +370,7 @@ default T last() { * ({@code index < 0 || index >= size()}) */ default T get(int index) { - if(index < 0 || index >= size()) { + if (index < 0 || index >= size()) { throw new IndexOutOfBoundsException(); } else { return take(index).head(); diff --git a/key.util/src/main/java/org/key_project/util/java/IOUtil.java b/key.util/src/main/java/org/key_project/util/java/IOUtil.java index c4357a636b4..4aaf81fe484 100644 --- a/key.util/src/main/java/org/key_project/util/java/IOUtil.java +++ b/key.util/src/main/java/org/key_project/util/java/IOUtil.java @@ -739,7 +739,8 @@ public static boolean copy(InputStream source, OutputStream target) throws IOExc public static URL makeMemoryURL(String data) { try { - return new URL("memory", "", 0, String.format("/%x", System.identityHashCode(data)), new MemoryDataHandler(data)); + return new URL("memory", "", 0, String.format("/%x", System.identityHashCode(data)), + new MemoryDataHandler(data)); } catch (MalformedURLException e) { throw new RuntimeException(e); } @@ -747,13 +748,15 @@ public static URL makeMemoryURL(String data) { private static final class MemoryDataHandler extends URLStreamHandler { private final String data; + public MemoryDataHandler(String data) { this.data = data; } + @Override protected URLConnection openConnection(URL u) throws IOException { // perhaps check the hash code too? - if(!u.getProtocol().equals("memory")) { + if (!u.getProtocol().equals("memory")) { throw new IOException("Unsupported protocol"); } return new URLConnection(u) { From 6fe9a38c0429447309913bb5dcebd9ed66eb172f Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Wed, 1 Oct 2025 18:14:55 +0200 Subject: [PATCH 37/90] towards 'obtain' in JML scripts --- key.core/src/main/antlr4/JmlLexer.g4 | 1 + key.core/src/main/antlr4/JmlParser.g4 | 17 ++- .../java/de/uka/ilkd/key/java/JavaInfo.java | 3 +- .../key/java/ast/statement/JmlAssert.java | 15 +- .../ilkd/key/macros/ApplyScriptsMacro.java | 139 ++++++++++++++---- .../java/de/uka/ilkd/key/nparser/KeyAst.java | 80 ++++++---- .../proof/mgt/SpecificationRepository.java | 7 +- .../jml/translation/JMLSpecFactory.java | 4 +- .../ProgramVariableCollection.java | 2 +- .../de/uka/ilkd/key/speclang/njml/JmlIO.java | 18 +++ .../uka/ilkd/key/scripts/JmlScriptTest.java | 70 +++++++++ .../de/uka/ilkd/key/scripts/jml/Obtain1.java | 11 ++ .../de/uka/ilkd/key/scripts/jml/project.key | 14 ++ 13 files changed, 310 insertions(+), 71 deletions(-) create mode 100644 key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/Obtain1.java create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/project.key diff --git a/key.core/src/main/antlr4/JmlLexer.g4 b/key.core/src/main/antlr4/JmlLexer.g4 index 986d9079b89..d90a738ff3d 100644 --- a/key.core/src/main/antlr4/JmlLexer.g4 +++ b/key.core/src/main/antlr4/JmlLexer.g4 @@ -246,6 +246,7 @@ FP_SUBNORMAL: '\\fp_subnormal'; //KeY extension, not official JML FP_ZERO: '\\fp_zero'; //KeY extension, not official JML FREE: '\\free'; //KeY extension, not official JML FRESH: '\\fresh'; +FROM_GOAL: '\\from_goal'; //KeY extension, not official JML INDEX: '\\index'; INDEXOF: '\\seq_indexOf'; //KeY extension, not official JML INTERSECT: '\\intersect'; //KeY extension, not official JML diff --git a/key.core/src/main/antlr4/JmlParser.g4 b/key.core/src/main/antlr4/JmlParser.g4 index f2d3412e0ab..aed3866b353 100644 --- a/key.core/src/main/antlr4/JmlParser.g4 +++ b/key.core/src/main/antlr4/JmlParser.g4 @@ -9,6 +9,9 @@ options { tokenVocab=JmlLexer; } @members { private SyntaxErrorReporter errorReporter = new SyntaxErrorReporter(getClass()); public SyntaxErrorReporter getErrorReporter() { return errorReporter;} + private boolean isNextToken(String tokenText) { + return _input.LA(1) != Token.EOF && tokenText.equals(_input.LT(1).getText()); + } } modifiersEOF: modifiers EOF; @@ -212,9 +215,19 @@ assert_statement: (ASSERT (label=IDENT COLON)? expression | UNREACHABLE) (assert // --- proof scripts in JML assertionProof: BY (proofCmd | LBRACE ( proofCmd )+ RBRACE) ; proofCmd: - cmd=IDENT ( proofArg )* - ( SEMI | BY ( proofCmd | LBRACE (proofCmd+ | proofCmdCase+) RBRACE )) + // TODO allow more than one var in obtain + { isNextToken("obtain") }? obtain=IDENT typespec var=IDENT + ( obtKind=EQUAL_SINGLE expression SEMI + | obtKind=SUCH_THAT expression proofCmdSuffix + | obtKind=FROM_GOAL SEMI + ) + | cmd=IDENT ( proofArg )* proofCmdSuffix ; + +proofCmdSuffix: + SEMI | BY ( proofCmd | LBRACE (proofCmd+ | proofCmdCase+) RBRACE ) + ; + proofCmdCase: CASE ( label=STRING_LITERAL )? COLON ( proofCmd )* | DEFAULT COLON ( proofCmd )* diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/JavaInfo.java b/key.core/src/main/java/de/uka/ilkd/key/java/JavaInfo.java index 4e780b7ca34..94458e38498 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/JavaInfo.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/JavaInfo.java @@ -23,6 +23,7 @@ import de.uka.ilkd.key.speclang.HeapContext; import de.uka.ilkd.key.speclang.SpecificationElement; +import org.jspecify.annotations.Nullable; import org.key_project.logic.Name; import org.key_project.logic.sort.Sort; import org.key_project.util.LRUCache; @@ -394,7 +395,7 @@ public static boolean isVisibleTo(SpecificationElement ax, KeYJavaType visibleTo /** * returns a KeYJavaType having the given sort */ - public KeYJavaType getKeYJavaType(Sort sort) { + public @Nullable KeYJavaType getKeYJavaType(Sort sort) { List l = lookupSort2KJTCache(sort); if (l != null && l.size() > 0) { // Return first KeYJavaType found for sort. diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java index 535d4e98816..2ab17a9fd59 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java @@ -8,13 +8,15 @@ import de.uka.ilkd.key.java.ast.PositionInfo; import de.uka.ilkd.key.java.ast.ProgramElement; import de.uka.ilkd.key.java.visitor.Visitor; +import de.uka.ilkd.key.logic.op.LocationVariable; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.speclang.jml.pretranslation.TextualJMLAssertStatement; +import de.uka.ilkd.key.speclang.njml.JmlIO; +import de.uka.ilkd.key.speclang.njml.JmlParser; import org.key_project.util.ExtList; import org.key_project.util.collection.ImmutableList; -import org.antlr.v4.runtime.ParserRuleContext; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; @@ -198,8 +200,8 @@ public void visit(Visitor v) { * * @return a freshly created list of at least one term */ - public @NonNull ImmutableList collectTerms() { - ImmutableList result = ImmutableList.of(); + public @NonNull ImmutableList collectTerms() { + ImmutableList result = ImmutableList.of(); if (assertionProof != null) { result = result.prepend(assertionProof.collectTerms()); } @@ -207,6 +209,13 @@ public void visit(Visitor v) { return result; } + public ImmutableList collectVariablesInProof(JmlIO io) { + if (assertionProof != null) { + return assertionProof.getObtainedProgramVars(io); + } + return ImmutableList.of(); + } + public String getOptLabel() { return optLabel; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 9e825416b5d..7b3f9739c22 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -13,18 +13,23 @@ import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.java.SourceElement; import de.uka.ilkd.key.java.statement.JmlAssert; +import de.uka.ilkd.key.logic.DefaultVisitor; import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.logic.op.LocationVariable; import de.uka.ilkd.key.logic.op.UpdateApplication; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.parser.Location; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Node; +import de.uka.ilkd.key.proof.ProgVarReplacer; import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.proof.mgt.SpecificationRepository; import de.uka.ilkd.key.prover.impl.DefaultTaskStartedInfo; import de.uka.ilkd.key.rule.JmlAssertBuiltInRuleApp; import de.uka.ilkd.key.scripts.ProofScriptEngine; import de.uka.ilkd.key.scripts.ScriptCommandAst; +import de.uka.ilkd.key.scripts.ScriptException; +import de.uka.ilkd.key.speclang.njml.JmlLexer; import de.uka.ilkd.key.speclang.njml.JmlParser; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofArgContext; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdCaseContext; @@ -76,6 +81,27 @@ public boolean canApplyTo(Proof proof, ImmutableList<@NonNull Goal> goals, || goals.exists(g -> getJmlAssert(g.node()) != null); } + record ObtainAwareTerm(JTerm term) { + JTerm resolve(Map obtainMap, Services services) { + ProgVarReplacer pvr = new ProgVarReplacer(obtainMap, services); + JTerm result = pvr.replace(term); + assertNoObtainVarsLeft(result, obtainMap); + return result; + } + + private void assertNoObtainVarsLeft(JTerm term, Map obtainMap) { + var v = new DefaultVisitor() { + @Override + public void visit(Term visited) { + if(obtainMap.containsKey(term.op())) { + throw new RuntimeException("Use of obtain variable before it being obtained: " + term.op()); + } + } + }; + term.execPreOrder(v); + } + } + private static JmlAssert getJmlAssert(Node node) { RuleApp ruleApp = node.parent().getAppliedRuleApp(); if (ruleApp instanceof JmlAssertBuiltInRuleApp) { @@ -122,10 +148,15 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, KeyAst.JMLProofScript proofScript = jmlAssert.getAssertionProof(); Map termMap = getTermMap(jmlAssert, proof.getServices()); + // We heavily rely on that variables have been computed before, otherwise this will raise an NPE. + Map obtainMap = makeObtainVarMap(jmlAssert.collectVariablesInProof(null)); JTerm update = getUpdate(goal); List renderedProof = renderProof(proofScript, termMap, update, proof.getServices()); ProofScriptEngine pse = new ProofScriptEngine(renderedProof, goal); + pse.getStateMap().putUserData("jml.obtainVarMap", obtainMap); + pse.getStateMap().getValueInjector().addConverter(JTerm.class, ObtainAwareTerm.class, + oat -> oat.resolve(obtainMap, goal.proof().getServices())); LOGGER.debug("---- Script"); LOGGER.debug(renderedProof.stream().map(ScriptCommandAst::asCommandLine) .collect(Collectors.joining("\n"))); @@ -157,18 +188,25 @@ private Map getTermMap(JmlAssert jmlAssert, Services s "No specification found for JML assert statement at " + jmlAssert); } ImmutableList terms = jmlspec.terms().tail(); - ImmutableList jmlExprs = jmlAssert.collectTerms().tail(); + ImmutableList jmlExprs = jmlAssert.collectTerms().tail(); Map result = new IdentityHashMap<>(); assert terms.size() == jmlExprs.size(); for (int i = 0; i < terms.size(); i++) { - // TODO build a map from jmlExprs.get(i) to terms.get(i) result.put(jmlExprs.get(i), terms.get(i)); } return result; } + private Map makeObtainVarMap(ImmutableList locationVariables) { + HashMap result = new HashMap<>(); + for (LocationVariable lv : locationVariables) { + result.put(lv, null); + } + return result; + } + private static List renderProof(KeyAst.JMLProofScript script, - Map termMap, JTerm update, Services services) { + Map termMap, JTerm update, Services services) throws ScriptException { List result = new ArrayList<>(); // Do not fail on open proofs // TODO Migrate into SetCommand @@ -184,13 +222,79 @@ private static List renderProof(KeyAst.JMLProofScript script, } private static List renderProofCmd(ProofCmdContext ctx, - Map termMap, JTerm update, Services services) { + Map termMap, JTerm update, Services services) throws ScriptException { List result = new ArrayList<>(); // Push the current branch context result.add(new ScriptCommandAst("branches", Map.of(), List.of("push"))); // Compose the command itself + if(ctx.obtain != null) { + ScriptCommandAst command = renderObtainCommand(ctx, termMap, update, services); + result.add(command); + } else { + ScriptCommandAst command = renderRegularCommand(ctx, termMap, update, services); + result.add(command); + } + + // handle followup proofCmd if present + JmlParser.ProofCmdSuffixContext suffix = ctx.proofCmdSuffix(); + if(suffix != null) { + if (!suffix.proofCmd().isEmpty()) { + result.add(new ScriptCommandAst("branches", Map.of(), List.of("single"))); + for (ProofCmdContext proofCmdContext : suffix.proofCmd()) { + result.addAll(renderProofCmd(proofCmdContext, termMap, update, services)); + } + } + + // handle proofCmdCases if present + for (ProofCmdCaseContext pcase : suffix.proofCmdCase()) { + String label = StringUtil.stripQuotes(pcase.label.getText()); + result.add(new ScriptCommandAst("branches", Map.of("branch", label), + List.of("select"))); + for (ProofCmdContext proofCmdContext : pcase.proofCmd()) { + result.addAll(renderProofCmd(proofCmdContext, termMap, update, services)); + } + } + } + + // Pop the branch stack + result.add(new ScriptCommandAst("branches", Map.of(), List.of("pop"))); + + return result; + } + + private static ScriptCommandAst renderObtainCommand(ProofCmdContext ctx, Map termMap, JTerm update, Services services) throws ScriptException { + Map named = new HashMap<>(); + + String argName = switch(ctx.obtKind.getType()) { + case JmlLexer.SUCH_THAT -> "such_that"; + case JmlLexer.EQUAL_SINGLE -> "equals"; + case JmlLexer.FROM_GOAL -> "from_goal"; + default -> throw new ScriptException("Unknown obtain kind: " + ctx.obtKind.getText()); + }; + + if(ctx.expression() == null) { + named.put(argName, true); + } else { + JmlParser.ExpressionContext exp = ctx.expression(); + Object value; + if (isStringLiteral(exp)) { + value = StringUtil.stripQuotes(exp.getText()); + } else { + value = termMap.get(exp); + if (update != null) { + // Wrap in update application if an update is present + value = services.getTermBuilder().apply(update, (JTerm) value); + } + } + named.put(argName, value); + } + + return new ScriptCommandAst("__obtain", named, List.of(), Location.fromToken(ctx.start)); + } + + private static @NonNull ScriptCommandAst renderRegularCommand(ProofCmdContext ctx, Map termMap, JTerm update, Services services) { Map named = new HashMap<>(); List positional = new ArrayList<>(); for (ProofArgContext argContext : ctx.proofArg()) { @@ -211,31 +315,8 @@ private static List renderProofCmd(ProofCmdContext ctx, positional.add(value); } } - result.add(new ScriptCommandAst(ctx.cmd.getText(), named, positional, - Location.fromToken(ctx.start))); - - // handle proofCmd if present - if (!ctx.proofCmd().isEmpty()) { - result.add(new ScriptCommandAst("branches", Map.of(), List.of("single"))); - for (ProofCmdContext proofCmdContext : ctx.proofCmd()) { - result.addAll(renderProofCmd(proofCmdContext, termMap, update, services)); - } - } - - // handle proofCmdCase if present - for (ProofCmdCaseContext pcase : ctx.proofCmdCase()) { - String label = StringUtil.stripQuotes(pcase.label.getText()); - result.add(new ScriptCommandAst("branches", Map.of("branch", label), - List.of("select"))); - for (ProofCmdContext proofCmdContext : pcase.proofCmd()) { - result.addAll(renderProofCmd(proofCmdContext, termMap, update, services)); - } - } - - // Pop the branch stack - result.add(new ScriptCommandAst("branches", Map.of(), List.of("pop"))); - - return result; + return new ScriptCommandAst(ctx.cmd.getText(), named, positional, + Location.fromToken(ctx.start)); } private static boolean isStringLiteral(JmlParser.ExpressionContext ctx) { diff --git a/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java b/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java index bc997990614..bc4085cac43 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java +++ b/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java @@ -12,6 +12,9 @@ import java.util.List; import de.uka.ilkd.key.java.Position; +import de.uka.ilkd.key.java.abstraction.KeYJavaType; +import de.uka.ilkd.key.logic.ProgramElementName; +import de.uka.ilkd.key.logic.op.LocationVariable; import de.uka.ilkd.key.nparser.builder.BuilderHelpers; import de.uka.ilkd.key.nparser.builder.ChoiceFinder; import de.uka.ilkd.key.nparser.builder.FindProblemInformation; @@ -22,8 +25,10 @@ import de.uka.ilkd.key.scripts.ScriptCommandAst; import de.uka.ilkd.key.settings.Configuration; import de.uka.ilkd.key.settings.ProofSettings; +import de.uka.ilkd.key.speclang.njml.JmlIO; import de.uka.ilkd.key.speclang.njml.JmlParser; +import de.uka.ilkd.key.speclang.njml.JmlParserBaseVisitor; import org.key_project.util.collection.ImmutableList; import org.key_project.util.java.StringUtil; @@ -215,6 +220,40 @@ public Expression(JmlParser.@NonNull ExpressionContext ctx) { } public static class JMLProofScript extends KeyAst { + + private static class ObtainedVarsVisitor extends JmlParserBaseVisitor { + /// To make debugging easier, obtained variables have a special (hopefully but not necessarily unique) prefix. + public static final String OBTAIN_PREFIX = "_obtained_"; + private ImmutableList collectedVars = ImmutableList.of(); + private final JmlIO io; + + private ObtainedVarsVisitor(JmlIO io) { + this.io = io; + } + + @Override + public Void visitProofCmd(JmlParser.ProofCmdContext ctx) { + if(ctx.obtain != null) { + KeYJavaType type = io.translateType(ctx.typespec()); + ProgramElementName name = new ProgramElementName(OBTAIN_PREFIX + ctx.var.getText()); + collectedVars = collectedVars.prepend(new LocationVariable(name, type, true)); + } + return null; + } + } + + private static class TermCollectionVisitor extends JmlParserBaseVisitor { + private ImmutableList collectedTerms = ImmutableList.of(); + + @Override + public Void visitExpression(JmlParser.ExpressionContext ctx) { + collectedTerms = collectedTerms.prepend(ctx); + return null; + } + } + + private ImmutableList obtainedProgramVars; + public JMLProofScript(JmlParser.@NonNull AssertionProofContext ctx) { super(ctx); } @@ -227,32 +266,13 @@ public static JMLProofScript fromContext(JmlParser.AssertionProofContext ctx) { } } - /** - * Collect all JML expressions in a script (and potentially sub-blocks) - * - * @param cmd the command to collect from - * @return a list in reverse(!) order of all expressions in cmd - */ - private static ImmutableList collectTerms( - JmlParser.ProofCmdContext cmd) { - ImmutableList result = ImmutableList.of(); - for (JmlParser.ProofArgContext arg : cmd.proofArg()) { - JmlParser.ExpressionContext exp = arg.expression(); - if (exp != null) { - result = result.prepend(exp); - } - } - for (JmlParser.ProofCmdContext childCmd : cmd.proofCmd()) { - result = result.prepend(collectTerms(childCmd)); - } - if (cmd.proofCmdCase() != null) { - for (JmlParser.ProofCmdCaseContext pcase : cmd.proofCmdCase()) { - for (JmlParser.ProofCmdContext childCmd : pcase.proofCmd()) { - result = result.prepend(collectTerms(childCmd)); - } - } + public ImmutableList getObtainedProgramVars(JmlIO io) { + if(obtainedProgramVars == null) { + var visitor = new ObtainedVarsVisitor(io); + ctx.accept(visitor); + obtainedProgramVars = visitor.collectedVars; } - return result; + return obtainedProgramVars; } /** @@ -260,12 +280,10 @@ private static ImmutableList collectTerms( * * Todo: Consider caching the result if this is called very often. */ - public @NonNull ImmutableList collectTerms() { - ImmutableList result = ImmutableList.of(); - for (JmlParser.ProofCmdContext cmd : ctx.proofCmd()) { - result = result.prepend(collectTerms(cmd)); - } - return result.reverse(); + public @NonNull ImmutableList collectTerms() { + TermCollectionVisitor visitor = new TermCollectionVisitor(); + ctx.accept(visitor); + return visitor.collectedTerms.reverse(); } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/mgt/SpecificationRepository.java b/key.core/src/main/java/de/uka/ilkd/key/proof/mgt/SpecificationRepository.java index 6b3af7663ee..3bd4423d870 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/mgt/SpecificationRepository.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/mgt/SpecificationRepository.java @@ -1616,11 +1616,12 @@ public void processJavaType(KeYJavaType kjt) { * list of terms, in * an immutable fasion. Updates require to create instances. *

- * Note: There is a immutability hole in {@link ProgramVariableCollection} due to mutable + * Note: There is an immutability hole in {@link ProgramVariableCollection} due to mutable * {@link Map} *

* For {@link de.uka.ilkd.key.java.ast.statement.JmlAssert} this is the formula behind the - * assert. + * assert + * (Potientially also containing the formulas within the optional proof). * For {@link de.uka.ilkd.key.java.ast.statement.SetStatement} this is the target and the value * terms. * You may want to use the index constant for accessing them: @@ -1647,7 +1648,7 @@ public JTerm term(int index) { } /** - * Retrieve a term with a update to the given {@code self} term. + * Retrieve a term with an update to the given {@code self} term. * * @param services the corresponding services instance * @param self a term which describes the {@code self} object aka. this on the current diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/translation/JMLSpecFactory.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/translation/JMLSpecFactory.java index af6200a0604..6ab04e602e4 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/translation/JMLSpecFactory.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/translation/JMLSpecFactory.java @@ -1522,7 +1522,7 @@ private ProgramVariableCollection createProgramVariablesForStatement(Statement s * @param pm the enclosing method */ public void translateJmlAssertCondition(final JmlAssert jmlAssert, final IProgramMethod pm) { - final var pv = createProgramVariablesForStatement(jmlAssert, pm); + final ProgramVariableCollection pv = createProgramVariablesForStatement(jmlAssert, pm); var io = new JmlIO(services).context(Context.inMethod(pm, tb)) .selfVar(pv.selfVar) .parameters(pv.paramVars) @@ -1530,6 +1530,8 @@ public void translateJmlAssertCondition(final JmlAssert jmlAssert, final IProgra .exceptionVariable(pv.excVar) .atPres(pv.atPres) .atBefore(pv.atBefores); + ImmutableList varsInProof = jmlAssert.collectVariablesInProof(io); + io.parameters(pv.paramVars.prepend(varsInProof)); ImmutableList terms = jmlAssert.collectTerms().map(io::translateTerm); services.getSpecificationRepository().addStatementSpec( jmlAssert, diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/translation/ProgramVariableCollection.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/translation/ProgramVariableCollection.java index a431c2cef22..1c0ee8f2073 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/translation/ProgramVariableCollection.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/jml/translation/ProgramVariableCollection.java @@ -79,7 +79,7 @@ public ProgramVariableCollection(LocationVariable selfVar, * * @param selfVar {@code self} * @param paramVars the list of method parameters if the textual specification case is a method - * contract. + * contract. May also contain the local variables visible at a statement. * @param resultVar {@code result} * @param excVar {@code exception} * @param atPreVars a map from every variable {@code var} to {@code \old(var)}. diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlIO.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlIO.java index 9f9cee10243..b4dcc1d4d6d 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlIO.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlIO.java @@ -20,6 +20,7 @@ import de.uka.ilkd.key.util.InfFlowSpec; import de.uka.ilkd.key.util.mergerule.MergeParamsSpec; +import org.key_project.logic.sort.Sort; import org.key_project.util.collection.ImmutableList; import org.key_project.util.collection.ImmutableSLList; import org.key_project.util.collection.Pair; @@ -199,6 +200,22 @@ public JTerm translateTerm(ParserRuleContext expr) { } } + /** + * Interpret the given parse tree as an KeYJavaType in the current context. + * May return null if the KJT cannot be resolved. + */ + public @Nullable KeYJavaType translateType(JmlParser.TypespecContext ctx) { + Object interpreted = interpret(ctx); + return switch (interpreted) { + case SLExpression slExpression -> slExpression.getType(); + case Sort sort -> services.getJavaInfo().getKeYJavaType(sort); + case KeYJavaType kjt -> kjt; + case Type type -> services.getJavaInfo().getKeYJavaType(type); + default -> throw new IllegalArgumentException("Cannot translate to KeYJavaType: " + + interpreted + " of class " + interpreted.getClass()); + }; + } + /** * Interpret the given parse tree as an JML expression in the current context. Label is * attached. @@ -410,4 +427,5 @@ public ImmutableList getWarnings() { public void clearWarnings() { warnings = ImmutableSLList.nil(); } + } diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java new file mode 100644 index 00000000000..bb5db7edbb6 --- /dev/null +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java @@ -0,0 +1,70 @@ +package de.uka.ilkd.key.scripts; + +import de.uka.ilkd.key.control.DefaultUserInterfaceControl; +import de.uka.ilkd.key.control.KeYEnvironment; +import de.uka.ilkd.key.control.UserInterfaceControl; +import de.uka.ilkd.key.nparser.KeyAst; +import de.uka.ilkd.key.proof.io.ProblemLoaderControl; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.Comparator; +import java.util.stream.Stream; + +public class JmlScriptTest { + + private static final Path KEY_FILE; + static { + URL url = JmlScriptTest.class.getResource("jml/project.key"); + try { + KEY_FILE = Paths.get(url.toURI()); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + @ParameterizedTest(name = "{0}") + @MethodSource("filesProvider") + public void testJmlScript(Path path) throws Exception { + + Path tmpDir = Files.createTempDirectory("key.jmltest."); + try { + Files.copy(path, tmpDir.resolve("Test.java")); + Path projectFile = tmpDir.resolve("project.key"); + Files.copy(KEY_FILE, projectFile); + KeYEnvironment env = KeYEnvironment.load(projectFile); + KeyAst.ProofScript script = env.getProofScript(); + if (script != null) { + ProofScriptEngine pse = new ProofScriptEngine(script); + pse.execute(env.getUi(), env.getLoadedProof()); + } + // TODO read comments from java file to allow for more than only closed proofs ... + Assertions.assertTrue(env.getLoadedProof().closed(), "Proof did not close."); + } finally { + // Uncomment the following line to delete the temporary directory after the test + // Files.walk(tmpDir).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); + } + + } + + public static Stream filesProvider() throws URISyntaxException, IOException { + URL jmlUrl = JmlScriptTest.class.getResource("jml"); + return Files.list(Paths.get(jmlUrl.toURI())) + .filter(p -> p.toString().endsWith(".java")) + .map(p -> Arguments.of(p)); + } + + + +} diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/Obtain1.java b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/Obtain1.java new file mode 100644 index 00000000000..34814fd588d --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/Obtain1.java @@ -0,0 +1,11 @@ +class Test { + //@ ensures true; + void test() { + int x = 42; + /*@ assert x == 42 \by { + obtain int y = 41; + assert x+1 == 42; + } */ + } + +} \ No newline at end of file diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/project.key b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/project.key new file mode 100644 index 00000000000..dad8dd3de51 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/project.key @@ -0,0 +1,14 @@ +\profile "Java Profile"; + + +\javaSource "."; + +\proofObligation { + "class" : "de.uka.ilkd.key.proof.init.FunctionalOperationContractPO", + "contract" : "Test[Test::test()].JML operation contract.0", + "name" : "Test[Test::test()].JML operation contract.0" + } + +\proofScript { + macro "script-auto"; +} From 7179e810f25c95e5b252523aa00c0652eeb89d07 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Wed, 1 Oct 2025 18:40:45 +0200 Subject: [PATCH 38/90] update ProofScriptEngine's workflow after discussion with Alexander --- .../ilkd/key/macros/ApplyScriptsMacro.java | 5 +- .../de/uka/ilkd/key/scripts/EngineState.java | 2 +- .../ilkd/key/scripts/ProofScriptEngine.java | 107 ++++++------------ .../uka/ilkd/key/scripts/ScriptCommand.java | 4 +- .../uka/ilkd/key/logic/TestLocalSymbols.java | 4 +- .../key/proof/proverules/ProveRulesTest.java | 4 +- .../ilkd/key/scripts/FocusCommandTest.java | 8 +- .../uka/ilkd/key/scripts/JmlScriptTest.java | 4 +- .../key/scripts/TestProofScriptCommand.java | 7 +- .../ilkd/key/scripts/meta/RewriteTest.java | 8 +- .../key/proof/runallproofs/ProveTest.java | 4 +- .../proofcollection/TestFile.java | 4 +- .../uka/ilkd/key/gui/ProofScriptWorker.java | 6 +- .../key/ui/ConsoleUserInterfaceControl.java | 4 +- 14 files changed, 70 insertions(+), 101 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 7b3f9739c22..6557a92ea0f 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -153,7 +153,8 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, JTerm update = getUpdate(goal); List renderedProof = renderProof(proofScript, termMap, update, proof.getServices()); - ProofScriptEngine pse = new ProofScriptEngine(renderedProof, goal); + ProofScriptEngine pse = new ProofScriptEngine(proof); + pse.setInitiallySelectedGoal(goal); pse.getStateMap().putUserData("jml.obtainVarMap", obtainMap); pse.getStateMap().getValueInjector().addConverter(JTerm.class, ObtainAwareTerm.class, oat -> oat.resolve(obtainMap, goal.proof().getServices())); @@ -162,7 +163,7 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, .collect(Collectors.joining("\n"))); LOGGER.debug("---- End Script"); - pse.execute((AbstractUserInterfaceControl) uic, proof); + pse.execute((AbstractUserInterfaceControl) uic, renderedProof); } listener.taskStarted(new DefaultTaskStartedInfo(TaskStartedInfo.TaskKind.Other, "Running fallback macro on the remaining goals", 0)); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java index 969b3ea95b1..0de9931d6cc 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java @@ -146,7 +146,7 @@ protected static Goal getGoal(ImmutableList openGoals, Node node) { return null; } - public void setGoal(Goal g) { + public void setGoal(@Nullable Goal g) { goal = g; lastSetGoalNode = Optional.ofNullable(g).map(Goal::node).orElse(null); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java index 229fa298489..6215cc9e518 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java @@ -13,6 +13,7 @@ import java.util.stream.Collectors; import de.uka.ilkd.key.control.AbstractUserInterfaceControl; +import de.uka.ilkd.key.control.DefaultUserInterfaceControl; import de.uka.ilkd.key.nparser.KeYParser; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.nparser.ParsingFacade; @@ -31,44 +32,21 @@ * @author Alexander Weigl */ public class ProofScriptEngine { - private static final Map COMMANDS = loadCommands(); private static final Logger LOGGER = LoggerFactory.getLogger(ProofScriptEngine.class); - private final List script; - /** - * The initially selected goal. + * The collection of known script commands. */ - private final @Nullable Goal initiallySelectedGoal; + private static final Map COMMANDS = loadCommands(); /** * The engine state map. */ - private EngineState stateMap; - - private @Nullable Consumer commandMonitor; - - public ProofScriptEngine(Path file) throws IOException { - this(ParsingFacade.parseScript(file), null); - } - - public ProofScriptEngine(KeyAst.ProofScript script) { - this(script, null); - } - - /** - * Instantiates a new proof script engine. - * - * @param script the script - * @param initiallySelectedGoal the initially selected goal - */ - public ProofScriptEngine(KeyAst.ProofScript script, Goal initiallySelectedGoal) { - this(script.asAst(), initiallySelectedGoal); - } + private final EngineState stateMap; - public ProofScriptEngine(List script, Goal initiallySelectedGoal) { - this.initiallySelectedGoal = initiallySelectedGoal; - this.script = script; + public ProofScriptEngine(Proof proof) throws IOException { + super(); + this.stateMap = new EngineState(proof, this); } private static Map loadCommands() { @@ -84,49 +62,48 @@ private static Map loadCommands() { return result; } - public void execute(AbstractUserInterfaceControl uiControl, Proof proof) - throws IOException, InterruptedException, ScriptException { - stateMap = new EngineState(proof, this); + public void setInitiallySelectedGoal(@Nullable Goal initiallySelectedGoal) { + this.stateMap.setGoal(initiallySelectedGoal); + } - if (initiallySelectedGoal != null) { - stateMap.setGoal(initiallySelectedGoal); - } + public void execute(AbstractUserInterfaceControl uiControl, ScriptBlock block) + throws ScriptException, InterruptedException { + execute(uiControl, block.commands()); + } - if (script.isEmpty()) { // no commands given, no work to do - return; - } - // add the filename (if available) to the statemap. - try { - URI url = script.getFirst().location().fileUri(); - stateMap.setBaseFileName(Paths.get(url)); - } catch (NullPointerException | InvalidPathException ignored) { - // weigl: occurs on windows platforms, due to the fact - // that the URI contains "" from ANTLR4 when read by string - // "<" is illegal on windows - } - - // add the observer (if installed) to the state map - if (commandMonitor != null) { - stateMap.setObserver(commandMonitor); - } + public void execute(AbstractUserInterfaceControl uiControl, Path file) + throws ScriptException, InterruptedException, IOException { + KeyAst.ProofScript script = ParsingFacade.parseScript(file); execute(uiControl, script); } - public void execute(AbstractUserInterfaceControl uiControl, ScriptBlock block) + public void execute(AbstractUserInterfaceControl ui, KeyAst.ProofScript script) throws ScriptException, InterruptedException { - execute(uiControl, block.commands()); + execute(ui, script.asAst()); } public void execute(AbstractUserInterfaceControl uiControl, List commands) throws InterruptedException, ScriptException { - if (script.isEmpty()) { // no commands given, no work to do + if (commands.isEmpty()) { // no commands given, no work to do return; } - Location start = script.getFirst().location(); + Location start = commands.getFirst().location(); Proof proof = stateMap.getProof(); + // add the filename (if available) to the statemap. + try { + if(start != null) { + URI url = start.fileUri(); + stateMap.setBaseFileName(Paths.get(url)); + } + } catch (InvalidPathException ignored) { + // weigl: occurs on windows platforms, due to the fact + // that the URI contains "" from ANTLR4 when read by string + // "<" is illegal on windows + } + int cnt = 0; for (ScriptCommandAst ast : commands) { if (Thread.interrupted()) { @@ -138,8 +115,8 @@ public void execute(AbstractUserInterfaceControl uiControl, List monitor) { - this.commandMonitor = monitor; + this.stateMap.setObserver(monitor); } public static ProofScriptCommand getCommand(String commandName) { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommand.java index 66d1ec2b656..50a6eff3b2c 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommand.java @@ -38,9 +38,9 @@ public void execute(ScriptCommandAst ast) throws ScriptException, InterruptedExc LOGGER.info("Included script {}", file); try { - ProofScriptEngine pse = new ProofScriptEngine(file); + ProofScriptEngine pse = new ProofScriptEngine(proof); pse.setCommandMonitor(state().getObserver()); - pse.execute(uiControl, proof); + pse.execute(uiControl, file); } catch (NoSuchFileException e) { // The message is very cryptic otherwise. throw new ScriptException("Script file '" + file + "' not found", e); diff --git a/key.core/src/test/java/de/uka/ilkd/key/logic/TestLocalSymbols.java b/key.core/src/test/java/de/uka/ilkd/key/logic/TestLocalSymbols.java index b67ec93f1ab..fc8153f353f 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/logic/TestLocalSymbols.java +++ b/key.core/src/test/java/de/uka/ilkd/key/logic/TestLocalSymbols.java @@ -135,8 +135,8 @@ public void testDoubleInstantiation() throws Exception { Proof proof = env.getLoadedProof(); var script = env.getProofScript(); - ProofScriptEngine pse = new ProofScriptEngine(script); - pse.execute(null, proof); + ProofScriptEngine pse = new ProofScriptEngine(proof); + pse.execute(null, script); ImmutableList openGoals = proof.openGoals(); assert openGoals.size() == 1; diff --git a/key.core/src/test/java/de/uka/ilkd/key/proof/proverules/ProveRulesTest.java b/key.core/src/test/java/de/uka/ilkd/key/proof/proverules/ProveRulesTest.java index f46b1133ff9..c4266d159f9 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/proof/proverules/ProveRulesTest.java +++ b/key.core/src/test/java/de/uka/ilkd/key/proof/proverules/ProveRulesTest.java @@ -69,8 +69,8 @@ public void loadTacletProof(String tacletName, Taclet taclet, @Nullable Path pro KeyAst.ProofScript script = env.getProofScript(); if (script != null) { - ProofScriptEngine pse = new ProofScriptEngine(script); - pse.execute(env.getUi(), proof); + ProofScriptEngine pse = new ProofScriptEngine(proof); + pse.execute(env.getUi(), script); } assertTrue(proof.closed(), "Taclet proof of taclet " + tacletName + " did not close."); diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/FocusCommandTest.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/FocusCommandTest.java index 612d69ac541..8d2469c4efb 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/FocusCommandTest.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/FocusCommandTest.java @@ -32,8 +32,8 @@ public void testSimpleSelection() throws Exception { KeYEnvironment env = KeYEnvironment.load(temp); Proof p = env.getLoadedProof(); var script = ParsingFacade.parseScript("macro \"nosplit-prop\"; focus (i=1 ==> i = 4);"); - ProofScriptEngine pse = new ProofScriptEngine(script); - pse.execute(env.getUi(), p); + ProofScriptEngine pse = new ProofScriptEngine(p); + pse.execute(env.getUi(), script); assertEquals(1, p.openGoals().size()); Goal g = p.openGoals().head(); @@ -53,8 +53,8 @@ public void testSelectionWithLabels() throws Exception { KeYEnvironment env = KeYEnvironment.load(temp); Proof p = env.getLoadedProof(); var script = ParsingFacade.parseScript("macro \"nosplit-prop\"; focus (i=1 ==> i = 3);"); - ProofScriptEngine pse = new ProofScriptEngine(script); - pse.execute(env.getUi(), p); + ProofScriptEngine pse = new ProofScriptEngine(p); + pse.execute(env.getUi(), script); assertEquals(1, p.openGoals().size()); Goal g = p.openGoals().head(); diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java index bb5db7edbb6..103619375f5 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java @@ -46,8 +46,8 @@ public void testJmlScript(Path path) throws Exception { KeYEnvironment env = KeYEnvironment.load(projectFile); KeyAst.ProofScript script = env.getProofScript(); if (script != null) { - ProofScriptEngine pse = new ProofScriptEngine(script); - pse.execute(env.getUi(), env.getLoadedProof()); + ProofScriptEngine pse = new ProofScriptEngine(env.getLoadedProof()); + pse.execute(env.getUi(), script); } // TODO read comments from java file to allow for more than only closed proofs ... Assertions.assertTrue(env.getLoadedProof().closed(), "Proof did not close."); diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java index 34ae12c0c6a..b4724c50264 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java @@ -13,6 +13,7 @@ import de.uka.ilkd.key.control.DefaultUserInterfaceControl; import de.uka.ilkd.key.control.KeYEnvironment; +import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.nparser.ParsingFacade; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Proof; @@ -77,12 +78,12 @@ void testProofScript(TestInstance data) throws Exception { Proof proof = env.getLoadedProof(); - var script = ParsingFacade.parseScript(data.script()); - ProofScriptEngine pse = new ProofScriptEngine(script); + KeyAst.ProofScript script = ParsingFacade.parseScript(data.script()); + ProofScriptEngine pse = new ProofScriptEngine(proof); boolean hasException = data.exception() != null; try { - pse.execute(env.getUi(), proof); + pse.execute(env.getUi(), script); } catch (ScriptException ex) { assertTrue(data.exception != null && !data.exception.isEmpty(), "An exception was not expected, but got " + ex.getMessage()); diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/RewriteTest.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/RewriteTest.java index ed7c54baaed..62b2991694b 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/RewriteTest.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/RewriteTest.java @@ -42,8 +42,8 @@ public void testTransitive() assertNotNull(env); Proof p = env.getLoadedProof(); - ProofScriptEngine engine = new ProofScriptEngine(script); - engine.execute(env.getUi(), p); + ProofScriptEngine engine = new ProofScriptEngine(p); + engine.execute(env.getUi(), script); String firstOpenGoal = p.openGoals().head().sequent().toString(); String expectedSequent = "[equals(x,f),equals(x,z)]==>[equals(z,f)]"; @@ -69,8 +69,8 @@ public void testLessTransitive() KeYEnvironment env = KeYEnvironment.load(keyFile); Proof proof = env.getLoadedProof(); - ProofScriptEngine engine = new ProofScriptEngine(script); - engine.execute(env.getUi(), proof); + ProofScriptEngine engine = new ProofScriptEngine(proof); + engine.execute(env.getUi(), script); String firstOpenGoal = proof.openGoals().head().sequent().toString(); String expectedSequent = "[]==>[imp(and(gt(x,f),lt(x,z)),lt(f,z))]"; diff --git a/key.core/src/testFixtures/java/de/uka/ilkd/key/proof/runallproofs/ProveTest.java b/key.core/src/testFixtures/java/de/uka/ilkd/key/proof/runallproofs/ProveTest.java index a1216597ec4..0f343a7d020 100644 --- a/key.core/src/testFixtures/java/de/uka/ilkd/key/proof/runallproofs/ProveTest.java +++ b/key.core/src/testFixtures/java/de/uka/ilkd/key/proof/runallproofs/ProveTest.java @@ -225,8 +225,8 @@ private void autoMode(KeYEnvironment env, Proof loa env.getProofControl().startAndWaitForAutoMode(loadedProof); } else { // ... script - ProofScriptEngine pse = new ProofScriptEngine(script); - pse.execute(env.getUi(), env.getLoadedProof()); + ProofScriptEngine pse = new ProofScriptEngine(env.getLoadedProof()); + pse.execute(env.getUi(), script); } } diff --git a/key.core/src/testFixtures/java/de/uka/ilkd/key/proof/runallproofs/proofcollection/TestFile.java b/key.core/src/testFixtures/java/de/uka/ilkd/key/proof/runallproofs/proofcollection/TestFile.java index ded0ef25dff..fa70826f758 100644 --- a/key.core/src/testFixtures/java/de/uka/ilkd/key/proof/runallproofs/proofcollection/TestFile.java +++ b/key.core/src/testFixtures/java/de/uka/ilkd/key/proof/runallproofs/proofcollection/TestFile.java @@ -275,8 +275,8 @@ protected void autoMode(KeYEnvironment env, Proof l env.getProofControl().startAndWaitForAutoMode(loadedProof); } else { // ... script - ProofScriptEngine pse = new ProofScriptEngine(script); - pse.execute(env.getUi(), env.getLoadedProof()); + ProofScriptEngine pse = new ProofScriptEngine(env.getLoadedProof()); + pse.execute(env.getUi(), script); } } diff --git a/key.ui/src/main/java/de/uka/ilkd/key/gui/ProofScriptWorker.java b/key.ui/src/main/java/de/uka/ilkd/key/gui/ProofScriptWorker.java index ccb6b52727a..14cc9adcda5 100644 --- a/key.ui/src/main/java/de/uka/ilkd/key/gui/ProofScriptWorker.java +++ b/key.ui/src/main/java/de/uka/ilkd/key/gui/ProofScriptWorker.java @@ -5,6 +5,7 @@ import java.awt.*; import java.awt.Dialog.ModalityType; +import java.io.IOException; import java.net.URI; import java.util.List; import java.util.concurrent.CancellationException; @@ -75,14 +76,15 @@ public ProofScriptWorker(KeYMediator mediator, KeyAst.ProofScript script, this.mediator = mediator; this.script = script; this.initiallySelectedGoal = initiallySelectedGoal; - engine = new ProofScriptEngine(script, initiallySelectedGoal); + engine = new ProofScriptEngine(initiallySelectedGoal.proof()); + engine.setInitiallySelectedGoal(initiallySelectedGoal); } @Override protected @Nullable Object doInBackground() throws Exception { try { engine.setCommandMonitor(observer); - engine.execute(mediator.getUI(), mediator.getSelectedProof()); + engine.execute(mediator.getUI(), script); } catch (InterruptedException ex) { LOGGER.debug("Proof macro has been interrupted:", ex); } diff --git a/key.ui/src/main/java/de/uka/ilkd/key/ui/ConsoleUserInterfaceControl.java b/key.ui/src/main/java/de/uka/ilkd/key/ui/ConsoleUserInterfaceControl.java index 4b33d65c79b..64554e31174 100644 --- a/key.ui/src/main/java/de/uka/ilkd/key/ui/ConsoleUserInterfaceControl.java +++ b/key.ui/src/main/java/de/uka/ilkd/key/ui/ConsoleUserInterfaceControl.java @@ -166,10 +166,10 @@ public void taskFinished(TaskFinishedInfo info) { var script = problemLoader.getProofScript(); if (script != null) { ProofScriptEngine pse = - new ProofScriptEngine(script); + new ProofScriptEngine(proof); this.taskStarted( new DefaultTaskStartedInfo(TaskKind.Macro, "Script started", 0)); - pse.execute(this, proof); + pse.execute(this, script); // The start and end messages are fake to persuade the system ... // All this here should refactored anyway ... this.taskFinished(new ProofMacroFinishedInfo(new SkipMacro(), proof)); From fb770b9abf9adaef78d88d3f463b99dec73b1887 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Wed, 1 Oct 2025 21:11:09 +0200 Subject: [PATCH 39/90] consolidating, introducing test cases for jml scripts --- .../ilkd/key/macros/ApplyScriptsMacro.java | 3 -- .../java/de/uka/ilkd/key/nparser/KeyAst.java | 4 +- .../de/uka/ilkd/key/scripts/CheatCommand.java | 4 +- .../ilkd/key/scripts/ProofScriptEngine.java | 2 +- ...de.uka.ilkd.key.scripts.ProofScriptCommand | 3 +- .../uka/ilkd/key/scripts/JmlScriptTest.java | 50 ++++++++++++++++--- .../ilkd/key/scripts/jml/NestedAssert.java | 11 ++++ .../de/uka/ilkd/key/scripts/jml/Obtain1.java | 9 ++-- 8 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/NestedAssert.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 6557a92ea0f..1c7cfac878c 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -209,9 +209,6 @@ private Map makeObtainVarMap(ImmutableList renderProof(KeyAst.JMLProofScript script, Map termMap, JTerm update, Services services) throws ScriptException { List result = new ArrayList<>(); - // Do not fail on open proofs - // TODO Migrate into SetCommand - result.add(new ScriptCommandAst("failonopen", Map.of(), List.of("off"))); // Push current settings onto the settings stack result.add(new ScriptCommandAst("set", Map.of("stack", "push"), List.of())); for (ProofCmdContext proofCmdContext : script.ctx.proofCmd()) { diff --git a/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java b/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java index bc4085cac43..368557ab0f5 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java +++ b/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java @@ -222,8 +222,6 @@ public Expression(JmlParser.@NonNull ExpressionContext ctx) { public static class JMLProofScript extends KeyAst { private static class ObtainedVarsVisitor extends JmlParserBaseVisitor { - /// To make debugging easier, obtained variables have a special (hopefully but not necessarily unique) prefix. - public static final String OBTAIN_PREFIX = "_obtained_"; private ImmutableList collectedVars = ImmutableList.of(); private final JmlIO io; @@ -235,7 +233,7 @@ private ObtainedVarsVisitor(JmlIO io) { public Void visitProofCmd(JmlParser.ProofCmdContext ctx) { if(ctx.obtain != null) { KeYJavaType type = io.translateType(ctx.typespec()); - ProgramElementName name = new ProgramElementName(OBTAIN_PREFIX + ctx.var.getText()); + ProgramElementName name = new ProgramElementName(ctx.var.getText()); collectedVars = collectedVars.prepend(new LocationVariable(name, type, true)); } return null; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java index 8805210e2c9..65b697d1219 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java @@ -25,8 +25,8 @@ public class CheatCommand extends NoArgumentCommand { static { TacletApplPart applPart = new TacletApplPart(JavaDLSequentKit.getInstance().getEmptySequent(), - ApplicationRestriction.NONE, ImmutableList.of(), ImmutableList.of(), - ImmutableList.of(), ImmutableList.of()); + new ApplicationRestriction(ApplicationRestriction.IN_SEQUENT_STATE), ImmutableList.of(), + ImmutableList.of(), ImmutableList.of(), ImmutableList.of()); CHEAT_TACLET = new NoFindTaclet(new Name("CHEAT"), applPart, ImmutableList.of(), ImmutableList.of(), new TacletAttributes("cheat", null), DefaultImmutableMap.nilMap(), ChoiceExpr.TRUE, diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java index 6215cc9e518..4259296d086 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java @@ -44,7 +44,7 @@ public class ProofScriptEngine { */ private final EngineState stateMap; - public ProofScriptEngine(Proof proof) throws IOException { + public ProofScriptEngine(Proof proof) { super(); this.stateMap = new EngineState(proof, this); } diff --git a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand index bd47db5194b..1dca8527c92 100644 --- a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand +++ b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand @@ -37,4 +37,5 @@ de.uka.ilkd.key.scripts.RewriteCommand de.uka.ilkd.key.scripts.AllCommand de.uka.ilkd.key.scripts.HideCommand de.uka.ilkd.key.scripts.UnhideCommand -de.uka.ilkd.key.scripts.BranchesCommand \ No newline at end of file +de.uka.ilkd.key.scripts.BranchesCommand +de.uka.ilkd.key.scripts.CheatCommand \ No newline at end of file diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java index 103619375f5..0e93d9bb60e 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java @@ -1,5 +1,7 @@ package de.uka.ilkd.key.scripts; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import de.uka.ilkd.key.control.DefaultUserInterfaceControl; import de.uka.ilkd.key.control.KeYEnvironment; import de.uka.ilkd.key.control.UserInterfaceControl; @@ -10,6 +12,9 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import recoder.util.Debug; import java.io.File; import java.io.IOException; @@ -18,13 +23,16 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Collections; import java.util.Comparator; +import java.util.logging.LogManager; +import java.util.stream.Collectors; import java.util.stream.Stream; public class JmlScriptTest { private static final Path KEY_FILE; + private static final Logger LOGGER = LoggerFactory.getLogger(JmlScriptTest.class); + static { URL url = JmlScriptTest.class.getResource("jml/project.key"); try { @@ -34,9 +42,11 @@ public class JmlScriptTest { } } - @ParameterizedTest(name = "{0}") + @ParameterizedTest(name = "{1}") @MethodSource("filesProvider") - public void testJmlScript(Path path) throws Exception { + public void testJmlScript(Path path, String identifier) throws Exception { + + Parameters params = readParams(path); Path tmpDir = Files.createTempDirectory("key.jmltest."); try { @@ -49,22 +59,48 @@ public void testJmlScript(Path path) throws Exception { ProofScriptEngine pse = new ProofScriptEngine(env.getLoadedProof()); pse.execute(env.getUi(), script); } - // TODO read comments from java file to allow for more than only closed proofs ... - Assertions.assertTrue(env.getLoadedProof().closed(), "Proof did not close."); + + if(params.shouldClose) { + Assertions.assertTrue(env.getLoadedProof().closed(), "Proof did not close."); + } else { + Assertions.assertFalse(env.getLoadedProof().closed(), "Proof closes unexpectedly."); + } } finally { // Uncomment the following line to delete the temporary directory after the test - // Files.walk(tmpDir).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); + if(params.deleteTmpDir) { + LOGGER.info("Deleting temporary directory: {}", tmpDir); + Files.walk(tmpDir).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); + } else { + LOGGER.info("Temporary directory retained for inspection: {}", tmpDir); + } } } + private static Parameters readParams(Path path) throws IOException { + String input = Files.lines(path).filter(l -> l.startsWith("//!")).map(l -> l.substring(3).trim()) + .collect(Collectors.joining("\n")).trim(); + if(input.isEmpty()) { + return new Parameters(); + } + var objectMapper = new ObjectMapper(new YAMLFactory()); + objectMapper.findAndRegisterModules(); + return objectMapper.readValue(input, Parameters.class); + } + public static Stream filesProvider() throws URISyntaxException, IOException { URL jmlUrl = JmlScriptTest.class.getResource("jml"); return Files.list(Paths.get(jmlUrl.toURI())) .filter(p -> p.toString().endsWith(".java")) - .map(p -> Arguments.of(p)); + .map(p -> Arguments.of(p, p.getFileName().toString())); } + static class Parameters { + public boolean shouldClose = true; + public String method; + public String exception; + public boolean deleteTmpDir = true; + } } diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/NestedAssert.java b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/NestedAssert.java new file mode 100644 index 00000000000..76a6686eddd --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/NestedAssert.java @@ -0,0 +1,11 @@ +class Test { + //@ ensures true; + void test() { + int x; + /*@ assert x > 2 \by { + assert x == 7 \by { cheat; } + auto; // should not be necessary ... eventually removed + } */ + } + +} diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/Obtain1.java b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/Obtain1.java index 34814fd588d..aeb6f6b356d 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/Obtain1.java +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/Obtain1.java @@ -1,11 +1,14 @@ +//! deleteTmpDir : false + class Test { //@ ensures true; void test() { int x = 42; /*@ assert x == 42 \by { obtain int y = 41; - assert x+1 == 42; - } */ + assert y+1 == 42; + auto; + } */ } -} \ No newline at end of file +} From 5dcb7797dd338ff92f6e9aec33a7e00ac2b67231 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Wed, 1 Oct 2025 23:51:36 +0200 Subject: [PATCH 40/90] first working obtain scripts --- .../ilkd/key/macros/ApplyScriptsMacro.java | 26 +-- .../uka/ilkd/key/scripts/ObtainCommand.java | 188 ++++++++++++++++++ .../ilkd/key/scripts/meta/ValueInjector.java | 2 +- ...de.uka.ilkd.key.scripts.ProofScriptCommand | 1 + .../rules/classicalLogic/firstOrderRules.key | 5 + .../uka/ilkd/key/scripts/JmlScriptTest.java | 13 +- .../de/uka/ilkd/key/scripts/jml/Obtain1.java | 3 +- .../ilkd/key/scripts/jml/ObtainFromGoal.java | 18 ++ 8 files changed, 239 insertions(+), 17 deletions(-) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/scripts/ObtainCommand.java create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/ObtainFromGoal.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 1c7cfac878c..9bd55b96f3e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -15,14 +15,12 @@ import de.uka.ilkd.key.java.statement.JmlAssert; import de.uka.ilkd.key.logic.DefaultVisitor; import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.logic.op.JFunction; import de.uka.ilkd.key.logic.op.LocationVariable; import de.uka.ilkd.key.logic.op.UpdateApplication; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.parser.Location; -import de.uka.ilkd.key.proof.Goal; -import de.uka.ilkd.key.proof.Node; -import de.uka.ilkd.key.proof.ProgVarReplacer; -import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.proof.*; import de.uka.ilkd.key.proof.mgt.SpecificationRepository; import de.uka.ilkd.key.prover.impl.DefaultTaskStartedInfo; import de.uka.ilkd.key.rule.JmlAssertBuiltInRuleApp; @@ -36,6 +34,7 @@ import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdContext; import org.key_project.logic.Term; +import org.key_project.logic.op.SortedOperator; import org.key_project.prover.engine.ProverTaskListener; import org.key_project.prover.engine.TaskStartedInfo; import org.key_project.prover.rules.RuleApp; @@ -82,14 +81,14 @@ public boolean canApplyTo(Proof proof, ImmutableList<@NonNull Goal> goals, } record ObtainAwareTerm(JTerm term) { - JTerm resolve(Map obtainMap, Services services) { - ProgVarReplacer pvr = new ProgVarReplacer(obtainMap, services); + JTerm resolve(Map obtainMap, Services services) { + OpReplacer pvr = new OpReplacer(obtainMap, services.getTermFactory()); JTerm result = pvr.replace(term); assertNoObtainVarsLeft(result, obtainMap); return result; } - private void assertNoObtainVarsLeft(JTerm term, Map obtainMap) { + private void assertNoObtainVarsLeft(JTerm term, Map obtainMap) { var v = new DefaultVisitor() { @Override public void visit(Term visited) { @@ -149,7 +148,7 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, KeyAst.JMLProofScript proofScript = jmlAssert.getAssertionProof(); Map termMap = getTermMap(jmlAssert, proof.getServices()); // We heavily rely on that variables have been computed before, otherwise this will raise an NPE. - Map obtainMap = makeObtainVarMap(jmlAssert.collectVariablesInProof(null)); + Map obtainMap = makeObtainVarMap(jmlAssert.collectVariablesInProof(null)); JTerm update = getUpdate(goal); List renderedProof = renderProof(proofScript, termMap, update, proof.getServices()); @@ -198,8 +197,8 @@ private Map getTermMap(JmlAssert jmlAssert, Services s return result; } - private Map makeObtainVarMap(ImmutableList locationVariables) { - HashMap result = new HashMap<>(); + private Map makeObtainVarMap(ImmutableList locationVariables) { + HashMap result = new HashMap<>(); for (LocationVariable lv : locationVariables) { result.put(lv, null); } @@ -272,6 +271,8 @@ private static ScriptCommandAst renderObtainCommand(ProofCmdContext ctx, Map throw new ScriptException("Unknown obtain kind: " + ctx.obtKind.getText()); }; + named.put("var", ctx.var.getText()); + if(ctx.expression() == null) { named.put(argName, true); } else { @@ -307,10 +308,11 @@ private static ScriptCommandAst renderObtainCommand(ProofCmdContext ctx, Map + *

  • #2 = rule name
  • + *
  • on= key.core.logic.Term on which the rule should be applied to as String (find part of the + * rule)
  • + *
  • formula= toplevel formula in which term appears in
  • + *
  • occ = occurrence number
  • + *
  • inst_= instantiation
  • + * + */ +public class ObtainCommand extends AbstractCommand { + + private static final Name INTRO_TACLET_NAME = new Name("intro"); + private static final Name ALL_RIGHT_TACLET_NAME = new Name("allRight"); + + public ObtainCommand() { + super(Parameters.class); + } + + @Override + public String getName() { + return "__obtain"; + } + + @Override + public void execute(ScriptCommandAst ast) + throws ScriptException, InterruptedException { + var args = state().getValueInjector().inject(new Parameters(), ast); + + var obtainMap = (Map)state().getUserData("jml.obtainVarMap"); + if(obtainMap == null) { + throw new ScriptException("No obtain variable map found. This command must be used within a JML proof."); + } + + LocationVariable var = obtainMap.keySet().stream() + .filter(lv -> lv.name().toString().equals(args.var)) + .findAny().orElseThrow(() -> new ScriptException("No such obtain variable registered: " + args.var)); + + JTerm skolem; + if (args.equals != null) { + skolem = executeEquals(var, args.equals); + } else if (args.suchThat != null) { + skolem = executeSuchThat(var, args.suchThat); + } else if (args.fromGoal) { + skolem = executeFromGoal(var); + } else { + throw new ScriptException("Exactly one of 'such_that', 'equals', or 'from_goal' must be given."); + } + + obtainMap.put(var, skolem.op(JFunction.class)); + + } + + private JTerm executeFromGoal(LocationVariable var) throws ScriptException { + Goal goal = state.getFirstOpenAutomaticGoal(); + + // This works under the assumption that the first succedent formula is the "goal" formula. + SequentFormula sequentFormula = identifySequentFormula(goal.node()); + JTerm formula = (JTerm) sequentFormula.formula(); + while(formula.op() instanceof UpdateApplication) { + formula = formula.sub(1); + } + if(formula.op() != Quantifier.ALL) { + throw new ScriptException("For 'obtain \\from_goal, the goal formula needs to be a universally quantified formula."); + } + + Services services = state().getProof().getServices(); + Taclet intro = state.getProof().getEnv().getInitConfigForEnvironment() + .lookupActiveTaclet(ALL_RIGHT_TACLET_NAME); + TacletApp app = NoPosTacletApp.createNoPosTacletApp(intro); + + SchemaVariable sk = getSV(app.uninstantiatedVars(), "sk"); + String name = VariableNameProposer.DEFAULT.getNameProposal(var.name().toString(), services, null); + app = app.createSkolemConstant(name, sk, var.sort(), true, services); + + SchemaVariable b = getSV(app.uninstantiatedVars(), "b"); + app = app.addCheckedInstantiation(b, formula.sub(0), services, true); + + SchemaVariable u = getSV(app.uninstantiatedVars(), "u"); + app = app.addCheckedInstantiation(u, services.getTermBuilder().var((LogicVariable) formula.boundVars().get(0)), services, true); + app = app.setPosInOccurrence(new PosInOccurrence(sequentFormula, PosInTerm.getTopLevel(), false), services); + + goal.apply(app); + return app.instantiations().getInstantiation(sk); + } + + private SequentFormula identifySequentFormula(Node node) { + SemisequentChangeInfo changes = node.getNodeInfo().getSequentChangeInfo().getSemisequentChangeInfo(false); + ImmutableList added = changes.addedFormulas(); + if(!added.isEmpty()) { + if(added.size() == 1) { + return added.get(0); + } + } else { + ImmutableList modified = changes.modifiedFormulas(); + if(modified.size() == 1) { + return modified.get(0).newFormula(); + } + } + throw new IllegalStateException("Multiple or no formulas modified or added in last step, cannot identify sequent formula to skolemize."); + } + + private JTerm executeSuchThat(LocationVariable var, @Nullable JTerm suchThat) { + throw new UnsupportedOperationException("such_that not yet supported in obtain."); + } + + private JTerm executeEquals(LocationVariable var, @Nullable JTerm equals) throws ScriptException { + Services services = state().getProof().getServices(); + Taclet intro = state.getProof().getEnv().getInitConfigForEnvironment() + .lookupActiveTaclet(INTRO_TACLET_NAME); + TacletApp app = NoPosTacletApp.createNoPosTacletApp(intro); + SchemaVariable sk = getSV(app.uninstantiatedVars(), "sk"); + SchemaVariable t = getSV(app.uninstantiatedVars(), "t"); + String name = VariableNameProposer.DEFAULT.getNameProposal(var.name().toString(), services, null); + app = app.createSkolemConstant(name, sk, var.sort(), true, services); + app = app.addCheckedInstantiation(t, equals, services, true); + state.getFirstOpenAutomaticGoal().apply(app); + return app.instantiations().getInstantiation(sk); + } + + private static SchemaVariable getSV(ImmutableSet schemaVars, String name) { + for (SchemaVariable schemaVar : schemaVars) { + if(schemaVar.name().toString().equals(name)) { + return schemaVar; + } + } + throw new NoSuchElementException("No schema variable with name " + name); + } + + @Documentation("TODO.") + public static class Parameters implements ValueInjector.VerifyableParameters { + @Option(value = "var") + @Documentation("Name of the instantiated variable.") + public @MonotonicNonNull String var; + + @Option(value = "such_that") + @Documentation("Condition that is to be established for the fresh variable.") + public @Nullable JTerm suchThat; + + @Option(value = "from_goal") + @Documentation("Top-level formula in which the term appears.") + public @Nullable boolean fromGoal = false; + + @Option(value = "equals") + @Documentation("Represented term for which this is an abbreviation.") + public @Nullable JTerm equals; + + @Override + public void verifyParameters() throws IllegalArgumentException, InjectionException { + int cnt = 0; + if(suchThat != null) cnt++; + if(equals != null) cnt++; + if(fromGoal) cnt++; + if(cnt != 1) { + throw new InjectionException("Exactly one of 'such_that', 'equals', or 'from_goal' must be given."); + } + } + } + +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java index a875ce6c731..8300997d66e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java @@ -48,7 +48,7 @@ private record ConverterKey( Class source, Class target) { } - interface VerifyableParameters { + public interface VerifyableParameters { void verifyParameters() throws IllegalArgumentException, InjectionException; } diff --git a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand index 1dca8527c92..7799535bc13 100644 --- a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand +++ b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand @@ -7,6 +7,7 @@ de.uka.ilkd.key.scripts.EchoCommand de.uka.ilkd.key.scripts.FocusCommand de.uka.ilkd.key.scripts.AutoCommand de.uka.ilkd.key.scripts.CutCommand +de.uka.ilkd.key.scripts.ObtainCommand # de.uka.ilkd.key.scripts.AssertCommand # it is an alias for CutCommand now de.uka.ilkd.key.scripts.SetCommand de.uka.ilkd.key.scripts.SetEchoCommand diff --git a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/classicalLogic/firstOrderRules.key b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/classicalLogic/firstOrderRules.key index ef14af1896f..599a957014f 100644 --- a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/classicalLogic/firstOrderRules.key +++ b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/classicalLogic/firstOrderRules.key @@ -215,6 +215,11 @@ \heuristics(semantics_blasting) }; + intro { + \varcond(\newDependingOn(sk, t)) + \add(t = sk ==>) + }; + \lemma eqTermCut { \find(t) diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java index 0e93d9bb60e..9dc7b9cbab3 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java @@ -33,6 +33,9 @@ public class JmlScriptTest { private static final Path KEY_FILE; private static final Logger LOGGER = LoggerFactory.getLogger(JmlScriptTest.class); + // Set this to a specific case to only run that case for debugging + private static final String ONLY_CASE = null; + static { URL url = JmlScriptTest.class.getResource("jml/project.key"); try { @@ -90,9 +93,13 @@ private static Parameters readParams(Path path) throws IOException { public static Stream filesProvider() throws URISyntaxException, IOException { URL jmlUrl = JmlScriptTest.class.getResource("jml"); - return Files.list(Paths.get(jmlUrl.toURI())) - .filter(p -> p.toString().endsWith(".java")) - .map(p -> Arguments.of(p, p.getFileName().toString())); + if (ONLY_CASE != null) { + return Stream.of(Arguments.of(Paths.get(jmlUrl.toURI()).resolve(ONLY_CASE), "single specified case")); + } else { + return Files.list(Paths.get(jmlUrl.toURI())) + .filter(p -> p.toString().endsWith(".java")) + .map(p -> Arguments.of(p, p.getFileName().toString())); + } } static class Parameters { diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/Obtain1.java b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/Obtain1.java index aeb6f6b356d..92bd352ddf2 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/Obtain1.java +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/Obtain1.java @@ -6,8 +6,9 @@ void test() { int x = 42; /*@ assert x == 42 \by { obtain int y = 41; - assert y+1 == 42; + assert y+1 == 42 \by auto; auto; + // Still too verbose on auto } */ } diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/ObtainFromGoal.java b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/ObtainFromGoal.java new file mode 100644 index 00000000000..5bd1ba3b553 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/ObtainFromGoal.java @@ -0,0 +1,18 @@ +//! deleteTmpDir : false + +class Test { + + //@ static model int f(int arg); + + //@ ensures true; + void test() { + int x = 42; + /*@ assert (\forall int x; f(x) > 40) \by { + obtain int y \from_goal; + assert f(y) == 42 \by cheat; + auto; + // Still too verbose on auto + } */ + } + +} From 6ca05d6a00cdc83567119a4d13975a85f56fbc04 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Thu, 2 Oct 2025 18:44:51 +0200 Subject: [PATCH 41/90] pimping the document generation for proof script commands --- .../uka/ilkd/key/scripts/AbstractCommand.java | 4 + .../de/uka/ilkd/key/scripts/AutoCommand.java | 35 ++-- .../de/uka/ilkd/key/scripts/CutCommand.java | 17 +- .../de/uka/ilkd/key/scripts/EchoCommand.java | 9 +- .../de/uka/ilkd/key/scripts/FocusCommand.java | 17 ++ .../ilkd/key/scripts/InstantiateCommand.java | 34 ++-- .../de/uka/ilkd/key/scripts/LeaveCommand.java | 4 + .../uka/ilkd/key/scripts/ObtainCommand.java | 10 +- .../key/scripts/OneStepSimplifierCommand.java | 11 ++ .../ilkd/key/scripts/ProofScriptCommand.java | 23 ++- .../ilkd/key/scripts/ProofScriptEngine.java | 2 +- .../de/uka/ilkd/key/scripts/RuleCommand.java | 24 ++- .../de/uka/ilkd/key/scripts/SMTCommand.java | 16 ++ .../uka/ilkd/key/scripts/SelectCommand.java | 41 ++++- .../de/uka/ilkd/key/scripts/SkipCommand.java | 7 +- .../key/scripts/meta/ArgumentsLifter.java | 154 +++++++++++------- .../ilkd/key/scripts/meta/Documentation.java | 1 + .../key/scripts/meta/ProofScriptArgument.java | 13 ++ .../key/scripts/DocumentationGenerator.java | 104 ++++++++++++ 19 files changed, 405 insertions(+), 121 deletions(-) create mode 100644 key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AbstractCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AbstractCommand.java index 55fe6f0777e..dd622d79b27 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AbstractCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AbstractCommand.java @@ -104,4 +104,8 @@ public String getDocumentation() { return Objects.requireNonNullElse(documentation, ""); } + @Override + public String getCategory() { + return ArgumentsLifter.extractCategory(getClass(), parameterClazz); + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java index 9dfa98d7358..8cd83fa1f52 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java @@ -161,9 +161,10 @@ private void setupFocussedBreakpointStrategy(final String maybeMatchesRegEx, new AbstractProofControl.FocussedAutoModeTaskListener(services.getProof())); } - @Documentation(""" - The AutoCommand is a command that invokes the automatic strategy "Auto" of KeY. - It can be used to automatically prove a goal or a set of goals. + @Documentation(category = "Fundamental", value =""" + The AutoCommand invokes the automatic strategy "Auto" of KeY (which is also launched by + when clicking the "Auto" button in the GUI). + It can be used to try to automatically prove the current goal. Use with care, as this command may leave the proof in a incomprehensible state with many open goals. @@ -172,46 +173,48 @@ private void setupFocussedBreakpointStrategy(final String maybeMatchesRegEx, public static class Parameters { // @ TODO Deprecated with the higher order proof commands? @Flag(value = "all") - @Documentation("Apply the strategy on all open goals. There is a better syntax for that now.") + @Documentation("*Deprecated*. Apply the strategy on all open goals. There is a better syntax for that now.") public boolean onAllOpenGoals = false; @Option(value = "steps") - @Documentation("The maximum number of steps to be performed.") - public int maxSteps = -1; + @Documentation("The maximum number of proof steps to be performed.") + public @Nullable int maxSteps = -1; /** * Run on formula matching the given regex */ @Option(value = "matches") - @Documentation("Run on formula matching the given regex.") + @Documentation("Run on the formula matching the given regex.") public @Nullable String matches = null; /** * Run on formula matching the given regex */ @Option(value = "breakpoint") - @Documentation("Run on formula matching the given regex.") + @Documentation("When doing symbolic execution by auto, this option can be used to set a Java statement at which " + + "symbolic execution has to stop.") public @Nullable String breakpoint = null; @Flag(value = "modelsearch") - @Documentation("Enable model search. Better for some types of arithmetic problems. Sometimes a lot worse") - public @Nullable Boolean modelSearch; + @Documentation("Enable model search. Better for some (types of) arithmetic problems. Sometimes a lot worse.") + public boolean modelSearch; @Flag(value = "expandQueries") - @Documentation("Expand queries by modalities.") - public @Nullable Boolean expandQueries; + @Documentation("Automatically expand occurrences of query symbols using additional modalities on the sequent.") + public boolean expandQueries; @Flag(value = "classAxioms") @Documentation(""" - Enable class axioms. This expands model methods and fields and invariants quite eagerly. \ - May lead to divergence.""") - public @Nullable Boolean classAxioms; + Enable automatic and eager expansion of symbols. This expands class invariants, model methods and + fields and invariants quite eagerly. May be an enabler (if a few definitions need to expanded), + may be a showstopper (if expansion increases the complexity on the sequent too much).""") + public boolean classAxioms; @Flag(value = "dependencies") @Documentation(""" Enable dependency reasoning. In modular reasoning, the value of symbols may stay the same, \ without that its definition is known. May be an enabler, may be a showstopper.""") - public @Nullable Boolean dependencies; + public boolean dependencies; } private static final class OriginalValue { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java index 465c90e41a9..7ef35b44fe7 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java @@ -11,6 +11,7 @@ import de.uka.ilkd.key.rule.TacletApp; import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Documentation; import org.key_project.logic.Name; import org.key_project.logic.op.sv.SchemaVariable; @@ -38,15 +39,6 @@ public List getAliases() { return List.of(getName(), "assert"); } - @Override - public String getDocumentation() { - return """ - CutCommand has as script command name "cut" - - As parameters: - * a formula with the id "#2"""; - } - @Override public void execute(ScriptCommandAst arguments) throws ScriptException, InterruptedException { var args = state().getValueInjector().inject(new Parameters(), arguments); @@ -64,8 +56,15 @@ static void execute(EngineState state, Parameters args) throws ScriptException { state.getFirstOpenAutomaticGoal().apply(app); } + @Documentation(category = "Fundamental", value = """ + The cut command makes a case distinction (a cut) on a formula on the current proof goal. + From within JML scripts, the alias 'assert' is more common than using 'cut'. + If followed by a `\\by proof` suffix in JML, it refers the sequent where + the cut formula is introduced to the succedent (i.e. where it is to be established). + """) public static class Parameters { @Argument + @Documentation("The formula to make the case distinction on.") public @MonotonicNonNull JTerm formula; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/EchoCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/EchoCommand.java index 306a7d43c32..1095212d468 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/EchoCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/EchoCommand.java @@ -6,6 +6,7 @@ import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Documentation; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** @@ -32,11 +33,13 @@ public void execute(ScriptCommandAst args) } } + @Documentation(category = "Control", value = """ + A simple "print" command for giving progress feedback to the + human verfier during lengthy executions. + """) public static class Parameters { - /** - * The message to show. - */ @Argument + @Documentation("The message to be printed.") public @MonotonicNonNull String message; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java index 6f9b1f724a2..df279f4afea 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java @@ -15,6 +15,7 @@ import de.uka.ilkd.key.rule.inst.SVInstantiations; import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Documentation; import org.key_project.logic.Name; import org.key_project.logic.PosInTerm; import org.key_project.logic.Term; @@ -48,8 +49,24 @@ public FocusCommand() { super(Parameters.class); } + @Documentation(category = "Fundamental", value = """ + The command "focus" allows you to select formulas from the current sequent + to focus verification on. This means that all other formulas are discarded + (i.e. hidden using `hide_right`, `hide_left`). + + Benefits are: The automation is guided into focussing on a relevant set of + formulas. + + The selected set of sequent formulas can be regarded as an equivalent to a + believed "unsat core" of the sequent. + + #### Examples: + - `focus x > 2 ==> x > 1` only keeps the mentioned to formulas in the current goal + removing all other formulas that could distract the automation. + """) static class Parameters { @Argument + @Documentation("The sequent containing the formulas to keep. It may contain placeholder symbols.") public @MonotonicNonNull Sequent toKeep; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java index 6dd0c2e7f96..45424416ce6 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java @@ -222,34 +222,36 @@ public String getName() { return "instantiate"; } - @Override - public String getDocumentation() { - return """ - instantiate var=a occ=2 with="a_8" hide -

    - instantiate formula="\\forall int a; phi(a)" with="a_8\" - """; - } - - @Documentation("Instantiate a universally quantified formula (or an existentially quantified formula in succedent) by a term." - + "One of 'var' or 'formula' must be specified. If 'var' is given, the formula is determined by looking for a particular occurrence of a quantifier over that variable name.\n" - + "'with' must be specified.") + @Documentation(category = "Fundamental", value = """ + Instantiate a universally quantified formula (in the antecedent; + or an existentially quantified formula in succedent) by a term. + One of `var` or `formula` must be specified. If `var` is given, the formula is determined by looking for + a particular occurrence of a quantifier over that variable name. + If `formula` is given, that quantified formula is used directly. + `with` must be specified. + + #### Examples: + + * `instantiate var:a occ:2 with:a_8 hide` + * `instantiate formula:"\\forall int a; phi(a)" with="a_8"` + """) public static class Parameters { - @Documentation("The toplevel quantified formula to instantiate. Either this or 'var' must be given.") + @Documentation("The toplevel quantified formula to instantiate. Placeholder matching symbols can be used.") @Option(value = "formula") @Nullable public JTerm formula; - @Documentation("The name of the bound variable to instantiate. Either this or 'formula' must be given.") + @Documentation("The name of the bound variable to instantiate.") @Option(value = "var") @Nullable public String var; - @Documentation("The occurrence number of the quantifier over 'var' in the sequent. Default is 1 (the first).") + @Documentation("The occurrence number of the quantifier over 'var' in the sequent starting at 1. Default is 1.") @Option(value = "occ") public @Nullable int occ = 1; - @Documentation("If given, the rule used for instantiation is the one that hides the instantiated formula.") + @Documentation("If given, the rule used for instantiation is the one that hides the instantiated formula to " + + "prevent it from being used for further automatic proof steps.") @Flag("hide") public boolean hide; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/LeaveCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/LeaveCommand.java index c2c9643ee74..a7d3689e61f 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/LeaveCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/LeaveCommand.java @@ -6,9 +6,13 @@ import de.uka.ilkd.key.control.AbstractUserInterfaceControl; import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.scripts.meta.Documentation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@Documentation(category = "Control", value = """ + Leave the current goal as it is. Technically, this + marks the current goal to be 'interactive' that is ignored by script commands or calls to automation.""") public class LeaveCommand extends NoArgumentCommand { private static final Logger LOGGER = LoggerFactory.getLogger(LeaveCommand.class.getName()); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ObtainCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ObtainCommand.java index f855070bf1c..2576e9250d5 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ObtainCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ObtainCommand.java @@ -155,10 +155,16 @@ private static SchemaVariable getSV(ImmutableSet schemaVars, Str throw new NoSuchElementException("No schema variable with name " + name); } - @Documentation("TODO.") + @Documentation(category = "JML", value = """ + Command that introduces a fresh variable with a given name and sort. + Exactly one of `such_that`, `equals`, or `from_goal` must be given. + + The command should not be called directly, but is used internally by + the JML script support within KeY. + """) public static class Parameters implements ValueInjector.VerifyableParameters { @Option(value = "var") - @Documentation("Name of the instantiated variable.") + @Documentation("Name of the variable to be instantiated.") public @MonotonicNonNull String var; @Option(value = "such_that") diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java index 741b6eeee58..e9925d75949 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java @@ -6,6 +6,7 @@ import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.rule.IBuiltInRuleApp; import de.uka.ilkd.key.rule.OneStepSimplifierRuleApp; +import de.uka.ilkd.key.scripts.meta.Documentation; import de.uka.ilkd.key.scripts.meta.Option; import org.key_project.logic.PosInTerm; @@ -57,10 +58,20 @@ public void execute(ScriptCommandAst command) throws ScriptException, Interrupte } } + @Documentation(category = "Fundamental", value = """ + The oss command applies the *one step simplifier* on the current proof goal. + This simplifier applies a set of built-in simplification rules to the formulas in the sequent. + It can be configured to apply the one step simplifier only on the antecedent or succedent. + By default, it is applied on both sides of the sequent. + """) public static class Parameters { + @Documentation("Application of the one step simplifier can be forbidden on the antecedent side by setting " + + "this option to false. Default is true.") @Option(value = "antecedent") public @Nullable Boolean antecedent = Boolean.TRUE; + @Documentation("Application of the one step simplifier can be forbidden on the succedent side by setting " + + "this option to false. Default is true.") @Option(value = "succedent") public @Nullable Boolean succedent = Boolean.TRUE; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptCommand.java index d4fb03c6735..a8bf31ab11e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptCommand.java @@ -6,6 +6,7 @@ import java.util.List; import de.uka.ilkd.key.control.AbstractUserInterfaceControl; +import de.uka.ilkd.key.scripts.meta.ArgumentsLifter; import de.uka.ilkd.key.scripts.meta.ProofScriptArgument; import org.jspecify.annotations.NullMarked; @@ -38,15 +39,19 @@ void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst args, EngineState stateMap) throws ScriptException, InterruptedException; - /// Returns the name of this proof command. The name should be constant and not be clash with - /// the - /// name of other commands. The name is essential for finding this command within an hashmap. + /// Returns the name of this proof command. The name must be a constant and not be clash with + /// the name of other commands. The name is used to identify the command in a script. The name + /// must be amongst the aliases returned by [#getAliases()]. /// /// @return a non-null, non-empty string /// @see ProofScriptEngine + /// @see #getAliases() String getName(); - /// Announce a list of potential aliases of this command. + /// Announce a list of aliases of this command. + /// + /// Aliases of different commands should be disjoint, otherwise the first command found + /// will be executed. /// /// The command can react differently for each alias. The call name is given to /// [#execute(AbstractUserInterfaceControl,ScriptCommandAst,EngineState)] @@ -55,6 +60,7 @@ void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst args, /// /// @return an unmodifiable list of alias names under which command can be called, including /// [#getName()] + /// @see #getName() default List getAliases() { return List.of(getName()); } @@ -62,5 +68,12 @@ default List getAliases() { /// A documentation for the commands. /// /// @return a non-null string - String getDocumentation(); + default String getDocumentation() { + return ArgumentsLifter.extractDocumentation(getName(), getClass(), null); + } + + /// A category name for this command. This is used to group commands in the UI or documentation. + default String getCategory() { + return ArgumentsLifter.extractCategory(getClass(), null); + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java index 4259296d086..1fd2bceb78a 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java @@ -49,7 +49,7 @@ public ProofScriptEngine(Proof proof) { this.stateMap = new EngineState(proof, this); } - private static Map loadCommands() { + static Map loadCommands() { Map result = new HashMap<>(); var loader = ServiceLoader.load(ProofScriptCommand.class); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java index b96b6609476..ceae3c429f1 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java @@ -392,34 +392,46 @@ private List filterList(Services services, Parameters p, return matchingApps; } - @Documentation("This command can be used to apply a calculus rule to the currently active open goal.") + @Documentation(category = "Fundamental", value = """ + This command can be used to apply a calculus rule to the currently active open goal. + + #### Examples: + - `rule cut inst_cutFormula: (a > 0)` applies the cut rule on the formula `a > 0` like the cut command. + - `rule and_right on=(__ & __)` applies the rule `and_right` to the second occurrence + of a conjunction in the succedent. + - `rule my_rule on=(f(x)) formula="f\\(.*search.*\\)"` applies the rule `my_rule` to the term + `f(x)` in a formula matching the regular expression. + """) public static class Parameters { @Argument @Documentation("Name of the rule to be applied.") public @MonotonicNonNull String rulename; @Option(value = "on") - @Documentation("Term on which the rule should be applied to (matching the 'find' clause of the rule).") + @Documentation("Term on which the rule should be applied to (matching the 'find' clause of the rule). " + + "This may contain placeholders.") public @Nullable JTerm on; @Option(value = "formula") - @Documentation("Top-level formula in which the term appears.") + @Documentation("Top-level formula in which the term appears. This may contain placeholders.") public @Nullable JTerm formula; @Option(value = "occ") - @Documentation("Occurrence number if more than one occurrence matches.") + @Documentation("Occurrence number if more than one occurrence matches. The first occurrence is 1. " + + "If ommitted, there must be exactly one occurrence.") public @Nullable Integer occ = -1; /** * Represents a part of a formula (may use Java regular expressions as long as supported by * proof script parser). Rule is applied to the sequent formula which matches that string. */ - @Documentation("Instead of giving the toplevl formula completely, a regular expression can be specified to match the toplevel formula.") + @Documentation("Instead of giving the toplevl formula completely, a regular expression can be " + + "specified to match the toplevel formula.") @Option(value = "matches") public @Nullable String matches = null; @OptionalVarargs(as = JTerm.class, prefix = "inst_") - @Documentation("Instantiations for schema variables used in the rule.") + @Documentation("Instantiations for term schema variables used in the rule.") public Map instantiations = new HashMap<>(); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SMTCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SMTCommand.java index 86b21964637..3a2bd65efa9 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SMTCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SMTCommand.java @@ -7,6 +7,7 @@ import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.rule.IBuiltInRuleApp; +import de.uka.ilkd.key.scripts.meta.Documentation; import de.uka.ilkd.key.scripts.meta.Flag; import de.uka.ilkd.key.scripts.meta.Option; import de.uka.ilkd.key.settings.DefaultSMTSettings; @@ -23,6 +24,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Command to invoke an SMT solver on the current goal(s). + * + * See Parameters for documentation. + */ public class SMTCommand extends AbstractCommand { private static final Logger LOGGER = LoggerFactory.getLogger(SMTCommand.class); @@ -108,11 +114,21 @@ private SolverTypeCollection computeSolvers(String value) throws ScriptException return new SolverTypeCollection(value, 1, types); } + @Documentation(category = "Fundamental", value = """ + The smt command invokes an SMT solver on the current goal(s). + By default, it uses the Z3 solver on the first open automatic goal. + If the option 'all' is given, it runs on all open goals. + If the option 'solver' is given, it uses the specified solver(s) instead of Z3. + Multiple solvers can be specified by separating their names with commas. + The available solvers depend on your system: KeY supports at least z3, cvc5. + """) public static class SMTCommandArguments { @Option("solver") public String solver = "Z3"; + @Deprecated @Flag(value = "all") + @Documentation("*Deprecated!* Apply the command on all open goals instead of only the first open automatic goal.") public boolean all = false; @Option(value = "timeout") diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SelectCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SelectCommand.java index 650ef75542d..71bb5a35d40 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SelectCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SelectCommand.java @@ -12,8 +12,11 @@ import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Node; import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.scripts.meta.Documentation; +import de.uka.ilkd.key.scripts.meta.InjectionException; import de.uka.ilkd.key.scripts.meta.Option; +import de.uka.ilkd.key.scripts.meta.ValueInjector; import org.key_project.logic.Term; import org.key_project.prover.sequent.Semisequent; import org.key_project.prover.sequent.Sequent; @@ -24,12 +27,15 @@ import static de.uka.ilkd.key.logic.equality.RenamingTermProperty.RENAMING_TERM_PROPERTY; +/** + * The SelectCommand selects a goal in the current proof. See documentation of {@link Parameters} + * for more information. + */ public class SelectCommand extends AbstractCommand { public SelectCommand() { super(Parameters.class); } - @Override public void execute(ScriptCommandAst params) throws ScriptException, InterruptedException { var args = state().getValueInjector().inject(new Parameters(), params); @@ -147,19 +153,48 @@ public String getName() { return "select"; } - public static class Parameters { + @Documentation(category = "Control", value = """ + The select command selects a goal in the current proof. + Exactly one of the parameters must be given. + The next command will then continue on the selected goal. + + #### Examples: + - `select formula: (x > 0)` + - `select number: -2` + - `select branch: "Loop Invariant"` + """) + public static class Parameters implements ValueInjector.VerifyableParameters { /** A formula defining the goal to select */ + @Documentation("A formula defining the goal to select. May contain placeholder symbols. If there is a formula " + + "matching the given formula in multiple goals, the first one is selected.") @Option(value = "formula") public @Nullable JTerm formula; + /** * The number of the goal to select, starts with 0. Negative indices are also allowed: -1 is * the last goal, -2 the second-to-last, etc. */ + @Documentation("The number of the goal to select, starts with 0. Negative indices are also allowed: -1 is " + + "the last goal, -2 the second-to-last, etc.") @Option(value = "number") public @Nullable Integer number; + /** The name of the branch to select */ + @Documentation("The name of the branch to select. If there are multiple branches with the same name, " + + "the first one is selected.") @Option(value = "branch") public @Nullable String branch; - } + @Override + public void verifyParameters() throws IllegalArgumentException, InjectionException { + int cnt = 0; + if (formula != null) cnt++; + if (number != null) cnt++; + if (branch != null) cnt++; + if (cnt != 1) { + throw new InjectionException( + "Exactly one of 'formula', 'branch' or 'number' are required"); + } + } + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SkipCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SkipCommand.java index 18418fb0729..f3c99991b26 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SkipCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SkipCommand.java @@ -4,7 +4,9 @@ package de.uka.ilkd.key.scripts; import de.uka.ilkd.key.control.AbstractUserInterfaceControl; +import de.uka.ilkd.key.scripts.meta.Documentation; +@Documentation(category = "Control", value = "Does exactly nothing.") public class SkipCommand extends NoArgumentCommand { @Override public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst args, @@ -16,9 +18,4 @@ public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst arg public String getName() { return "skip"; } - - @Override - public String getDocumentation() { - return "Does exactly nothing. Really nothing."; - } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ArgumentsLifter.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ArgumentsLifter.java index cced4bcc96d..67dc24ea8fe 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ArgumentsLifter.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ArgumentsLifter.java @@ -5,18 +5,21 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; +import java.util.*; +import de.uka.ilkd.key.scripts.AbstractCommand; +import de.uka.ilkd.key.scripts.ProofScriptCommand; import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * @author Alexander Weigl * @version 1 (21.04.17) */ public final class ArgumentsLifter { + private static final String OPEN_BRACKET = "\u27e8"; + private static final String CLOSE_BRACKET = "\u27e9"; + private ArgumentsLifter() { } @@ -40,21 +43,21 @@ public static String generateCommandUsage(String commandName, Class parameter var sb = new StringBuilder(commandName); for (var meta : args) { sb.append(' '); - sb.append(meta.isRequired() ? '<' : '['); + if(!meta.isRequired() || meta.isFlag()) + sb.append("["); if (meta.isPositional()) { - sb.append(meta.getName()); + sb.append(OPEN_BRACKET + meta.getType().getSimpleName() + " (" + meta.getName() + ")" + CLOSE_BRACKET); } if (meta.isOption()) { sb.append(meta.getName()); - sb.append(": "); - sb.append(meta.getField().getType().getName()); + sb.append(":"); + sb.append(OPEN_BRACKET + meta.getField().getType().getSimpleName() + CLOSE_BRACKET); } if (meta.isFlag()) { sb.append(meta.getName()); - sb.append("[: true/false]"); } if (meta.isPositionalVarArgs()) { @@ -64,8 +67,9 @@ public static String generateCommandUsage(String commandName, Class parameter if (meta.isOptionalVarArgs()) { sb.append("%s...".formatted(meta.getName())); } - - sb.append(meta.isRequired() ? '>' : ']'); + + if(!meta.isRequired() || meta.isFlag()) + sb.append("]"); } @@ -76,48 +80,60 @@ public static String extractDocumentation(String command, Class commandClazz, Class parameterClazz) { StringBuilder sb = new StringBuilder(); + Deprecated dep = commandClazz.getAnnotation(Deprecated.class); + if(dep != null) { + sb.append("**Caution! This proof script command is deprecated, and may be removed soon!**\n\n"); + } + Documentation docCommand = commandClazz.getAnnotation(Documentation.class); if (docCommand != null) { sb.append(docCommand.value()); sb.append("\n\n"); } + if(parameterClazz == null) { + return sb.toString(); + } + Documentation docAn = parameterClazz.getAnnotation(Documentation.class); if (docAn != null) { sb.append(docAn.value()); sb.append("\n\n"); } - sb.append("Usage: ").append(generateCommandUsage(command, parameterClazz)) - .append("\n\n"); + sb.append("#### Usage: \n`").append(generateCommandUsage(command, parameterClazz)) + .append("`\n\n"); - final var args = getSortedProofScriptArguments(parameterClazz); + List args = getSortedProofScriptArguments(parameterClazz); - for (var meta : args) { + sb.append("#### Parameters:\n"); + for (ProofScriptArgument meta : args) { sb.append("\n\n"); if (meta.isPositional()) { - sb.append("* Argument %s (%s): %s".formatted( + sb.append("* `%s` *(%s%s positional argument, type %s)*:
    %s".formatted( meta.getName(), - meta.getField().getType(), + meta.isRequired() ? "" : "optional ", + ordinalStr(meta.getArgumentPosition() + 1), + meta.getField().getType().getSimpleName(), meta.getDocumentation())); } if (meta.isOption()) { - sb.append("* Option %s (%s): %s".formatted( + sb.append("* `%s` *(%snamed option, type %s)*:
    %s".formatted( meta.getName(), - meta.getField().getType(), + meta.isRequired() ? "" : "optional ", + meta.getField().getType().getSimpleName(), meta.getDocumentation())); } if (meta.isFlag()) { - sb.append("* Option %s [%s]: %s".formatted( + sb.append("* `%s` *(flag)*:
    %s".formatted( meta.getName(), - meta.getFlag().defValue(), meta.getDocumentation())); } if (meta.isPositionalVarArgs()) { - sb.append("* %s... (%s): %s".formatted( + sb.append("* `%s...` (%s): %s".formatted( meta.getName(), meta.getPositionalVarargs().as(), meta.getPositionalVarargs().startIndex(), @@ -125,10 +141,9 @@ public static String extractDocumentation(String command, Class commandClazz, } if (meta.isOptionalVarArgs()) { - sb.append("* %s: %s... (%s): %s".formatted( - meta.getOptionalVarArgs(), - meta.getName(), - meta.getField().getType(), + sb.append("* `%s...`: *(options prefixed by `%s`, type %s)*:
    %s".formatted( + meta.getName(), meta.getOptionalVarArgs().prefix(), + meta.getField().getType().getSimpleName(), meta.getDocumentation())); } @@ -137,42 +152,71 @@ public static String extractDocumentation(String command, Class commandClazz, return sb.toString(); } - private static @NonNull List getSortedProofScriptArguments( - Class parameterClazz) { - Comparator optional = - Comparator.comparing(ProofScriptArgument::isOption); - Comparator positional = - Comparator.comparing(ProofScriptArgument::isPositional); - Comparator flagal = Comparator.comparing(ProofScriptArgument::isFlag); - Comparator allargsal = - Comparator.comparing(ProofScriptArgument::isPositionalVarArgs); - Comparator byRequired = - Comparator.comparing(ProofScriptArgument::isRequired); - Comparator byName = Comparator.comparing(ProofScriptArgument::getName); - - Comparator byPos = Comparator.comparing(it -> { - if (it.isPositionalVarArgs()) { - it.getPositionalVarargs().startIndex(); - } - if (it.isPositional()) { - it.getArgument().value(); + private static String ordinalStr(int post) { + if (post % 100 >= 11 && post % 100 <= 13) { + return post + "th"; + } + return switch (post % 10) { + case 1 -> post + "st"; + case 2 -> post + "nd"; + case 3 -> post + "rd"; + default -> post + "th"; + }; + } + + public static String extractCategory(Class commandClazz, @Nullable Class parameterClazz) { + Documentation docCommand = commandClazz.getAnnotation(Documentation.class); + if (docCommand != null && !docCommand.category().isBlank()) { + return docCommand.category(); + } + + if(parameterClazz != null) { + Documentation docAn = parameterClazz.getAnnotation(Documentation.class); + if (docAn != null && !docAn.category().isBlank()) { + return docAn.category(); } + } - return -1; - }); + return "Uncategorized"; + } - var comp = optional - .thenComparing(flagal) - .thenComparing(positional) - .thenComparing(allargsal) - .thenComparing(byRequired) - .thenComparing(byPos) - .thenComparing(byName); + private static @NonNull List getSortedProofScriptArguments( + Class parameterClazz) { +// Comparator optional = +// Comparator.comparing(ProofScriptArgument::isOption); +// Comparator positional = +// Comparator.comparing(ProofScriptArgument::isPositional); +// Comparator flagal = Comparator.comparing(ProofScriptArgument::isFlag); +// Comparator allargsal = +// Comparator.comparing(ProofScriptArgument::isPositionalVarArgs); +// Comparator byRequired = +// Comparator.comparing(ProofScriptArgument::isRequired); +// Comparator byName = Comparator.comparing(ProofScriptArgument::getName); +// +// Comparator byPos = Comparator.comparing(it -> { +// if (it.isPositionalVarArgs()) { +// it.getPositionalVarargs().startIndex(); +// } +// if (it.isPositional()) { +// it.getArgument().value(); +// } +// +// return -1; +// }); +// +// +// var comp = optional +// .thenComparing(flagal) +// .thenComparing(positional) +// .thenComparing(allargsal) +// .thenComparing(byRequired) +// .thenComparing(byPos) +// .thenComparing(byName); var args = Arrays.stream(parameterClazz.getDeclaredFields()) .map(ProofScriptArgument::new) - .sorted(comp) + .sorted(Comparator.comparing(ProofScriptArgument::orderString)) .toList(); return args; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Documentation.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Documentation.java index a4fe62a1766..e3c7a1f62f4 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Documentation.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Documentation.java @@ -17,4 +17,5 @@ public @interface Documentation { /// @return a non-null string String value(); + String category() default ""; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ProofScriptArgument.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ProofScriptArgument.java index 66939fadcfc..02b11bf41e9 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ProofScriptArgument.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ProofScriptArgument.java @@ -138,4 +138,17 @@ public boolean hasNoAnnotation() { public Class getType() { return field.getType(); } + + /** + * This is used for ordering arguments in the usage string and documention. + * + * A twodigit number is used for positional arguments. 'M' for mandatory, 'O' for optional flags and options. + */ + public String orderString() { + if(isPositional()) + return "%02d".formatted(getArgumentPosition()); + if(isRequired()) + return "M " + getName(); + return "O " + getName(); + } } diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java new file mode 100644 index 00000000000..f747d4da417 --- /dev/null +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java @@ -0,0 +1,104 @@ +package de.uka.ilkd.key.scripts; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import de.uka.ilkd.key.control.DefaultUserInterfaceControl; +import de.uka.ilkd.key.control.KeYEnvironment; +import de.uka.ilkd.key.nparser.KeyAst; +import de.uka.ilkd.key.util.KeYResourceManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class DocumentationGenerator { + + public static void main(String[] args) throws FileNotFoundException { + + if(args.length > 0) { + System.err.println("Redirecting output to " + args[0]); + System.setOut(new java.io.PrintStream(args[0])); + } + + printHeader(); + + Collection commands = ProofScriptEngine.loadCommands().values(); + Map> commandsByCategory = new TreeMap<>(); + + for (ProofScriptCommand command : commands) { + String category = command.getCategory(); + if (category == null) { + category = "Uncategorized"; + } + commandsByCategory.computeIfAbsent(category, k -> new ArrayList<>()).add(command); + } + + List categories = new ArrayList<>(commandsByCategory.keySet()); + categories.remove("Uncategorized"); + Collections.sort(categories); + + for (String category : categories) { + listCategory(category, commandsByCategory.get(category)); + } + + listCategory("Uncategorized", commandsByCategory.get("Uncategorized")); + + } + + private static void printHeader() { + String branch = KeYResourceManager.getManager().getBranch(); + String version = KeYResourceManager.getManager().getVersion(); + String sha1 = KeYResourceManager.getManager().getSHA1(); + + System.out.printf(""" + # Proof Script Commands + + This document lists all proof script commands available in the KeY system. + The general ideas of scripts, their syntax, and control flow are described + in the general documentation files on proof scripts. + + Field | Value + ----- | ----- + Generated on: | %s + Branch: | %s + Version: | %s + Commit: | %s + + The commands are organised into categories. Each command may have multiple aliases + under which it can be invoked. The first alias listed is the primary name of the command. + There *named* and *positional* arguments. Named arguments need to be prefixed by their name + and a colon. Positional arguments are given in the order defined by the command. + Optional arguments are enclosed in square brackets. + """, new Date(), branch, version, sha1); + } + + private static void listCategory(String category, List proofScriptCommands) { + Set alreadyListed = new HashSet<>(); + System.out.println("\n## Category *" + category + "*\n"); + for (ProofScriptCommand command : proofScriptCommands) { + if(alreadyListed.contains(command)) + continue; + alreadyListed.add(command); + System.out.println("


    \n"); + System.out.println("### Command `" + command.getName() + "`\n"); + System.out.println(command.getDocumentation() + "\n"); + if(command.getAliases().size() > 1) { + System.out.println("#### Aliases:\n" + String.join(", ", command.getAliases()) + "\n"); + } + } + } +} From d666b66f02f2cefca7d1182fbb61a74ec9bc3f58 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 3 Oct 2025 00:04:07 +0200 Subject: [PATCH 42/90] lots of script documentation --- .../uka/ilkd/key/scripts/ActivateCommand.java | 12 +++--- .../de/uka/ilkd/key/scripts/AllCommand.java | 19 +++++----- .../key/scripts/AssertOpenGoalsCommand.java | 2 +- .../uka/ilkd/key/scripts/AssumeCommand.java | 14 ++----- .../de/uka/ilkd/key/scripts/AxiomCommand.java | 5 +++ .../de/uka/ilkd/key/scripts/CheatCommand.java | 13 +++---- .../scripts/DependencyContractCommand.java | 19 +++++++++- .../de/uka/ilkd/key/scripts/ExitCommand.java | 10 ++--- .../de/uka/ilkd/key/scripts/HideCommand.java | 8 +++- .../ilkd/key/scripts/JavascriptCommand.java | 37 +++++++++++++++++++ .../de/uka/ilkd/key/scripts/MacroCommand.java | 22 ++++++++++- .../uka/ilkd/key/scripts/SaveInstCommand.java | 6 +++ .../ilkd/key/scripts/SaveNewNameCommand.java | 15 +++++++- .../ilkd/key/scripts/SchemaVarCommand.java | 5 +++ .../uka/ilkd/key/scripts/ScriptCommand.java | 7 ++++ .../uka/ilkd/key/scripts/SetEchoCommand.java | 7 +++- .../key/scripts/SetFailOnClosedCommand.java | 2 + .../uka/ilkd/key/scripts/UnhideCommand.java | 8 +++- .../key/scripts/meta/ArgumentsLifter.java | 2 +- .../key/scripts/DocumentationGenerator.java | 7 +++- 20 files changed, 174 insertions(+), 46 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ActivateCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ActivateCommand.java index 4d04f87e091..244ce4bc9e9 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ActivateCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ActivateCommand.java @@ -5,6 +5,7 @@ import de.uka.ilkd.key.control.AbstractUserInterfaceControl; import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.scripts.meta.Documentation; /** * Command for re-activating the first open (not necessarily enabled) {@link Goal} after a "leave" @@ -13,17 +14,18 @@ * * @author Dominic Steinhoefel */ +@Documentation(category = "Control", value = """ + Reactivates the first open (not necessarily enabled) goal. + This can be useful after a 'leave' command to continue + working on a complicated proof where 'tryclose' should not + apply on certain branches temporarily, but where one still + wants to finish the proof.""") public class ActivateCommand extends NoArgumentCommand { @Override public String getName() { return "activate"; } - @Override - public String getDocumentation() { - return ""; - } - @Override public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst args, EngineState state) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AllCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AllCommand.java index d185a3af2c0..aad15803e3f 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AllCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AllCommand.java @@ -7,8 +7,18 @@ import de.uka.ilkd.key.control.AbstractUserInterfaceControl; import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.scripts.meta.Documentation; import de.uka.ilkd.key.scripts.meta.ProofScriptArgument; +@Documentation(category = "Control", value = """ + Executes a given block of script commands on all open goals. + The current goal is set to each open goal in turn while executing the block. + It expects exactly one positional argument, which is the block to be executed on each goal. + + #### Examples: + * `onAll { smt solver="z3"; }` + * `onAll { auto; }` + """) public class AllCommand implements ProofScriptCommand { @Override public List getArguments() { @@ -40,13 +50,4 @@ public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst arg public String getName() { return "onAll"; } - - /** - * {@inheritDoc} - */ - @Override - public String getDocumentation() { - return """ - Applies the given command to all the open goals."""; - } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertOpenGoalsCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertOpenGoalsCommand.java index ba84205242e..f4aa0ff05ab 100755 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertOpenGoalsCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertOpenGoalsCommand.java @@ -45,7 +45,7 @@ public String getName() { /** * The Assigned parameters (currently only the passed goals). */ - @Documentation(""" + @Documentation(category = "Control", value = """ The assert command checks if the number of open and enabled goals is equal to the given number. If not, the script is halted with an error message. diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssumeCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssumeCommand.java index 27e2d753301..c997292a3cb 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssumeCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssumeCommand.java @@ -34,16 +34,6 @@ public String getName() { return "assume"; } - @Override - public String getDocumentation() { - return """ - The assume command is an unsound taclet rule and takes one argument: - - The command adds the formula passed as argument to the antecedent - a formula #2 to which the command is applied"""; - } - - public void execute(ScriptCommandAst arguments) throws ScriptException, InterruptedException { var parameter = state().getValueInjector() .inject(new FormulaParameter(), arguments); @@ -57,6 +47,10 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup state.getFirstOpenAutomaticGoal().apply(app); } + @Documentation(category = "Control", value = """ + The assume command is an **unsound** taclet rule and adds a formula to the antecedent of the current goal + Can be used for debug and proof exploration purposes. + """) public static class FormulaParameter { @Argument @Documentation("The formula to be assumed.") diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AxiomCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AxiomCommand.java index 182f94916ec..5da8270eb51 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AxiomCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AxiomCommand.java @@ -10,6 +10,7 @@ import de.uka.ilkd.key.rule.TacletApp; import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Documentation; import org.key_project.logic.Name; import org.key_project.logic.op.sv.SchemaVariable; @@ -25,6 +26,10 @@ * Use the {@link AssumeCommand} "assume" instead. */ @Deprecated(forRemoval = true) +@Documentation(""" + This command is deprecated and should not be used in new scripts. + Use the equivalent `assume` command instead. + """) public class AxiomCommand extends AssumeCommand { private static final Name TACLET_NAME = new Name("cut"); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java index 65b697d1219..e3194efcb71 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java @@ -10,6 +10,7 @@ import de.uka.ilkd.key.rule.Taclet; import de.uka.ilkd.key.rule.TacletApp; +import de.uka.ilkd.key.scripts.meta.Documentation; import org.key_project.logic.ChoiceExpr; import org.key_project.logic.Name; import org.key_project.prover.rules.ApplicationRestriction; @@ -19,6 +20,11 @@ import org.key_project.util.collection.ImmutableList; import org.key_project.util.collection.ImmutableSet; +@Documentation(category = "Internal", value = """ + Use this to close a goal unconditionally. This is unsound and should only + be used for testing and proof debugging purposes. It is similar to 'sorry' + in Isabelle or 'admit' in Rocq. + """) public class CheatCommand extends NoArgumentCommand { private static final Taclet CHEAT_TACLET; @@ -38,13 +44,6 @@ public String getName() { return "cheat"; } - @Override - public String getDocumentation() { - return "Use this to close a goal unconditionally. This is unsound and should only " + - "be used for testing and proof debugging purposes. It is similar to 'sorry' " + - "in Isabelle or 'admit' in Rocq."; - } - @Override public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst ast, EngineState state) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java index 99c1deaaf5a..587b7bf5799 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java @@ -11,8 +11,10 @@ import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.rule.IBuiltInRuleApp; import de.uka.ilkd.key.rule.UseDependencyContractApp; +import de.uka.ilkd.key.scripts.meta.Documentation; import de.uka.ilkd.key.scripts.meta.Option; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.key_project.logic.PosInTerm; import org.key_project.logic.Term; import org.key_project.prover.sequent.PosInOccurrence; @@ -23,6 +25,10 @@ import org.jspecify.annotations.Nullable; +/** + * The DependencyContractCommand applies a dependency contract to a selected formula in the current + * goal. See documentation of {@link Parameters} for more information. + */ public class DependencyContractCommand extends AbstractCommand { public DependencyContractCommand() { @@ -104,10 +110,21 @@ private void apply(Goal goal, UseDependencyContractApp ruleApp, Parameters argum goal.apply(ruleApp); } + @Documentation(category = "Fundamental", value = """ + The dependency command applies a dependency contract to a specified term in the current goal. + Dependency contracts allow you to do modular reasoning. If for a heap-dependent function symbol, + no changes occur inside the dependency set of this function, the result remains the same. + This can be applied to model methods, model fields or invariants. + """) public static class Parameters { + + @Documentation("The term to which the dependency contract should be applied. " + + "This term must occur in the current goal. " + + "And it must be the invocation of a heap-dependent observer function symbol.") @Option(value = "on") - public JTerm on; + public @MonotonicNonNull JTerm on; + @Documentation("The heap term to be compared against. If not given, the default heap is used.") @Option(value = "heap") public @Nullable JTerm heap; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExitCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExitCommand.java index a3c79c478f5..3b4fbf8a9d0 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExitCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExitCommand.java @@ -5,7 +5,12 @@ import de.uka.ilkd.key.control.AbstractUserInterfaceControl; +import de.uka.ilkd.key.scripts.meta.Documentation; +@Documentation(category = "Control", value = """ + Exits the currently running script context unconditionally. + (In the future, there may try-catch blocks to react to this). + """) public class ExitCommand extends NoArgumentCommand { @Override public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst args, @@ -18,9 +23,4 @@ public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst arg public String getName() { return "exit"; } - - @Override - public String getDocumentation() { - return "Kills the script execution."; - } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/HideCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/HideCommand.java index 6f37e696469..77ef42f4302 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/HideCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/HideCommand.java @@ -11,6 +11,7 @@ import de.uka.ilkd.key.rule.TacletApp; import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Documentation; import org.key_project.logic.Name; import org.key_project.logic.PosInTerm; import org.key_project.logic.Term; @@ -25,7 +26,7 @@ import static de.uka.ilkd.key.logic.equality.TermLabelsProperty.TERM_LABELS_PROPERTY; /** - * Proof script command to hide a formula from the sequent. + * Proof script command to hide formulas from the sequent. * * Usage: * @@ -99,9 +100,14 @@ public String getName() { return "hide"; } + @Documentation(category = "Control", value = """ + The hide command hides all formulas of the current proof goal that are in the given sequent. + The formulas in the given sequent are hidden using the taclets hide_left and hide_right. + """) public static class Parameters { @Argument @MonotonicNonNull + @Documentation("The sequent containing the formulas to hide. Placeholders are allowed.") public Sequent sequent; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/JavascriptCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/JavascriptCommand.java index d3bb13607c9..77ef4459391 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/JavascriptCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/JavascriptCommand.java @@ -13,10 +13,29 @@ import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Documentation; import org.key_project.prover.sequent.Sequent; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +/** + * This command allows to execute arbitrary JavaScript code. The code is executed in a context where + * the current selected goal is available as {@code goal} and a function {@code setVar(v,t)} is + * available to set an abbreviation (where {@code v} is the name of the variable including the + * leading {@code @} and {@code t} is either a term or a string that can be parsed as a term). + *

    + * Example: + * + *

    + * javascript {
    + *   var x = goal.getAntecedent().get(0).getFormula();
    + *   setVar("@myVar", x);
    + * }
    + * 
    + * + * This command is powerful but should be used with care, as it can easily lead to unsound proofs if + * used incorrectly. + */ public class JavascriptCommand extends AbstractCommand { private static final String PREAMBLE = """ @@ -52,7 +71,25 @@ public String getName() { return "javascript"; } + @Documentation(category = "Internal", value = """ + This command allows to execute arbitrary JavaScript code. The code is executed in a context where + the current selected goal is available as `goal` and a function `setVar(v,t)` is + available to set an abbreviation (where `v` is the name of the variable including the + leading `@` and `t` is either a term or a string that can be parsed as a term). + + #### Example: + ``` + javascript { + var x = goal.getAntecedent().get(0).getFormula(); + setVar("@myVar", x); + } + ``` + + This command is powerful but should be used with care, as it can easily lead to unsound proofs if + used incorrectly. + """) public static class Parameters { + @Documentation("The JavaScript code to execute.") @Argument public @MonotonicNonNull String script; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/MacroCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/MacroCommand.java index bd7763f428f..cffb75e9f57 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/MacroCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/MacroCommand.java @@ -19,13 +19,18 @@ import de.uka.ilkd.key.scripts.meta.Option; import de.uka.ilkd.key.scripts.meta.OptionalVarargs; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.key_project.logic.PosInTerm; import org.key_project.prover.engine.TaskStartedInfo; import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.prover.sequent.Sequent; import org.jspecify.annotations.Nullable; - +/** + * Command to invoke a user-defined macro (like from UI) + * + * See Parameters for documentation. + */ public class MacroCommand extends AbstractCommand { private static final Map macroMap = loadMacroMap(); @@ -164,10 +169,22 @@ private static String formatTermString(String str) { .replace(" +", " "); } + @Documentation(category = "Fundamental", value = """ + The MacroCommand invokes one of KeY's macros. The macro must be registered to KeY's services. + + The command takes the name of the macro as first argument, followed by optional + parameters to configure the macro. + + The macro is applied to the first open automatic goal in the proof. + + #### Examples: + * `macro "prop-split"` + * `macro "auto-pilot"` + """) public static class Parameters { @Argument @Documentation("Macro name") - public String macroName; + public @MonotonicNonNull String macroName; @Documentation("Run on formula number \"occ\" parameter") @Option(value = "occ") @@ -179,6 +196,7 @@ public static class Parameters { public @Nullable String matches = null; /** Variable macro parameters */ + @Documentation("Macro parameters, given as varargs with prefix 'arg_'. E.g. arg_param1=value1") @OptionalVarargs(as = String.class, prefix = "arg_") public Map instantiations = new HashMap<>(); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveInstCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveInstCommand.java index 61bf7156e1f..2b39adbfaea 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveInstCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveInstCommand.java @@ -9,6 +9,7 @@ import de.uka.ilkd.key.pp.AbbrevMap; import de.uka.ilkd.key.rule.TacletApp; +import de.uka.ilkd.key.scripts.meta.Documentation; import org.key_project.logic.Name; import org.key_project.logic.op.Function; import org.key_project.logic.op.sv.SchemaVariable; @@ -24,6 +25,11 @@ * * @author Dominic Steinhoefel */ +@Documentation(category = "Internal", value = """ + Saves the instantiation of a schema variable by the last taclet application into an abbreviation for later use. + A nice use case is a manual loop invariant rule application, where the newly introduced anonymizing Skolem constants can be saved for later interactive instantiations. + As for the let command, it is not allowed to call this command multiple times with the same name argument (all names used for remembering instantiations are "final"). + """) public class SaveInstCommand extends AbstractCommand { public SaveInstCommand() { super(null); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveNewNameCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveNewNameCommand.java index 49d28077f40..42d0c818376 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveNewNameCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveNewNameCommand.java @@ -12,6 +12,8 @@ import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Node; import de.uka.ilkd.key.rule.TacletApp; +import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Documentation; import de.uka.ilkd.key.scripts.meta.Option; import org.key_project.logic.Name; @@ -87,9 +89,20 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup } } + @Documentation(category = "Internal", value = """ + Special "Let" usually to be applied immediately after a manual rule application. Saves a new name + introduced by the last rule which matches certain criteria into an abbreviation for + later use. A nice use case is a manual loop invariant rule application, where the newly + introduced anonymizing Skolem constants can be saved for later interactive instantiations. As for + the let command, it is not allowed to call this command multiple times with the same name + argument (all names used for remembering instantiations are "final"). + """) public static class Parameters { - @Option(value = "#2") + @Documentation("The abbreviation to store the new name under, must start with @") + @Argument public String abbreviation; + + @Documentation("A regular expression to match the new name against, must match exactly one name") @Option(value = "matches") public String matches; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SchemaVarCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SchemaVarCommand.java index eef052c8e0d..06ac00c5099 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SchemaVarCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SchemaVarCommand.java @@ -10,6 +10,7 @@ import de.uka.ilkd.key.pp.AbbrevMap; import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Documentation; import org.key_project.logic.Name; import org.key_project.logic.sort.Sort; @@ -18,6 +19,7 @@ /** * */ +@Deprecated public class SchemaVarCommand extends AbstractCommand { public SchemaVarCommand() { @@ -65,6 +67,9 @@ public String getName() { return "schemaVar"; } + @Documentation(category = "Internal", value = """ + Defines a schema variable that can be used in subsequent commands. + """) public static class Parameters { @Argument(0) public @MonotonicNonNull String type; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommand.java index 50a6eff3b2c..978edf073a2 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommand.java @@ -9,10 +9,15 @@ import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Documentation; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Includes and runs another script file. + * See Parameters for more documentation. + */ public class ScriptCommand extends AbstractCommand { private static final Logger LOGGER = LoggerFactory.getLogger(ProofScriptCommand.class); @@ -21,7 +26,9 @@ public ScriptCommand() { super(Parameters.class); } + @Documentation(category = "Control", value = "Includes and runs another script file.") public static class Parameters { + @Documentation("The filename of the script to include. May be relative to the current script.") @Argument public @MonotonicNonNull String filename; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetEchoCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetEchoCommand.java index 843b4038f59..ff84be60a72 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetEchoCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetEchoCommand.java @@ -6,11 +6,16 @@ import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Documentation; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** - * A simple "echo" command for giving feedback to human observers during lengthy executions. + * An internal command to switch on/off echoing of executed commands. */ +@Deprecated +@Documentation(category = "Internal", value = """ + An internal command to switch on/off echoing of executed commands. + """) public class SetEchoCommand extends AbstractCommand { public SetEchoCommand() { super(Parameters.class); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetFailOnClosedCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetFailOnClosedCommand.java index 85aeb15dbe6..3ca852901ea 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetFailOnClosedCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetFailOnClosedCommand.java @@ -6,6 +6,7 @@ import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Documentation; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** @@ -19,6 +20,7 @@ * @deprecated This should be merged in the {@link SetCommand} with a parameter like "failonclosed". */ @Deprecated +@Documentation(category = "Control", value = "") public class SetFailOnClosedCommand extends AbstractCommand { public SetFailOnClosedCommand() { super(Parameters.class); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/UnhideCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/UnhideCommand.java index b7622eb9a78..c4d902aa13a 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/UnhideCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/UnhideCommand.java @@ -11,6 +11,7 @@ import de.uka.ilkd.key.rule.NoPosTacletApp; import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Documentation; import org.key_project.logic.Term; import org.key_project.logic.op.sv.SchemaVariable; import org.key_project.prover.proof.rulefilter.TacletFilter; @@ -21,7 +22,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** - * Proof script command to insert a formula hidden earlier in the proof. + * Proof script command to insert formulas hidden earlier in the proof. * * Usage: * @@ -83,7 +84,12 @@ public String getName() { return "unhide"; } + @Documentation(category = "Control", value = """ + The unhide command re-inserts formulas that have been hidden earlier in the proof using the hide command. + It takes a sequent as parameter and re-inserts all formulas in this sequent that have been hidden earlier. + """) public static class Parameters { + @Documentation("The sequent containing the formulas to be re-inserted. Placeholders are allowed.") @Argument public @MonotonicNonNull Sequent sequent; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ArgumentsLifter.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ArgumentsLifter.java index 67dc24ea8fe..e37627457ff 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ArgumentsLifter.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ArgumentsLifter.java @@ -143,7 +143,7 @@ public static String extractDocumentation(String command, Class commandClazz, if (meta.isOptionalVarArgs()) { sb.append("* `%s...`: *(options prefixed by `%s`, type %s)*:
    %s".formatted( meta.getName(), meta.getOptionalVarArgs().prefix(), - meta.getField().getType().getSimpleName(), + meta.getOptionalVarArgs().as().getSimpleName(), meta.getDocumentation())); } diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java index f747d4da417..fab732e5206 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java @@ -64,7 +64,11 @@ private static void printHeader() { String version = KeYResourceManager.getManager().getVersion(); String sha1 = KeYResourceManager.getManager().getSHA1(); - System.out.printf(""" + // This gets too technical. But this is for the key-docs repository. ... + System.out.printf(""" + # Proof Script Commands This document lists all proof script commands available in the KeY system. @@ -87,6 +91,7 @@ private static void printHeader() { } private static void listCategory(String category, List proofScriptCommands) { + proofScriptCommands.sort(Comparator.comparing(ProofScriptCommand::getName)); Set alreadyListed = new HashSet<>(); System.out.println("\n## Category *" + category + "*\n"); for (ProofScriptCommand command : proofScriptCommands) { From d5ef8d916adac75a0c1326c6cc60a51a2bf6cdda Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 3 Oct 2025 02:09:45 +0200 Subject: [PATCH 43/90] fixing a bug regarding proof script application --- .../java/de/uka/ilkd/key/gui/ProofScriptWorker.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/key.ui/src/main/java/de/uka/ilkd/key/gui/ProofScriptWorker.java b/key.ui/src/main/java/de/uka/ilkd/key/gui/ProofScriptWorker.java index 14cc9adcda5..e218efc1623 100644 --- a/key.ui/src/main/java/de/uka/ilkd/key/gui/ProofScriptWorker.java +++ b/key.ui/src/main/java/de/uka/ilkd/key/gui/ProofScriptWorker.java @@ -23,6 +23,7 @@ import de.uka.ilkd.key.scripts.ProofScriptEngine; import de.uka.ilkd.key.scripts.ScriptException; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; @@ -47,7 +48,8 @@ public class ProofScriptWorker extends SwingWorker<@Nullable Object, ProofScript /** * The proof script engine. */ - private final ProofScriptEngine engine; + private @MonotonicNonNull ProofScriptEngine engine; + private final JDialog monitor = new JDialog(MainWindow.getInstance(), "Running Script ...", ModalityType.MODELESS); private final JTextArea logArea = new JTextArea(); @@ -76,13 +78,13 @@ public ProofScriptWorker(KeYMediator mediator, KeyAst.ProofScript script, this.mediator = mediator; this.script = script; this.initiallySelectedGoal = initiallySelectedGoal; - engine = new ProofScriptEngine(initiallySelectedGoal.proof()); - engine.setInitiallySelectedGoal(initiallySelectedGoal); } @Override protected @Nullable Object doInBackground() throws Exception { try { + engine = new ProofScriptEngine(mediator.getSelectedProof()); + engine.setInitiallySelectedGoal(initiallySelectedGoal); engine.setCommandMonitor(observer); engine.execute(mediator.getUI(), script); } catch (InterruptedException ex) { @@ -173,7 +175,8 @@ public void done() { private void selectGoalOrNode() { final KeYSelectionModel selectionModel = mediator.getSelectionModel(); - if (!mediator.getSelectedProof().closed()) { + final Proof proof = mediator.getSelectedProof(); + if (proof != null && !proof.closed() && engine != null) { try { selectionModel .setSelectedGoal(engine.getStateMap().getFirstOpenAutomaticGoal()); From 59ab8309a560540d340292f2da411741d6de5e2e Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 3 Oct 2025 10:37:57 +0200 Subject: [PATCH 44/90] repairing some weird self variable treatment in SpecStatement --- .../ilkd/key/macros/ApplyScriptsMacro.java | 35 ++++++++++++++++--- .../ilkd/key/scripts/jml/ObtainFromGoal.java | 2 +- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 9bd55b96f3e..d7557e761e4 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -13,8 +13,10 @@ import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.java.SourceElement; import de.uka.ilkd.key.java.statement.JmlAssert; +import de.uka.ilkd.key.java.statement.MethodFrame; import de.uka.ilkd.key.logic.DefaultVisitor; import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.logic.JavaBlock; import de.uka.ilkd.key.logic.op.JFunction; import de.uka.ilkd.key.logic.op.LocationVariable; import de.uka.ilkd.key.logic.op.UpdateApplication; @@ -33,8 +35,9 @@ import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdCaseContext; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdContext; +import de.uka.ilkd.key.util.MiscTools; import org.key_project.logic.Term; -import org.key_project.logic.op.SortedOperator; +import org.key_project.logic.op.Modality; import org.key_project.prover.engine.ProverTaskListener; import org.key_project.prover.engine.TaskStartedInfo; import org.key_project.prover.rules.RuleApp; @@ -126,6 +129,16 @@ private static JmlAssert getJmlAssert(Node node) { return null; } + private static JavaBlock getJavaBlock(Goal goal) { + RuleApp ruleApp = goal.node().parent().getAppliedRuleApp(); + JTerm appliedOn = (JTerm) ruleApp.posInOccurrence().subTerm(); + if (appliedOn.op() instanceof UpdateApplication) { + appliedOn = UpdateApplication.getTarget(appliedOn); + } + assert appliedOn.op() instanceof Modality; + return appliedOn.javaBlock(); + } + @Override public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, ImmutableList goals, PosInOccurrence posInOcc, ProverTaskListener listener) @@ -146,7 +159,7 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, "Running attached script from goal " + goal.node().serialNr(), 0)); KeyAst.JMLProofScript proofScript = jmlAssert.getAssertionProof(); - Map termMap = getTermMap(jmlAssert, proof.getServices()); + Map termMap = getTermMap(jmlAssert, getJavaBlock(goal), proof.getServices()); // We heavily rely on that variables have been computed before, otherwise this will raise an NPE. Map obtainMap = makeObtainVarMap(jmlAssert.collectVariablesInProof(null)); JTerm update = getUpdate(goal); @@ -180,14 +193,17 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, } - private Map getTermMap(JmlAssert jmlAssert, Services services) { + private Map getTermMap(JmlAssert jmlAssert, JavaBlock javaBlock, Services services) { SpecificationRepository.@Nullable JmlStatementSpec jmlspec = services.getSpecificationRepository().getStatementSpec(jmlAssert); if (jmlspec == null) { throw new IllegalStateException( "No specification found for JML assert statement at " + jmlAssert); } - ImmutableList terms = jmlspec.terms().tail(); + ImmutableList terms = ImmutableList.of(); + for (int i = jmlspec.terms().size() - 1; i >= 1; i--) { + terms = terms.prepend(correctSelfVar(i, javaBlock, jmlspec, services)); + } ImmutableList jmlExprs = jmlAssert.collectTerms().tail(); Map result = new IdentityHashMap<>(); assert terms.size() == jmlExprs.size(); @@ -197,6 +213,17 @@ private Map getTermMap(JmlAssert jmlAssert, Services s return result; } + /** + * For some reason, the self variable in the spec is not the same as the self variable and needs to + * be corrected. + */ + private JTerm correctSelfVar(int index, JavaBlock javaBlock, SpecificationRepository.JmlStatementSpec spec, Services services) { + final MethodFrame frame = JavaTools.getInnermostMethodFrame(javaBlock, services); + final JTerm self = MiscTools.getSelfTerm(frame, services); + return spec.getTerm(services, self, index); + + } + private Map makeObtainVarMap(ImmutableList locationVariables) { HashMap result = new HashMap<>(); for (LocationVariable lv : locationVariables) { diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/ObtainFromGoal.java b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/ObtainFromGoal.java index 5bd1ba3b553..4f7350b56f1 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/ObtainFromGoal.java +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/ObtainFromGoal.java @@ -2,7 +2,7 @@ class Test { - //@ static model int f(int arg); + //@ model int f(int arg); //@ ensures true; void test() { From 1cd54346bc1b96b766587ce031312eb6afca42db Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 19 Jul 2024 17:46:43 +0200 Subject: [PATCH 45/90] introducing "auto add_*" to scripts. --- .../key/scripts/AdditionalRulesStrategy.java | 116 ++++++++++++++++++ .../de/uka/ilkd/key/scripts/AutoCommand.java | 15 +++ 2 files changed, 131 insertions(+) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java new file mode 100644 index 00000000000..27aad464413 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java @@ -0,0 +1,116 @@ +package de.uka.ilkd.key.scripts; + +import org.jspecify.annotations.Nullable; +import org.key_project.prover.rules.Rule; +import de.uka.ilkd.key.macros.FilterStrategy; +import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.rule.Taclet; +import de.uka.ilkd.key.strategy.Strategy; +import org.key_project.logic.Name; +import org.key_project.prover.rules.RuleApp; +import org.key_project.prover.rules.RuleSet; +import org.key_project.prover.sequent.PosInOccurrence; +import org.key_project.prover.strategy.costbased.NumberRuleAppCost; +import org.key_project.prover.strategy.costbased.RuleAppCost; +import org.key_project.util.collection.Pair; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +class AdditionalRulesStrategy extends FilterStrategy { + /** Name of that strategy */ + private static final Name NAME = new Name( + AdditionalRulesStrategy.class.getSimpleName()); + + private static final Map TRANSLATIONS = + Map.of("high", "-50", "medium", "1000", "low", "10000"); + private static final int DEFAULT_PRIORITY = 1000; + + private final List> additionalRules; + + public AdditionalRulesStrategy(Strategy delegate, String additionalRules) { + super(delegate); + this.additionalRules = parseAddRules(additionalRules); + } + + private List> parseAddRules(String additionalRules) { + List> result = new ArrayList<>(); + for (String entry : additionalRules.trim().split(" *, *")) { + String[] parts = entry.split(" *= *", 2); + int prio; + if (parts.length == 2) { + String prioStr = parts[1]; + prioStr = TRANSLATIONS.getOrDefault(prioStr, prioStr); + try { + prio = Integer.parseInt(prioStr); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid value for additional rule: " + parts[1]); + } + } else { + prio = DEFAULT_PRIORITY; + } + + result.add(new Pair<>(parts[0], prio)); + } + return result; + } + + @Override + public Name name() { + return NAME; + } + + @Override + public RuleAppCost computeCost(RuleApp app, PosInOccurrence pio, Goal goal) { + RuleAppCost localCost = computeLocalCost(app.rule()); + if (localCost != null) { + return localCost; + } + return super.computeCost(app, pio, goal); + } + + @Override + public boolean isApprovedApp(RuleApp app, PosInOccurrence pio, Goal goal) { + RuleAppCost localCost = computeLocalCost(app.rule()); + if (localCost != null) { + return true; + } + return super.isApprovedApp(app, pio, goal); + } + + private @Nullable RuleAppCost computeLocalCost(Rule rule) { + String name = rule.name().toString(); + Optional cost = lookup(name); + if(cost.isPresent()) { + return NumberRuleAppCost.create(cost.get()); + } + + if (rule instanceof Taclet taclet) { + for (RuleSet rs : taclet.getRuleSets()) { + String rname = rs.name().toString(); + cost = lookup(rname); + if(cost.isPresent()) { + return NumberRuleAppCost.create(cost.get()); + } + } + } + + return null; + } + + private Optional lookup(String name) { + return additionalRules.stream() + .filter(nameAndPrio -> name.matches(nameAndPrio.first)) + .findFirst() + .map(p -> p.second); + } + + @Override + public boolean isStopAtFirstNonCloseableGoal() { + return false; + } + + +} \ No newline at end of file diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java index 8cd83fa1f52..e9699a5647f 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java @@ -14,6 +14,7 @@ import de.uka.ilkd.key.prover.impl.ApplyStrategy; import de.uka.ilkd.key.scripts.meta.*; import de.uka.ilkd.key.strategy.FocussedBreakpointRuleApplicationManager; +import de.uka.ilkd.key.strategy.Strategy; import de.uka.ilkd.key.strategy.StrategyProperties; import org.key_project.prover.engine.ProverCore; @@ -92,6 +93,11 @@ public void execute(ScriptCommandAst args) throws ScriptException, InterruptedEx SetCommand.updateStrategySettings(state(), activeStrategyProperties); + final Strategy originalStrategy = state.getProof().getActiveStrategy(); + if (arguments.additionalRules != null) { + state.getProof().setActiveStrategy(new AdditionalRulesStrategy(originalStrategy, arguments.additionalRules)); + } + // Give some feedback applyStrategy.addProverTaskObserver(uiControl); @@ -113,6 +119,7 @@ public void execute(ScriptCommandAst args) throws ScriptException, InterruptedEx activeStrategyProperties.setProperty(ov.settingName, ov.oldValue); } } + state.getProof().setActiveStrategy(originalStrategy); SetCommand.updateStrategySettings(state(), activeStrategyProperties); } } @@ -215,6 +222,14 @@ may be a showstopper (if expansion increases the complexity on the sequent too m Enable dependency reasoning. In modular reasoning, the value of symbols may stay the same, \ without that its definition is known. May be an enabler, may be a showstopper.""") public boolean dependencies; + + @Option(value = "add") + @Documentation(""" + Additional rules to be used by the auto strategy. The rules have to be given as a + comma-separated list of rule names and rule set names. Each entry can be assigned to a priority + (high, low, medium or a natural number) using an equals sign. + """) + public @Nullable String additionalRules; } private static final class OriginalValue { From 16215c82ea39f64ec75cdc518497b9f62f8c4f08 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 3 Oct 2025 14:02:25 +0200 Subject: [PATCH 46/90] smaller fixes in proof script treatment --- .../java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java | 10 +++++++--- .../ilkd/key/proof/mgt/SpecificationRepository.java | 1 + .../java/de/uka/ilkd/key/scripts/ExpandDefCommand.java | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index d7557e761e4..56c24a237f1 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -170,6 +170,8 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, pse.getStateMap().putUserData("jml.obtainVarMap", obtainMap); pse.getStateMap().getValueInjector().addConverter(JTerm.class, ObtainAwareTerm.class, oat -> oat.resolve(obtainMap, goal.proof().getServices())); + pse.getStateMap().getValueInjector().addConverter(boolean.class, ObtainAwareTerm.class, + oat -> Boolean.parseBoolean(oat.term.toString())); LOGGER.debug("---- Script"); LOGGER.debug(renderedProof.stream().map(ScriptCommandAst::asCommandLine) .collect(Collectors.joining("\n"))); @@ -335,11 +337,13 @@ private static ScriptCommandAst renderObtainCommand(ProofCmdContext ctx, Map Date: Fri, 3 Oct 2025 14:01:51 +0200 Subject: [PATCH 47/90] The Boyer-Moore example now runs on scripts --- .../proof/runallproofs/ProofCollections.java | 15 ++-- key.ui/examples/heap/BoyerMoore/BM.bm.key | 86 +++++++++++++++++++ .../heap/BoyerMoore/BM.count.accessible.key | 85 ++++++++++++++++++ key.ui/examples/heap/BoyerMoore/BM.count.key | 84 ++++++++++++++++++ .../examples/heap/BoyerMoore/BM.monoLemma.key | 83 ++++++++++++++++++ .../examples/heap/BoyerMoore/BoyerMoore.key | 2 +- key.ui/examples/heap/BoyerMoore/README.txt | 5 +- .../heap/BoyerMoore/src/BoyerMoore.java | 12 +++ 8 files changed, 360 insertions(+), 12 deletions(-) create mode 100644 key.ui/examples/heap/BoyerMoore/BM.bm.key create mode 100644 key.ui/examples/heap/BoyerMoore/BM.count.accessible.key create mode 100644 key.ui/examples/heap/BoyerMoore/BM.count.key create mode 100644 key.ui/examples/heap/BoyerMoore/BM.monoLemma.key diff --git a/key.core/src/test/java/de/uka/ilkd/key/proof/runallproofs/ProofCollections.java b/key.core/src/test/java/de/uka/ilkd/key/proof/runallproofs/ProofCollections.java index 4ed58e52b79..d30081730ae 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/proof/runallproofs/ProofCollections.java +++ b/key.core/src/test/java/de/uka/ilkd/key/proof/runallproofs/ProofCollections.java @@ -351,15 +351,12 @@ public static ProofCollection automaticJavaDL() throws IOException { g.provable("heap/removeDups/contains.key"); g.provable("heap/removeDups/removeDup.key"); g.provable("heap/saddleback_search/Saddleback_search.key"); - // TODO: Make BoyerMoore run automatically, not only loading proofs. Need proofs scripts for - // that. - g.loadable("heap/BoyerMoore/BM(BM__bm((I)).JML normal_behavior operation contract.0.proof"); - g.loadable( - "heap/BoyerMoore/BM(BM__count((I,_bigint,_bigint)).JML accessible clause.0.proof"); - g.loadable( - "heap/BoyerMoore/BM(BM__count((I,_bigint,_bigint)).JML model_behavior operation contract.0.proof"); - g.loadable( - "heap/BoyerMoore/BM(BM__monoLemma((I,int,int)).JML normal_behavior operation contract.0.proof"); + // DONE: Make BoyerMoore run automatically, not only loading proofs. Need proofs scripts for + // that. YESSS, it runs with scripts now ... + g.provable("heap/BoyerMoore/BM.bm.key"); + g.provable("heap/BoyerMoore/BM.count.accessible.key"); + g.provable("heap/BoyerMoore/BM.count.key"); + g.provable("heap/BoyerMoore/BM.monoLemma.key"); g = c.group("quicksort"); g.setLocalSettings("[Choice]DefaultChoices=moreSeqRules-moreSeqRules:on"); diff --git a/key.ui/examples/heap/BoyerMoore/BM.bm.key b/key.ui/examples/heap/BoyerMoore/BM.bm.key new file mode 100644 index 00000000000..d9bd59e4d27 --- /dev/null +++ b/key.ui/examples/heap/BoyerMoore/BM.bm.key @@ -0,0 +1,86 @@ +\profile "Java Profile"; + +\settings // Proof-Settings-Config-File +{ + "Choice" : { + "JavaCard" : "JavaCard:on", + "Strings" : "Strings:on", + "assertions" : "assertions:on", + "bigint" : "bigint:on", + "floatRules" : "floatRules:strictfpOnly", + "initialisation" : "initialisation:disableStaticInitialisation", + "intRules" : "intRules:arithmeticSemanticsIgnoringOF", + "integerSimplificationRules" : "integerSimplificationRules:full", + "javaLoopTreatment" : "javaLoopTreatment:efficient", + "mergeGenerateIsWeakeningGoal" : "mergeGenerateIsWeakeningGoal:off", + "methodExpansion" : "methodExpansion:modularOnly", + "modelFields" : "modelFields:treatAsAxiom", + "moreSeqRules" : "moreSeqRules:off", + "permissions" : "permissions:off", + "programRules" : "programRules:Java", + "reach" : "reach:on", + "runtimeExceptions" : "runtimeExceptions:ban", + "sequences" : "sequences:on", + "wdChecks" : "wdChecks:off", + "wdOperator" : "wdOperator:L" + }, + "Labels" : { + "UseOriginLabels" : true + }, + "NewSMT" : { + + }, + "SMTSettings" : { + "SelectedTaclets" : [ + + ], + "UseBuiltUniqueness" : false, + "explicitTypeHierarchy" : false, + "instantiateHierarchyAssumptions" : true, + "integersMaximum" : 2147483645, + "integersMinimum" : -2147483645, + "invariantForall" : false, + "maxGenericSorts" : 2, + "useConstantsForBigOrSmallIntegers" : true, + "useUninterpretedMultiplication" : true + }, + "Strategy" : { + "ActiveStrategy" : "JavaCardDLStrategy", + "MaximumNumberOfAutomaticApplications" : 10000, + "Timeout" : -1, + "options" : { + "AUTO_INDUCTION_OPTIONS_KEY" : "AUTO_INDUCTION_OFF", + "BLOCK_OPTIONS_KEY" : "BLOCK_CONTRACT_INTERNAL", + "CLASS_AXIOM_OPTIONS_KEY" : "CLASS_AXIOM_DELAYED", + "DEP_OPTIONS_KEY" : "DEP_ON", + "INF_FLOW_CHECK_PROPERTY" : "INF_FLOW_CHECK_FALSE", + "LOOP_OPTIONS_KEY" : "LOOP_INVARIANT", + "METHOD_OPTIONS_KEY" : "METHOD_CONTRACT", + "MPS_OPTIONS_KEY" : "MPS_MERGE", + "NON_LIN_ARITH_OPTIONS_KEY" : "NON_LIN_ARITH_DEF_OPS", + "OSS_OPTIONS_KEY" : "OSS_ON", + "QUANTIFIERS_OPTIONS_KEY" : "QUANTIFIERS_NON_SPLITTING_WITH_PROGS", + "QUERYAXIOM_OPTIONS_KEY" : "QUERYAXIOM_ON", + "QUERY_NEW_OPTIONS_KEY" : "QUERY_OFF", + "SPLITTING_OPTIONS_KEY" : "SPLITTING_DELAYED", + "STOPMODE_OPTIONS_KEY" : "STOPMODE_DEFAULT", + "SYMBOLIC_EXECUTION_ALIAS_CHECK_OPTIONS_KEY" : "SYMBOLIC_EXECUTION_ALIAS_CHECK_NEVER", + "SYMBOLIC_EXECUTION_NON_EXECUTION_BRANCH_HIDING_OPTIONS_KEY" : "SYMBOLIC_EXECUTION_NON_EXECUTION_BRANCH_HIDING_OFF", + "USER_TACLETS_OPTIONS_KEY1" : "USER_TACLETS_OFF", + "USER_TACLETS_OPTIONS_KEY2" : "USER_TACLETS_OFF", + "USER_TACLETS_OPTIONS_KEY3" : "USER_TACLETS_OFF", + "VBT_PHASE" : "VBT_SYM_EX" + } + } + } + + +\javaSource "src"; + +\proofObligation { + "class" : "de.uka.ilkd.key.proof.init.FunctionalOperationContractPO", + "contract" : "BoyerMoore[BoyerMoore::bm([I)].JML normal_behavior operation contract.0", + "name" : "BoyerMoore[BoyerMoore::bm([I)].JML normal_behavior operation contract.0" + } + +\proofScript { macro "script-auto"; } diff --git a/key.ui/examples/heap/BoyerMoore/BM.count.accessible.key b/key.ui/examples/heap/BoyerMoore/BM.count.accessible.key new file mode 100644 index 00000000000..0c3fdb72aa0 --- /dev/null +++ b/key.ui/examples/heap/BoyerMoore/BM.count.accessible.key @@ -0,0 +1,85 @@ +\profile "Java Profile"; + +\settings // Proof-Settings-Config-File +{ + "Choice" : { + "JavaCard" : "JavaCard:on", + "Strings" : "Strings:on", + "assertions" : "assertions:on", + "bigint" : "bigint:on", + "floatRules" : "floatRules:strictfpOnly", + "initialisation" : "initialisation:disableStaticInitialisation", + "intRules" : "intRules:arithmeticSemanticsIgnoringOF", + "integerSimplificationRules" : "integerSimplificationRules:full", + "javaLoopTreatment" : "javaLoopTreatment:efficient", + "mergeGenerateIsWeakeningGoal" : "mergeGenerateIsWeakeningGoal:off", + "methodExpansion" : "methodExpansion:modularOnly", + "modelFields" : "modelFields:treatAsAxiom", + "moreSeqRules" : "moreSeqRules:off", + "permissions" : "permissions:off", + "programRules" : "programRules:Java", + "reach" : "reach:on", + "runtimeExceptions" : "runtimeExceptions:ban", + "sequences" : "sequences:on", + "wdChecks" : "wdChecks:off", + "wdOperator" : "wdOperator:L" + }, + "Labels" : { + "UseOriginLabels" : true + }, + "NewSMT" : { + + }, + "SMTSettings" : { + "SelectedTaclets" : [ + + ], + "UseBuiltUniqueness" : false, + "explicitTypeHierarchy" : false, + "instantiateHierarchyAssumptions" : true, + "integersMaximum" : 2147483645, + "integersMinimum" : -2147483645, + "invariantForall" : false, + "maxGenericSorts" : 2, + "useConstantsForBigOrSmallIntegers" : true, + "useUninterpretedMultiplication" : true + }, + "Strategy" : { + "ActiveStrategy" : "JavaCardDLStrategy", + "MaximumNumberOfAutomaticApplications" : 10000, + "Timeout" : -1, + "options" : { + "AUTO_INDUCTION_OPTIONS_KEY" : "AUTO_INDUCTION_OFF", + "BLOCK_OPTIONS_KEY" : "BLOCK_CONTRACT_INTERNAL", + "CLASS_AXIOM_OPTIONS_KEY" : "CLASS_AXIOM_DELAYED", + "DEP_OPTIONS_KEY" : "DEP_ON", + "INF_FLOW_CHECK_PROPERTY" : "INF_FLOW_CHECK_FALSE", + "LOOP_OPTIONS_KEY" : "LOOP_INVARIANT", + "METHOD_OPTIONS_KEY" : "METHOD_CONTRACT", + "MPS_OPTIONS_KEY" : "MPS_MERGE", + "NON_LIN_ARITH_OPTIONS_KEY" : "NON_LIN_ARITH_DEF_OPS", + "OSS_OPTIONS_KEY" : "OSS_ON", + "QUANTIFIERS_OPTIONS_KEY" : "QUANTIFIERS_NON_SPLITTING_WITH_PROGS", + "QUERYAXIOM_OPTIONS_KEY" : "QUERYAXIOM_ON", + "QUERY_NEW_OPTIONS_KEY" : "QUERY_OFF", + "SPLITTING_OPTIONS_KEY" : "SPLITTING_DELAYED", + "STOPMODE_OPTIONS_KEY" : "STOPMODE_DEFAULT", + "SYMBOLIC_EXECUTION_ALIAS_CHECK_OPTIONS_KEY" : "SYMBOLIC_EXECUTION_ALIAS_CHECK_NEVER", + "SYMBOLIC_EXECUTION_NON_EXECUTION_BRANCH_HIDING_OPTIONS_KEY" : "SYMBOLIC_EXECUTION_NON_EXECUTION_BRANCH_HIDING_OFF", + "USER_TACLETS_OPTIONS_KEY1" : "USER_TACLETS_OFF", + "USER_TACLETS_OPTIONS_KEY2" : "USER_TACLETS_OFF", + "USER_TACLETS_OPTIONS_KEY3" : "USER_TACLETS_OFF", + "VBT_PHASE" : "VBT_SYM_EX" + } + } + } + + +\javaSource "src"; + +\proofObligation { + "class" : "de.uka.ilkd.key.proof.init.DependencyContractPO", + "contract" : "BoyerMoore[BoyerMoore::count([I,\bigint,\bigint)].JML accessible clause.0", + "name" : "BoyerMoore[BoyerMoore::count([I,\bigint,\bigint)].JML accessible clause.0" + } + diff --git a/key.ui/examples/heap/BoyerMoore/BM.count.key b/key.ui/examples/heap/BoyerMoore/BM.count.key new file mode 100644 index 00000000000..52ef9b4383b --- /dev/null +++ b/key.ui/examples/heap/BoyerMoore/BM.count.key @@ -0,0 +1,84 @@ +\profile "Java Profile"; + +\settings // Proof-Settings-Config-File +{ + "Choice" : { + "JavaCard" : "JavaCard:on", + "Strings" : "Strings:on", + "assertions" : "assertions:on", + "bigint" : "bigint:on", + "floatRules" : "floatRules:strictfpOnly", + "initialisation" : "initialisation:disableStaticInitialisation", + "intRules" : "intRules:arithmeticSemanticsIgnoringOF", + "integerSimplificationRules" : "integerSimplificationRules:full", + "javaLoopTreatment" : "javaLoopTreatment:efficient", + "mergeGenerateIsWeakeningGoal" : "mergeGenerateIsWeakeningGoal:off", + "methodExpansion" : "methodExpansion:modularOnly", + "modelFields" : "modelFields:treatAsAxiom", + "moreSeqRules" : "moreSeqRules:off", + "permissions" : "permissions:off", + "programRules" : "programRules:Java", + "reach" : "reach:on", + "runtimeExceptions" : "runtimeExceptions:ban", + "sequences" : "sequences:on", + "wdChecks" : "wdChecks:off", + "wdOperator" : "wdOperator:L" + }, + "Labels" : { + "UseOriginLabels" : true + }, + "NewSMT" : { + + }, + "SMTSettings" : { + "SelectedTaclets" : [ + + ], + "UseBuiltUniqueness" : false, + "explicitTypeHierarchy" : false, + "instantiateHierarchyAssumptions" : true, + "integersMaximum" : 2147483645, + "integersMinimum" : -2147483645, + "invariantForall" : false, + "maxGenericSorts" : 2, + "useConstantsForBigOrSmallIntegers" : true, + "useUninterpretedMultiplication" : true + }, + "Strategy" : { + "ActiveStrategy" : "JavaCardDLStrategy", + "MaximumNumberOfAutomaticApplications" : 10000, + "Timeout" : -1, + "options" : { + "AUTO_INDUCTION_OPTIONS_KEY" : "AUTO_INDUCTION_OFF", + "BLOCK_OPTIONS_KEY" : "BLOCK_CONTRACT_INTERNAL", + "CLASS_AXIOM_OPTIONS_KEY" : "CLASS_AXIOM_DELAYED", + "DEP_OPTIONS_KEY" : "DEP_ON", + "INF_FLOW_CHECK_PROPERTY" : "INF_FLOW_CHECK_FALSE", + "LOOP_OPTIONS_KEY" : "LOOP_INVARIANT", + "METHOD_OPTIONS_KEY" : "METHOD_CONTRACT", + "MPS_OPTIONS_KEY" : "MPS_MERGE", + "NON_LIN_ARITH_OPTIONS_KEY" : "NON_LIN_ARITH_DEF_OPS", + "OSS_OPTIONS_KEY" : "OSS_ON", + "QUANTIFIERS_OPTIONS_KEY" : "QUANTIFIERS_NON_SPLITTING_WITH_PROGS", + "QUERYAXIOM_OPTIONS_KEY" : "QUERYAXIOM_ON", + "QUERY_NEW_OPTIONS_KEY" : "QUERY_OFF", + "SPLITTING_OPTIONS_KEY" : "SPLITTING_DELAYED", + "STOPMODE_OPTIONS_KEY" : "STOPMODE_DEFAULT", + "SYMBOLIC_EXECUTION_ALIAS_CHECK_OPTIONS_KEY" : "SYMBOLIC_EXECUTION_ALIAS_CHECK_NEVER", + "SYMBOLIC_EXECUTION_NON_EXECUTION_BRANCH_HIDING_OPTIONS_KEY" : "SYMBOLIC_EXECUTION_NON_EXECUTION_BRANCH_HIDING_OFF", + "USER_TACLETS_OPTIONS_KEY1" : "USER_TACLETS_OFF", + "USER_TACLETS_OPTIONS_KEY2" : "USER_TACLETS_OFF", + "USER_TACLETS_OPTIONS_KEY3" : "USER_TACLETS_OFF", + "VBT_PHASE" : "VBT_SYM_EX" + } + } + } + + +\javaSource "src"; + +\proofObligation { + "class" : "de.uka.ilkd.key.proof.init.FunctionalOperationContractPO", + "contract" : "BoyerMoore[BoyerMoore::count([I,\bigint,\bigint)].JML model_behavior operation contract.0", + "name" : "BoyerMoore[BoyerMoore::count([I,\bigint,\bigint)].JML model_behavior operation contract.0" + } diff --git a/key.ui/examples/heap/BoyerMoore/BM.monoLemma.key b/key.ui/examples/heap/BoyerMoore/BM.monoLemma.key new file mode 100644 index 00000000000..e4d85019478 --- /dev/null +++ b/key.ui/examples/heap/BoyerMoore/BM.monoLemma.key @@ -0,0 +1,83 @@ +\profile "Java Profile"; + +\settings // Proof-Settings-Config-File +{ + "Choice" : { + "JavaCard" : "JavaCard:on", + "Strings" : "Strings:on", + "assertions" : "assertions:on", + "bigint" : "bigint:on", + "floatRules" : "floatRules:strictfpOnly", + "initialisation" : "initialisation:disableStaticInitialisation", + "intRules" : "intRules:arithmeticSemanticsIgnoringOF", + "integerSimplificationRules" : "integerSimplificationRules:full", + "javaLoopTreatment" : "javaLoopTreatment:efficient", + "mergeGenerateIsWeakeningGoal" : "mergeGenerateIsWeakeningGoal:off", + "methodExpansion" : "methodExpansion:modularOnly", + "modelFields" : "modelFields:treatAsAxiom", + "moreSeqRules" : "moreSeqRules:off", + "permissions" : "permissions:off", + "programRules" : "programRules:Java", + "reach" : "reach:on", + "runtimeExceptions" : "runtimeExceptions:ban", + "sequences" : "sequences:on", + "wdChecks" : "wdChecks:off", + "wdOperator" : "wdOperator:L" + }, + "Labels" : { + "UseOriginLabels" : true + }, + "NewSMT" : { + + }, + "SMTSettings" : { + "SelectedTaclets" : [ + + ], + "UseBuiltUniqueness" : false, + "explicitTypeHierarchy" : false, + "instantiateHierarchyAssumptions" : true, + "integersMaximum" : 2147483645, + "integersMinimum" : -2147483645, + "invariantForall" : false, + "maxGenericSorts" : 2, + "useConstantsForBigOrSmallIntegers" : true, + "useUninterpretedMultiplication" : true + }, + "Strategy" : { + "ActiveStrategy" : "JavaCardDLStrategy", + "MaximumNumberOfAutomaticApplications" : 10000, + "Timeout" : -1, + "options" : { + "AUTO_INDUCTION_OPTIONS_KEY" : "AUTO_INDUCTION_OFF", + "BLOCK_OPTIONS_KEY" : "BLOCK_CONTRACT_INTERNAL", + "CLASS_AXIOM_OPTIONS_KEY" : "CLASS_AXIOM_DELAYED", + "DEP_OPTIONS_KEY" : "DEP_ON", + "INF_FLOW_CHECK_PROPERTY" : "INF_FLOW_CHECK_FALSE", + "LOOP_OPTIONS_KEY" : "LOOP_INVARIANT", + "METHOD_OPTIONS_KEY" : "METHOD_CONTRACT", + "MPS_OPTIONS_KEY" : "MPS_MERGE", + "NON_LIN_ARITH_OPTIONS_KEY" : "NON_LIN_ARITH_DEF_OPS", + "OSS_OPTIONS_KEY" : "OSS_ON", + "QUANTIFIERS_OPTIONS_KEY" : "QUANTIFIERS_NON_SPLITTING_WITH_PROGS", + "QUERYAXIOM_OPTIONS_KEY" : "QUERYAXIOM_ON", + "QUERY_NEW_OPTIONS_KEY" : "QUERY_OFF", + "SPLITTING_OPTIONS_KEY" : "SPLITTING_DELAYED", + "STOPMODE_OPTIONS_KEY" : "STOPMODE_DEFAULT", + "SYMBOLIC_EXECUTION_ALIAS_CHECK_OPTIONS_KEY" : "SYMBOLIC_EXECUTION_ALIAS_CHECK_NEVER", + "SYMBOLIC_EXECUTION_NON_EXECUTION_BRANCH_HIDING_OPTIONS_KEY" : "SYMBOLIC_EXECUTION_NON_EXECUTION_BRANCH_HIDING_OFF", + "USER_TACLETS_OPTIONS_KEY1" : "USER_TACLETS_OFF", + "USER_TACLETS_OPTIONS_KEY2" : "USER_TACLETS_OFF", + "USER_TACLETS_OPTIONS_KEY3" : "USER_TACLETS_OFF", + "VBT_PHASE" : "VBT_SYM_EX" + } + } + } + +\javaSource "src"; + +\proofObligation { + "class" : "de.uka.ilkd.key.proof.init.FunctionalOperationContractPO", + "contract" : "BoyerMoore[BoyerMoore::monoLemma([I,int,int)].JML normal_behavior operation contract.0", + "name" : "BoyerMoore[BoyerMoore::monoLemma([I,int,int)].JML normal_behavior operation contract.0" + } diff --git a/key.ui/examples/heap/BoyerMoore/BoyerMoore.key b/key.ui/examples/heap/BoyerMoore/BoyerMoore.key index 5aa881eb14a..b54ea73969f 100644 --- a/key.ui/examples/heap/BoyerMoore/BoyerMoore.key +++ b/key.ui/examples/heap/BoyerMoore/BoyerMoore.key @@ -51,7 +51,7 @@ "options" : { "AUTO_INDUCTION_OPTIONS_KEY" : "AUTO_INDUCTION_OFF", "BLOCK_OPTIONS_KEY" : "BLOCK_CONTRACT_INTERNAL", - "CLASS_AXIOM_OPTIONS_KEY" : "CLASS_AXIOM_OFF", + "CLASS_AXIOM_OPTIONS_KEY" : "CLASS_AXIOM_DELAYED", "DEP_OPTIONS_KEY" : "DEP_ON", "INF_FLOW_CHECK_PROPERTY" : "INF_FLOW_CHECK_FALSE", "LOOP_OPTIONS_KEY" : "LOOP_INVARIANT", diff --git a/key.ui/examples/heap/BoyerMoore/README.txt b/key.ui/examples/heap/BoyerMoore/README.txt index f56ae2a608a..9c748ddbecd 100644 --- a/key.ui/examples/heap/BoyerMoore/README.txt +++ b/key.ui/examples/heap/BoyerMoore/README.txt @@ -15,8 +15,9 @@ entries in the array hold m. Suggested by J.C. Filliâtre as an example during VerifyThis 24. -Currently the proofs do not go through automatically, the proof -files are checked in with the example. +Originally, the proofs did not go through automatically, with JML +proof scripts they now succeed. + @see https://en.wikipedia.org/wiki/Boyer-Moore_majority_vote_algorithm @author Mattias Ulbrich diff --git a/key.ui/examples/heap/BoyerMoore/src/BoyerMoore.java b/key.ui/examples/heap/BoyerMoore/src/BoyerMoore.java index dc8a350ff1a..abe99b428b1 100644 --- a/key.ui/examples/heap/BoyerMoore/src/BoyerMoore.java +++ b/key.ui/examples/heap/BoyerMoore/src/BoyerMoore.java @@ -59,11 +59,23 @@ public IntOpt bm(int[] a) { if(mc == 0) { mc = 1; mx = a[k]; + /*@ assert count(a, k+1, a[k]) <= count(a, k, a[k]) + 1 \by { + @ oss; + @ expand on: "self.count(a, k_0 + 1, a[k_0])"; + @ auto classAxioms:false; + @ }*/ } else if(mx == a[k]) { mc++; } else { mc--; } + /*@ assert (\forall int x; x != mx; 2 * count(a, k+1, x) <= k+1 - mc) \by { + @ oss; + @ obtain int x \from_goal; + @ expand on: "self.count(a, k_0 + 1, x)"; + @ instantiate var:"x" with: x; + @ auto classAxioms:false; + @ }*/ } if(mc == 0) return IntOpt.NONE; From 4cdfdd9eece4e77458aed8ae14c9b795add8a8be Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 3 Oct 2025 19:13:09 +0200 Subject: [PATCH 48/90] improved document generation --- .../key/scripts/DocumentationGenerator.java | 67 ++++++++----------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java index fab732e5206..7b42ca2e2b1 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java @@ -1,32 +1,15 @@ package de.uka.ilkd.key.scripts; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import de.uka.ilkd.key.control.DefaultUserInterfaceControl; -import de.uka.ilkd.key.control.KeYEnvironment; -import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.util.KeYResourceManager; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; + import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; public class DocumentationGenerator { + private static String branch; + private static String sha1; + public static void main(String[] args) throws FileNotFoundException { if(args.length > 0) { @@ -36,15 +19,15 @@ public static void main(String[] args) throws FileNotFoundException { printHeader(); - Collection commands = ProofScriptEngine.loadCommands().values(); - Map> commandsByCategory = new TreeMap<>(); + Set> commands = ProofScriptEngine.loadCommands().entrySet(); + Map>> commandsByCategory = new TreeMap<>(); - for (ProofScriptCommand command : commands) { - String category = command.getCategory(); + for (Map.Entry entry : commands) { + String category = entry.getValue().getCategory(); if (category == null) { category = "Uncategorized"; } - commandsByCategory.computeIfAbsent(category, k -> new ArrayList<>()).add(command); + commandsByCategory.computeIfAbsent(category, k -> new ArrayList<>()).add(entry); } List categories = new ArrayList<>(commandsByCategory.keySet()); @@ -60,9 +43,9 @@ public static void main(String[] args) throws FileNotFoundException { } private static void printHeader() { - String branch = KeYResourceManager.getManager().getBranch(); + branch = KeYResourceManager.getManager().getBranch(); String version = KeYResourceManager.getManager().getVersion(); - String sha1 = KeYResourceManager.getManager().getSHA1(); + sha1 = KeYResourceManager.getManager().getSHA1(); // This gets too technical. But this is for the key-docs repository. ... System.out.printf(""" @@ -90,19 +73,25 @@ private static void printHeader() { """, new Date(), branch, version, sha1); } - private static void listCategory(String category, List proofScriptCommands) { - proofScriptCommands.sort(Comparator.comparing(ProofScriptCommand::getName)); - Set alreadyListed = new HashSet<>(); + private static void listCategory(String category, List> proofScriptCommands) { + proofScriptCommands.sort(Map.Entry.comparingByKey()); System.out.println("\n## Category *" + category + "*\n"); - for (ProofScriptCommand command : proofScriptCommands) { - if(alreadyListed.contains(command)) - continue; - alreadyListed.add(command); + for (Map.Entry entry : proofScriptCommands) { System.out.println("
    \n"); - System.out.println("### Command `" + command.getName() + "`\n"); - System.out.println(command.getDocumentation() + "\n"); - if(command.getAliases().size() > 1) { - System.out.println("#### Aliases:\n" + String.join(", ", command.getAliases()) + "\n"); + if(entry.getKey().equals(entry.getValue().getName())) { + ProofScriptCommand command = entry.getValue(); + String link = "main".equals(branch) ? "main" : sha1; + System.out.printf("### [Source](https://github.com/KeYProject/key/blob/%s/key.core/src/main/java/%s.java)", + link, + command.getClass().getName().replace('.', '/') ); + System.out.println(" Command `" + command.getName() + "`\n\n"); + System.out.println(command.getDocumentation() + "\n"); + if (command.getAliases().size() > 1) { + System.out.println("#### Aliases:\n" + String.join(", ", command.getAliases()) + "\n"); + } + } else { + System.out.println("### Command `" + entry.getKey() + "`\n"); + System.out.println("Alias for command [\u2192 `" + entry.getValue().getName() + "`](#command-" + entry.getValue().getName() + ")\n"); } } } From f052e3507238b15e8552012929919dbd2718ff56 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sat, 4 Oct 2025 01:16:36 +0200 Subject: [PATCH 49/90] more proof script power, update inlining. --- .../ilkd/key/macros/ApplyScriptsMacro.java | 49 +++++++++++++----- .../key/scripts/OneStepSimplifierCommand.java | 50 ++++++++++++------- .../ilkd/key/scripts/meta/ValueInjector.java | 10 ++-- .../key/scripts/DocumentationGenerator.java | 4 +- .../uka/ilkd/key/scripts/JmlScriptTest.java | 22 +++++++- .../key/scripts/jml/ObtainWithUpdates.java | 23 +++++++++ 6 files changed, 118 insertions(+), 40 deletions(-) create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/ObtainWithUpdates.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 56c24a237f1..394c32c4e0d 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -17,9 +17,7 @@ import de.uka.ilkd.key.logic.DefaultVisitor; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.JavaBlock; -import de.uka.ilkd.key.logic.op.JFunction; -import de.uka.ilkd.key.logic.op.LocationVariable; -import de.uka.ilkd.key.logic.op.UpdateApplication; +import de.uka.ilkd.key.logic.op.*; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.parser.Location; import de.uka.ilkd.key.proof.*; @@ -120,15 +118,34 @@ private static JmlAssert getJmlAssert(Node node) { return null; } - private static @Nullable JTerm getUpdate(Goal goal) { + private static @Nullable OpReplacer getUpdateReplacer(Goal goal) { RuleApp ruleApp = goal.node().parent().getAppliedRuleApp(); Term appliedOn = ruleApp.posInOccurrence().subTerm(); if (appliedOn.op() instanceof UpdateApplication) { - return UpdateApplication.getUpdate((JTerm) appliedOn); + var update = UpdateApplication.getUpdate((JTerm) appliedOn); + Map updates = new HashMap<>(); + Services services = goal.proof().getServices(); + collectUpdates(update, updates, services); + return new OpReplacer(updates, services.getTermFactory()); } return null; } + private static void collectUpdates(JTerm update, Map updates, Services services) { + switch(update.op()) { + case ElementaryUpdate eu -> + updates.put(services.getTermBuilder().var((ProgramVariable) eu.lhs()), update.sub(0)); + + case UpdateJunctor uj-> { + collectUpdates(update.sub(0), updates, services); + collectUpdates(update.sub(1), updates, services); + } + + default -> + throw new IllegalStateException("Unexpected update operation: " + update.op().getClass()); + } + } + private static JavaBlock getJavaBlock(Goal goal) { RuleApp ruleApp = goal.node().parent().getAppliedRuleApp(); JTerm appliedOn = (JTerm) ruleApp.posInOccurrence().subTerm(); @@ -162,9 +179,9 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, Map termMap = getTermMap(jmlAssert, getJavaBlock(goal), proof.getServices()); // We heavily rely on that variables have been computed before, otherwise this will raise an NPE. Map obtainMap = makeObtainVarMap(jmlAssert.collectVariablesInProof(null)); - JTerm update = getUpdate(goal); + @Nullable OpReplacer updateReplacer = getUpdateReplacer(goal); List renderedProof = - renderProof(proofScript, termMap, update, proof.getServices()); + renderProof(proofScript, termMap, updateReplacer, proof.getServices()); ProofScriptEngine pse = new ProofScriptEngine(proof); pse.setInitiallySelectedGoal(goal); pse.getStateMap().putUserData("jml.obtainVarMap", obtainMap); @@ -235,7 +252,7 @@ private Map makeObtainVarMap(ImmutableList renderProof(KeyAst.JMLProofScript script, - Map termMap, JTerm update, Services services) throws ScriptException { + Map termMap, @Nullable OpReplacer update, Services services) throws ScriptException { List result = new ArrayList<>(); // Push current settings onto the settings stack result.add(new ScriptCommandAst("set", Map.of("stack", "push"), List.of())); @@ -248,9 +265,13 @@ private static List renderProof(KeyAst.JMLProofScript script, } private static List renderProofCmd(ProofCmdContext ctx, - Map termMap, JTerm update, Services services) throws ScriptException { + Map termMap, + @Nullable OpReplacer update, Services services) throws ScriptException { List result = new ArrayList<>(); + // Prepare by resolving the update + result.add(new ScriptCommandAst("oss", Map.of("recentOnly", true), List.of())); + // Push the current branch context result.add(new ScriptCommandAst("branches", Map.of(), List.of("push"))); @@ -290,7 +311,8 @@ private static List renderProofCmd(ProofCmdContext ctx, return result; } - private static ScriptCommandAst renderObtainCommand(ProofCmdContext ctx, Map termMap, JTerm update, Services services) throws ScriptException { + private static ScriptCommandAst renderObtainCommand(ProofCmdContext ctx, Map termMap, + @Nullable OpReplacer update, Services services) throws ScriptException { Map named = new HashMap<>(); String argName = switch(ctx.obtKind.getType()) { @@ -313,7 +335,7 @@ private static ScriptCommandAst renderObtainCommand(ProofCmdContext ctx, Map termMap, JTerm update, Services services) { + private static @NonNull ScriptCommandAst renderRegularCommand(ProofCmdContext ctx, Map termMap, @Nullable OpReplacer update, Services services) { Map named = new HashMap<>(); List positional = new ArrayList<>(); for (ProofArgContext argContext : ctx.proofArg()) { @@ -334,7 +356,7 @@ private static ScriptCommandAst renderObtainCommand(ProofCmdContext ctx, Map builtins = goal.ruleAppIndex().getBuiltInRules(goal, - new PosInOccurrence(sf, PosInTerm.getTopLevel(), true)); - for (IBuiltInRuleApp builtin : builtins) { - if (builtin instanceof OneStepSimplifierRuleApp) { - goal.apply(builtin); - } - } - } + + if(Boolean.TRUE.equals(arguments.recentOnly)) { + SequentChangeInfo sci = goal.node().getNodeInfo().getSequentChangeInfo(); + var ante = sci.addedFormulas(true) + .prepend(sci.modifiedFormulas(true).map(FormulaChangeInfo::newFormula)); + applyOSS(ante, goal, true); + + var succ = sci.addedFormulas(false) + .prepend(sci.modifiedFormulas(false).map(FormulaChangeInfo::newFormula)); + applyOSS(succ, goal, false); + return; } - if (arguments.succedent) { - for (SequentFormula sf : goal.sequent().succedent()) { + if (Boolean.TRUE.equals(arguments.antecedent)) { + applyOSS(goal.sequent().antecedent(), goal, true); + } + + if (Boolean.TRUE.equals(arguments.succedent)) { + applyOSS(goal.sequent().succedent(), goal, false); + } + } + + + private static void applyOSS(Iterable antecedent, Goal goal, boolean inAntec) { + for (SequentFormula sf : antecedent) { ImmutableList builtins = goal.ruleAppIndex().getBuiltInRules(goal, - new PosInOccurrence(sf, PosInTerm.getTopLevel(), false)); + new PosInOccurrence(sf, PosInTerm.getTopLevel(), inAntec)); for (IBuiltInRuleApp builtin : builtins) { if (builtin instanceof OneStepSimplifierRuleApp) { goal.apply(builtin); @@ -56,9 +67,9 @@ public void execute(ScriptCommandAst command) throws ScriptException, Interrupte } } } - } - @Documentation(category = "Fundamental", value = """ + + @Documentation(category = "Fundamental", value = """ The oss command applies the *one step simplifier* on the current proof goal. This simplifier applies a set of built-in simplification rules to the formulas in the sequent. It can be configured to apply the one step simplifier only on the antecedent or succedent. @@ -74,5 +85,10 @@ public static class Parameters { "this option to false. Default is true.") @Option(value = "succedent") public @Nullable Boolean succedent = Boolean.TRUE; + + @Documentation("Limit the application to the recently added or changed formulas. Deactivates the " + + "antecedent and succedent options.") + @Flag("recentOnly") + public @Nullable Boolean recentOnly = Boolean.FALSE; } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java index 8300997d66e..d4a33e16384 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java @@ -140,10 +140,10 @@ public T inject(T obj, ScriptCommandAst arguments) .findAny(); if (unhandled.isPresent()) { throw new UnknownArgumentException(String.format( - "Unknown argument %s (with value %s) was provided. For command class: '%s'", + "Unknown option %s (with value %s) was provided. For command: '%s'", unhandled.get(), arguments.namedArgs().get(unhandled.get()), - obj.getClass().getName())); + arguments.commandName())); } Optional unhandledPos = IntegerUtil.indexRangeOf(arguments.positionalArgs()) @@ -151,11 +151,9 @@ public T inject(T obj, ScriptCommandAst arguments) .filter(it -> !handledOptions.contains(it)) .findAny(); if (unhandledPos.isPresent()) { - long count = handledOptions.stream().filter(it -> it instanceof Integer).count(); throw new UnknownArgumentException(String.format( - "Unexpected positional argument at index %d was provided. " + - "Expected (at most) %d positional arguments. For command class: '%s'", - unhandledPos.get(), count, obj.getClass().getName())); + "Unexpected positional argument or flag provided: %s", + arguments.positionalArgs().get(unhandledPos.get()))); } if (obj instanceof VerifyableParameters vp) { diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java index 7b42ca2e2b1..a70ab5de2ed 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java @@ -81,10 +81,10 @@ private static void listCategory(String category, List[Source](https://github.com/KeYProject/key/blob/%s/key.core/src/main/java/%s.java)", + System.out.println("### Command `" + command.getName() + "`\n\n"); + System.out.printf("[Source](https://github.com/KeYProject/key/blob/%s/key.core/src/main/java/%s.java)\n\n", link, command.getClass().getName().replace('.', '/') ); - System.out.println(" Command `" + command.getName() + "`\n\n"); System.out.println(command.getDocumentation() + "\n"); if (command.getAliases().size() > 1) { System.out.println("#### Aliases:\n" + String.join(", ", command.getAliases()) + "\n"); diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java index 9dc7b9cbab3..75e91b84328 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java @@ -7,6 +7,8 @@ import de.uka.ilkd.key.control.UserInterfaceControl; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.proof.io.ProblemLoaderControl; +import de.uka.ilkd.key.proof.io.ProofSaver; +import de.uka.ilkd.key.util.KeYConstants; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -24,6 +26,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Comparator; +import java.util.Map; import java.util.logging.LogManager; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -35,6 +38,7 @@ public class JmlScriptTest { // Set this to a specific case to only run that case for debugging private static final String ONLY_CASE = null; + private static final boolean SAVE_PROOF = true; static { URL url = JmlScriptTest.class.getResource("jml/project.key"); @@ -57,12 +61,25 @@ public void testJmlScript(Path path, String identifier) throws Exception { Path projectFile = tmpDir.resolve("project.key"); Files.copy(KEY_FILE, projectFile); KeYEnvironment env = KeYEnvironment.load(projectFile); + if(params.settings != null && !params.settings.isEmpty()) { + for (Map.Entry entry : params.settings.entrySet()) { + env.getLoadedProof().getSettings().getStrategySettings().getActiveStrategyProperties() + .setProperty(entry.getKey(), entry.getValue()); + } + } KeyAst.ProofScript script = env.getProofScript(); if (script != null) { ProofScriptEngine pse = new ProofScriptEngine(env.getLoadedProof()); pse.execute(env.getUi(), script); } + if(SAVE_PROOF) { + String filename = tmpDir.resolve("saved.proof").toString(); + ProofSaver saver = new ProofSaver(env.getLoadedProof(), filename, KeYConstants.INTERNAL_VERSION); + saver.save(); + LOGGER.info("Saved proof to {}", filename); + } + if(params.shouldClose) { Assertions.assertTrue(env.getLoadedProof().closed(), "Proof did not close."); } else { @@ -70,7 +87,7 @@ public void testJmlScript(Path path, String identifier) throws Exception { } } finally { // Uncomment the following line to delete the temporary directory after the test - if(params.deleteTmpDir) { + if(params.deleteTmpDir && !SAVE_PROOF) { LOGGER.info("Deleting temporary directory: {}", tmpDir); Files.walk(tmpDir).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); } else { @@ -81,7 +98,7 @@ public void testJmlScript(Path path, String identifier) throws Exception { } private static Parameters readParams(Path path) throws IOException { - String input = Files.lines(path).filter(l -> l.startsWith("//!")).map(l -> l.substring(3).trim()) + String input = Files.lines(path).filter(l -> l.startsWith("//!")).map(l -> l.substring(3)) .collect(Collectors.joining("\n")).trim(); if(input.isEmpty()) { return new Parameters(); @@ -107,6 +124,7 @@ static class Parameters { public String method; public String exception; public boolean deleteTmpDir = true; + public Map settings; } diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/ObtainWithUpdates.java b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/ObtainWithUpdates.java new file mode 100644 index 00000000000..c85eebaee8b --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/ObtainWithUpdates.java @@ -0,0 +1,23 @@ +//! settings: +//! CLASS_AXIOM_OPTIONS_KEY: CLASS_AXIOM_OFF + +class Test { + + //@ model int f(int arg) { return arg + arg; } + + int field; + + //@ ensures true; + void test() { + + field = 21; + int local = 42; + + /*@ assert f(field) == local \by { + expand on: f(field); + auto; + // Still too verbose on auto + } */ + } + +} From 3655415ce9c501f2765f66f940c09bdfe44619e3 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sat, 4 Oct 2025 03:00:58 +0200 Subject: [PATCH 50/90] making sure that cuts work with boolean terms instead of formulas. --- .../de/uka/ilkd/key/scripts/CutCommand.java | 5 ++-- .../key/scripts/jml/AssertedModelMethod.java | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AssertedModelMethod.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java index 7ef35b44fe7..cc89557ddcc 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java @@ -51,8 +51,9 @@ static void execute(EngineState state, Parameters args) throws ScriptException { TacletApp app = NoPosTacletApp.createNoPosTacletApp(cut); SchemaVariable sv = app.uninstantiatedVars().iterator().next(); - app = app.addCheckedInstantiation(sv, args.formula, - state.getProof().getServices(), true); + var formula = state.getProof().getServices().getTermBuilder().convertToFormula(args.formula); + + app = app.addCheckedInstantiation(sv, formula, state.getProof().getServices(), true); state.getFirstOpenAutomaticGoal().apply(app); } diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AssertedModelMethod.java b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AssertedModelMethod.java new file mode 100644 index 00000000000..f10551b4dc7 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AssertedModelMethod.java @@ -0,0 +1,23 @@ +//! settings: +//! CLASS_AXIOM_OPTIONS_KEY: CLASS_AXIOM_OFF + +// Was a bug: +// Instantiation Test::pred(heap,self,int::select(heap,self,Test::$f)) of cutFormula (formula) does not satisfy the variable conditions + +class Test { + + //@ model boolean pred(int x) { return x > 20; } + + int f; + + //@ requires pred(f); + //@ ensures f > 2; + void test() { + int x; + /*@ assert f > 2 \by { + assert pred(f) \by { auto classAxioms:true; } + auto; // should not be necessary ... eventually removed + } */ + } + +} From c947f7b47568e3156a12998d2692c3cfad81d75a Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sun, 5 Oct 2025 18:03:25 +0200 Subject: [PATCH 51/90] better symbex-only machine --- .../ilkd/key/macros/ApplyScriptsMacro.java | 5 +- .../uka/ilkd/key/macros/ScriptAwareMacro.java | 2 +- .../ilkd/key/macros/ScriptAwarePrepMacro.java | 2 +- .../macros/SymbolicExecutionOnlyMacro.java | 149 ++++++++++++++++++ .../de.uka.ilkd.key.macros.ProofMacro | 1 + ...licExecutionOnlyMacro.admittedRuleSets.txt | 8 + ...mbolicExecutionOnlyMacro.admittedRules.txt | 4 + 7 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java create mode 100644 key.core/src/main/resources/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.admittedRuleSets.txt create mode 100644 key.core/src/main/resources/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.admittedRules.txt diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 394c32c4e0d..e9987ca40f1 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -256,6 +256,8 @@ private static List renderProof(KeyAst.JMLProofScript script, List result = new ArrayList<>(); // Push current settings onto the settings stack result.add(new ScriptCommandAst("set", Map.of("stack", "push"), List.of())); + // Prepare by resolving the update + result.add(new ScriptCommandAst("oss", Map.of("recentOnly", true), List.of())); for (ProofCmdContext proofCmdContext : script.ctx.proofCmd()) { result.addAll(renderProofCmd(proofCmdContext, termMap, update, services)); } @@ -269,9 +271,6 @@ private static List renderProofCmd(ProofCmdContext ctx, @Nullable OpReplacer update, Services services) throws ScriptException { List result = new ArrayList<>(); - // Prepare by resolving the update - result.add(new ScriptCommandAst("oss", Map.of("recentOnly", true), List.of())); - // Push the current branch context result.add(new ScriptCommandAst("branches", Map.of(), List.of("push"))); diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java index 320e7d57c22..397118f2ca8 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwareMacro.java @@ -34,7 +34,7 @@ */ public class ScriptAwareMacro extends SequentialProofMacro { - private final ProofMacro autoMacro = new FinishSymbolicExecutionMacro(); + private final ProofMacro autoMacro = new SymbolicExecutionOnlyMacro(); // FinishSymbolicExecutionMacro(); private final ApplyScriptsMacro applyMacro = new ApplyScriptsMacro(new TryCloseMacro()); @Override diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwarePrepMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwarePrepMacro.java index 175f5fa8e51..d2e40deb16b 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwarePrepMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ScriptAwarePrepMacro.java @@ -34,7 +34,7 @@ */ public class ScriptAwarePrepMacro extends SequentialProofMacro { - private final ProofMacro autoMacro = new FinishSymbolicExecutionMacro(); + private final ProofMacro autoMacro = new SymbolicExecutionOnlyMacro(); // new FinishSymbolicExecutionMacro(); private final ApplyScriptsMacro applyMacro = new ApplyScriptsMacro(null); @Override diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java new file mode 100644 index 00000000000..c47a4b958f1 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java @@ -0,0 +1,149 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.macros; + +import de.uka.ilkd.key.control.TermLabelVisibilityManager; +import de.uka.ilkd.key.logic.op.ObserverFunction; +import de.uka.ilkd.key.logic.op.UpdateApplication; +import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.rule.*; +import de.uka.ilkd.key.strategy.Strategy; +import org.jspecify.annotations.NonNull; +import org.key_project.logic.Name; +import org.key_project.logic.Term; +import org.key_project.logic.op.Modality; +import org.key_project.prover.rules.Rule; +import org.key_project.prover.rules.RuleApp; +import org.key_project.prover.rules.RuleSet; +import org.key_project.prover.sequent.PosInOccurrence; +import org.key_project.prover.strategy.costbased.NumberRuleAppCost; +import org.key_project.util.Streams; +import org.key_project.util.java.StringUtil; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +/** + * This macro is very restritive in which rules are allowed to be applied for symbolic + * execution. No reasoning rules should ever be applied. + * + * @author mattias ulbrich + */ +public class SymbolicExecutionOnlyMacro extends StrategyProofMacro { + + private static final List ADMITTED_RULES; + private static final List ADMITTED_RULE_SETS; + static { + try { + ADMITTED_RULES = Arrays.asList( + Streams.toString(SymbolicExecutionOnlyMacro.class.getResourceAsStream("SymbolicExecutionOnlyMacro.admittedRules.txt")).split("\n")); + ADMITTED_RULE_SETS = Arrays.asList( + Streams.toString(SymbolicExecutionOnlyMacro.class.getResourceAsStream("SymbolicExecutionOnlyMacro.admittedRuleSets.txt")).split("\n")); + } catch (IOException e) { + throw new RuntimeException("Failed to load admitted rules for symbolic execution macro.", e); + } + } + + @Override + public String getName() { + return "Symbolic Execution Only"; + } + + @Override + public String getCategory() { + return "Auto Pilot"; + } + + @Override + public String getScriptCommandName() { + return "symbex-only"; + } + + @Override + public String getDescription() { + return "Continue symbolic execution until no more modality is on the sequent."; + } + + @Override + protected Strategy<@NonNull Goal> createStrategy(Proof proof, + PosInOccurrence posInOcc) { + return new FilterSymbexStrategy(proof.getActiveStrategy()); + } + + public static boolean isAdmittedRule(RuleApp ruleApp) { + Rule rule = ruleApp.rule(); + String name = rule.name().toString(); + if (ADMITTED_RULES.contains(name)) { + return true; + } + + if (rule instanceof org.key_project.prover.rules.Taclet taclet) { + for (RuleSet rs : taclet.getRuleSets()) { + if (ADMITTED_RULE_SETS.contains(rs.name().toString())) { + return true; + } + } + } + + if("ifthenelse_split_for".equals(name)) { + Term iteTerm = ruleApp.posInOccurrence().subTerm(); + Term then = iteTerm.sub(1); + Term elze = iteTerm.sub(2); + if(isUpdatedModality(then) && isUpdatedModality(elze)) { + return true; + } + } + + // apply OSS to () calls. + if (rule instanceof OneStepSimplifier) { + PosInOccurrence pio = ruleApp.posInOccurrence(); + var target = pio.subTerm(); + if (isUpdatedModality(target)) { + return true; + } + } + + if(rule instanceof UseOperationContractRule || + rule instanceof JmlAssertRule || + rule instanceof WhileInvariantRule || + rule instanceof LoopScopeInvariantRule ) + return true; + + return false; + } + + private static boolean isUpdatedModality(Term term) { + while(term.op() instanceof UpdateApplication) { + term = term.sub(1); + } + return term.op() instanceof Modality; + } + + private static class FilterSymbexStrategy extends FilterStrategy { + + private static final Name NAME = new Name(FilterSymbexStrategy.class.getSimpleName()); + + public FilterSymbexStrategy(Strategy<@NonNull Goal> delegate) { + super(delegate); + } + + @Override + public Name name() { + return NAME; + } + + @Override + public boolean isApprovedApp(RuleApp app, PosInOccurrence pio, Goal goal) { + return isAdmittedRule(app) && super.isApprovedApp(app, pio, goal); + } + + @Override + public boolean isStopAtFirstNonCloseableGoal() { + return false; + } + } + +} diff --git a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.macros.ProofMacro b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.macros.ProofMacro index d910282338a..6ec02ca7559 100644 --- a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.macros.ProofMacro +++ b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.macros.ProofMacro @@ -24,3 +24,4 @@ de.uka.ilkd.key.macros.UpdateSimplificationMacro de.uka.ilkd.key.macros.TranscendentalFloatSMTMacro de.uka.ilkd.key.macros.ScriptAwareMacro de.uka.ilkd.key.macros.ScriptAwarePrepMacro +de.uka.ilkd.key.macros.SymbolicExecutionOnlyMacro diff --git a/key.core/src/main/resources/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.admittedRuleSets.txt b/key.core/src/main/resources/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.admittedRuleSets.txt new file mode 100644 index 00000000000..3288342e4c1 --- /dev/null +++ b/key.core/src/main/resources/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.admittedRuleSets.txt @@ -0,0 +1,8 @@ +rulesets +alpha +simplify_prog_subset +simplify_prog +simplify_autoname +executeIntegerAssignment +simplify_expression +loop_scope_inv_taclet diff --git a/key.core/src/main/resources/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.admittedRules.txt b/key.core/src/main/resources/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.admittedRules.txt new file mode 100644 index 00000000000..2acdd87a049 --- /dev/null +++ b/key.core/src/main/resources/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.admittedRules.txt @@ -0,0 +1,4 @@ +rules +ifUnfold +ifSplit +ifElseSplit From 265319ca53ea831381e37104531330b72ba0a721 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sun, 5 Oct 2025 18:04:11 +0200 Subject: [PATCH 52/90] Allowing "auto only: ..." --- .../key/scripts/AdditionalRulesStrategy.java | 10 ++++++-- .../de/uka/ilkd/key/scripts/AutoCommand.java | 25 +++++++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java index 27aad464413..bb0609b0653 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java @@ -29,10 +29,12 @@ class AdditionalRulesStrategy extends FilterStrategy { private static final int DEFAULT_PRIORITY = 1000; private final List> additionalRules; + private final boolean exclusive; - public AdditionalRulesStrategy(Strategy delegate, String additionalRules) { + public AdditionalRulesStrategy(Strategy delegate, String additionalRules, boolean exclusive) { super(delegate); this.additionalRules = parseAddRules(additionalRules); + this.exclusive = exclusive; } private List> parseAddRules(String additionalRules) { @@ -77,7 +79,11 @@ public boolean isApprovedApp(RuleApp app, PosInOccurrence pio, Goal goal) { if (localCost != null) { return true; } - return super.isApprovedApp(app, pio, goal); + if (exclusive) { + return false; + } else { + return super.isApprovedApp(app, pio, goal); + } } private @Nullable RuleAppCost computeLocalCost(Rule rule) { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java index e9699a5647f..c6199a2c88a 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java @@ -95,7 +95,10 @@ public void execute(ScriptCommandAst args) throws ScriptException, InterruptedEx final Strategy originalStrategy = state.getProof().getActiveStrategy(); if (arguments.additionalRules != null) { - state.getProof().setActiveStrategy(new AdditionalRulesStrategy(originalStrategy, arguments.additionalRules)); + state.getProof().setActiveStrategy(new AdditionalRulesStrategy(originalStrategy, arguments.additionalRules, false)); + } + if (arguments.onlyRules != null) { + state.getProof().setActiveStrategy(new AdditionalRulesStrategy(originalStrategy, arguments.onlyRules, true)); } // Give some feedback @@ -177,7 +180,7 @@ private void setupFocussedBreakpointStrategy(final String maybeMatchesRegEx, Use the command with "close" to make sure the command succeeds for fails without changes.""") - public static class Parameters { + public static class Parameters implements ValueInjector.VerifyableParameters { // @ TODO Deprecated with the higher order proof commands? @Flag(value = "all") @Documentation("*Deprecated*. Apply the strategy on all open goals. There is a better syntax for that now.") @@ -228,8 +231,26 @@ may be a showstopper (if expansion increases the complexity on the sequent too m Additional rules to be used by the auto strategy. The rules have to be given as a comma-separated list of rule names and rule set names. Each entry can be assigned to a priority (high, low, medium or a natural number) using an equals sign. + Cannot be combined with the 'only' parameter. """) public @Nullable String additionalRules; + + @Option(value = "only") + @Documentation(""" + Limit the rules to be used by the auto strategy. The rules have to be given as a + comma-separated list of rule names and rule set names. Each entry can be assigned to a priority + (high, low, medium or a natural number) using an equals sign. + All rules application which do not match the given names will be disabled. + Cannot be combined with the 'add' parameter. + """) + public @Nullable String onlyRules; + + @Override + public void verifyParameters() throws IllegalArgumentException, InjectionException { + if(onlyRules != null && additionalRules != null) { + throw new InjectionException("Parameters 'add' and 'only' are mutually exclusive."); + } + } } private static final class OriginalValue { From e72aaa6016d416c6087591575545465cad756153 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sun, 5 Oct 2025 18:04:41 +0200 Subject: [PATCH 53/90] quicksort example with JML scripts (partially) --- key.ui/examples/heap/quicksort/Quicksort.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/key.ui/examples/heap/quicksort/Quicksort.java b/key.ui/examples/heap/quicksort/Quicksort.java index 23d7bb6f0ff..75e85a0c7d0 100644 --- a/key.ui/examples/heap/quicksort/Quicksort.java +++ b/key.ui/examples/heap/quicksort/Quicksort.java @@ -28,7 +28,10 @@ * The example has been added to show the power of proof * scripts. * - * @author Mattias Ulbrich, 2015 + * Translated to the use of JML proof scripts in 2025. Currently, + * this still increases the size of proof. + * + * @author Mattias Ulbrich, 2015, 2025 */ class Quicksort { @@ -58,9 +61,21 @@ public void sort(int[] array) { @*/ private void sort(int[] array, int from, int to) { if(from < to) { + //@ ghost \seq seq0 = \dl_array2seq(array); int splitPoint = split(array, from, to); + //@ ghost \seq seq1 = \dl_array2seq(array); sort(array, from, splitPoint-1); + //@ ghost \seq seq2 = \dl_array2seq(array); sort(array, splitPoint+1, to); + //@ ghost \seq seq3 = \dl_array2seq(array); + /*@ assert \dl_seqPerm(seq3, seq0) \by { + @ assert \dl_seqPerm(seq1, seq0) \by auto; + @ assert \dl_seqPerm(seq2, seq0) \by auto; + @ auto; + @ } */ + //@ assert (\forall int i; from<=i && i 0 ==> (\forall int x; from<=x && x<=to; array[x] > array[from-1]) \by { auto; } + //@ assert to < array.length-1 ==> (\forall int x; from<=x && x<=to; array[x] <= array[to+1]) \by { auto; } } } From cb5dcea3dfd5bfa3e229b9a0c8ed77f94f166069 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sun, 5 Oct 2025 22:03:34 +0200 Subject: [PATCH 54/90] enabling the quicksort example with JML proof scripts! --- .../java/de/uka/ilkd/key/proof/NodeInfo.java | 5 ++-- .../uka/ilkd/key/scripts/BranchesCommand.java | 6 ++++ key.ui/examples/heap/quicksort/Quicksort.java | 28 +++++++++++++++++-- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/NodeInfo.java b/key.core/src/main/java/de/uka/ilkd/key/proof/NodeInfo.java index 81f41480bb1..4b77d3a1cf2 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/NodeInfo.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/NodeInfo.java @@ -26,6 +26,7 @@ import de.uka.ilkd.key.rule.Taclet; import de.uka.ilkd.key.rule.TacletApp; +import org.jspecify.annotations.Nullable; import org.key_project.logic.Name; import org.key_project.proof.LocationVariableTracker; import org.key_project.prover.rules.RuleApp; @@ -49,7 +50,7 @@ public class NodeInfo { /** firstStatement stripped of method frames */ private SourceElement activeStatement = null; - private String branchLabel = null; + private @Nullable String branchLabel = null; /** flag true if the first and active statement have been determined */ private boolean determinedFstAndActiveStatement = false; @@ -265,7 +266,7 @@ public SourceElement getActiveStatement() { * * @return branch label */ - public String getBranchLabel() { + public @Nullable String getBranchLabel() { return branchLabel; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java index 23696ecb000..9d6c186b7b9 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java @@ -67,6 +67,7 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup goal = findGoalByName(root, args.branch); } state.setGoal(goal); + break; case "single": root = findNodeByNumber(proof, stack.peek()); TacletApp ta = (TacletApp) root.getAppliedRuleApp(); @@ -105,13 +106,18 @@ private void ensureSingleGoal() { private Goal findGoalByName(Node root, String branch) throws ScriptException { Iterator it = root.childrenIterator(); List knownBranchLabels = new ArrayList<>(); + int number = 1; while (it.hasNext()) { Node node = it.next(); String label = node.getNodeInfo().getBranchLabel(); + if (label == null) { + label = "Case " + number; + } knownBranchLabels.add(label); if (branch.equals(label)) { return findGoalByNode(root.proof(), node); } + number ++; } throw new ScriptException( "Unknown branch " + branch + ". Known branches are " + knownBranchLabels); diff --git a/key.ui/examples/heap/quicksort/Quicksort.java b/key.ui/examples/heap/quicksort/Quicksort.java index 75e85a0c7d0..431eaf04726 100644 --- a/key.ui/examples/heap/quicksort/Quicksort.java +++ b/key.ui/examples/heap/quicksort/Quicksort.java @@ -73,9 +73,6 @@ private void sort(int[] array, int from, int to) { @ assert \dl_seqPerm(seq2, seq0) \by auto; @ auto; @ } */ - //@ assert (\forall int i; from<=i && i 0 ==> (\forall int x; from<=x && x<=to; array[x] > array[from-1]) \by { auto; } - //@ assert to < array.length-1 ==> (\forall int x; from<=x && x<=to; array[x] <= array[to+1]) \by { auto; } } } @@ -112,6 +109,18 @@ private int split(int[] array, int from, int to) { int t = array[i]; array[i] = array[j]; array[j] = t; + /*@ assert \dl_seqPerm(\dl_array2seq(array), \old(\dl_array2seq(array))) \by { + @ oss; + @ rule "seqPermFromSwap"; + @ rule "andRight" \by { + @ case "Case 1": // the first of the two conjuncts is easy + @ auto; + @ case "Case 2": // the 2nd requires instantiations: + @ instantiate hide:true var:"iv" with:i; + @ instantiate hide:true var:"jv" with:j; + @ auto; + @ } + @ }; */ i++; } } @@ -119,6 +128,19 @@ private int split(int[] array, int from, int to) { array[to] = array[i]; array[i] = pivot; + /*@ assert \dl_seqPerm(\dl_array2seq(array), \old(\dl_array2seq(array))) \by { + @ oss; + @ rule "seqPermFromSwap"; + @ rule "andRight" \by { + @ case "Case 1": // the first of the two conjuncts is easy + @ auto; + @ case "Case 2": // the 2nd requires instantiations: + @ instantiate hide:true var:"iv" with:i; + @ instantiate hide:true var:"jv" with:to; + @ auto; + @ } + @ }; */ + return i; } From 331e0e69c58341d4c4cc429eabd4678d345eaab8 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Mon, 6 Oct 2025 12:10:08 +0200 Subject: [PATCH 55/90] limiting the symbex only macro a bit added test cases --- .../macros/SymbolicExecutionOnlyMacro.java | 28 +++++++++++++++++++ .../uka/ilkd/key/scripts/JmlScriptTest.java | 5 ++-- .../key/scripts/TestProofScriptCommand.java | 8 +++--- .../uka/ilkd/key/scripts/cases/autoOnly.yml | 7 +++++ .../de/uka/ilkd/key/scripts/jml/AutoOnly.java | 19 +++++++++++++ .../uka/ilkd/key/scripts/jml/AutoOnly2.java | 18 ++++++++++++ 6 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/autoOnly.yml create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AutoOnly.java create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AutoOnly2.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java index c47a4b958f1..1b876922f45 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java @@ -18,6 +18,8 @@ import org.key_project.prover.rules.RuleApp; import org.key_project.prover.rules.RuleSet; import org.key_project.prover.sequent.PosInOccurrence; +import org.key_project.prover.sequent.Sequent; +import org.key_project.prover.sequent.SequentFormula; import org.key_project.prover.strategy.costbased.NumberRuleAppCost; import org.key_project.util.Streams; import org.key_project.util.java.StringUtil; @@ -25,6 +27,8 @@ import java.io.IOException; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; /** * This macro is very restritive in which rules are allowed to be applied for symbolic @@ -125,6 +129,7 @@ private static boolean isUpdatedModality(Term term) { private static class FilterSymbexStrategy extends FilterStrategy { private static final Name NAME = new Name(FilterSymbexStrategy.class.getSimpleName()); + private final Map modalityCache = new WeakHashMap<>(); public FilterSymbexStrategy(Strategy<@NonNull Goal> delegate) { super(delegate); @@ -137,9 +142,32 @@ public Name name() { @Override public boolean isApprovedApp(RuleApp app, PosInOccurrence pio, Goal goal) { + if(!hasModality(goal)) { + return false; + } return isAdmittedRule(app) && super.isApprovedApp(app, pio, goal); } + private boolean hasModality(Goal goal) { + return modalityCache.computeIfAbsent(goal.node().sequent(), this::hasModality); + } + + private boolean hasModality(Sequent seq) { + for (SequentFormula formula : seq.asList()) { + if (hasModality(formula.formula())) { + return true; + } + } + return false; + } + + private boolean hasModality(Term term) { + if(term.op() instanceof Modality) { + return true; + } + return term.subs().stream().anyMatch(this::hasModality); + } + @Override public boolean isStopAtFirstNonCloseableGoal() { return false; diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java index 75e91b84328..0f67cc7ecc3 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java @@ -38,7 +38,8 @@ public class JmlScriptTest { // Set this to a specific case to only run that case for debugging private static final String ONLY_CASE = null; - private static final boolean SAVE_PROOF = true; + // Set this to true to save the proof after running the script + private static final boolean SAVE_PROOF = false; static { URL url = JmlScriptTest.class.getResource("jml/project.key"); @@ -111,7 +112,7 @@ private static Parameters readParams(Path path) throws IOException { public static Stream filesProvider() throws URISyntaxException, IOException { URL jmlUrl = JmlScriptTest.class.getResource("jml"); if (ONLY_CASE != null) { - return Stream.of(Arguments.of(Paths.get(jmlUrl.toURI()).resolve(ONLY_CASE), "single specified case")); + return Stream.of(Arguments.of(Paths.get(jmlUrl.toURI()).resolve(ONLY_CASE), "single specified case: " + ONLY_CASE)); } else { return Files.list(Paths.get(jmlUrl.toURI())) .filter(p -> p.toString().endsWith(".java")) diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java index b4724c50264..1fdb1b9f088 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java @@ -56,7 +56,8 @@ public static List data() throws IOException, URISyntaxException { try { TestInstance instance = objectMapper.readValue(path.toFile(), TestInstance.class); - args.add(Arguments.of(instance)); + var name = instance.name == null ? path.getFileName().toString() : instance.name; + args.add(Arguments.of(instance, name)); } catch (Exception e) { System.out.println(path); e.printStackTrace(); @@ -67,10 +68,9 @@ public static List data() throws IOException, URISyntaxException { } } - @ParameterizedTest + @ParameterizedTest(name = "{1}") @MethodSource("data") - void testProofScript(TestInstance data) throws Exception { - var name = data.name(); + void testProofScript(TestInstance data, String name) throws Exception { Path tmpKey = Files.createTempFile("proofscript_key_" + name, ".key"); Files.writeString(tmpKey, data.key()); diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/autoOnly.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/autoOnly.yml new file mode 100644 index 00000000000..24eef5e7d10 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/autoOnly.yml @@ -0,0 +1,7 @@ +key: | + \predicates { a; b; c;} + \problem { a & b & 1=0 -> a | c } +script: | + auto only:alpha; +goals: + - a, b, 1 = 0 ==> a, c \ No newline at end of file diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AutoOnly.java b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AutoOnly.java new file mode 100644 index 00000000000..445fb66b9a5 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AutoOnly.java @@ -0,0 +1,19 @@ +//! settings: +//! CLASS_AXIOM_OPTIONS_KEY: CLASS_AXIOM_OFF + +class Test { + + //@ model int f(int arg) { return arg + arg; } + + boolean b,c,d; + + //@ ensures true; + void test() { + + /*@ assert b && c ==> c || d \by { + auto only:"alpha"; + rule "close" occ:"1"; + } */ + } + +} diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AutoOnly2.java b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AutoOnly2.java new file mode 100644 index 00000000000..a3c11a11113 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AutoOnly2.java @@ -0,0 +1,18 @@ +//! shouldClose: false + +class Test { + + //@ model int f(int arg) { return arg + arg; } + + boolean b,c,d; + + //@ ensures true; + void test() { + + /*@ assert b && c ==> c || d \by { + auto only:"beta"; + rule "close"; + } */ + } + +} From 8e5e5768529459a1840e0e008e2262bfdbcd0203 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Thu, 18 Jul 2024 15:31:16 +0200 Subject: [PATCH 56/90] introducing hole placeholders for terms and for formulas --- .../de/uka/ilkd/key/scripts/RuleCommand.java | 4 +- .../key/scripts/TermComparisonWithHoles.java | 201 ++++++++++++++++++ .../uka/ilkd/key/proof/rules/javaHeader.key | 7 + 3 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java index ceae3c429f1..3cfa634f7bf 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java @@ -371,8 +371,8 @@ private List filterList(Services services, Parameters p, for (TacletApp tacletApp : list) { if (tacletApp instanceof PosTacletApp pta) { JTerm term = (JTerm) pta.posInOccurrence().subTerm(); - boolean add = - p.on == null || RENAMING_TERM_PROPERTY.equalsModThisProperty(term, p.on); + boolean add = p.on == null + || TermComparisonWithHoles.compareModHoles(p.on, term); for (var entry : pta.instantiations().getInstantiationMap()) { final SchemaVariable sv = entry.key(); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java new file mode 100644 index 00000000000..4057ebbebd0 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java @@ -0,0 +1,201 @@ +package de.uka.ilkd.key.macros.scripts; + +import de.uka.ilkd.key.java.NameAbstractionTable; +import de.uka.ilkd.key.logic.Name; +import de.uka.ilkd.key.logic.Term; +import de.uka.ilkd.key.logic.op.*; +import org.key_project.util.collection.ImmutableList; +import org.key_project.util.collection.ImmutableSLList; + +/** This is more a temporary hack for scripts ... */ +public class TermComparisonWithHoles { + + private static final Name HOLE_NAME = new Name("_"); + private static final Name HOLE_PREDICATE_NAME = new Name("__"); + + private static final NameAbstractionTable FAILED = new NameAbstractionTable(); + + public static boolean compareModHoles(Term t1, Term t2) { + if (t1 == t2) { + return true; + } + return unifyHelp(t1, t2, + ImmutableSLList.nil(), + ImmutableSLList.nil(), + null); + } + + + /** + * Compares two terms modulo bound renaming + * + * @param t0 the first term + * @param t1 the second term + * @param ownBoundVars variables bound above the current position + * @param cmpBoundVars variables bound above the current position + * @return true is returned iff the terms are equal modulo + * bound renaming + */ + private static boolean unifyHelp(Term t0, Term t1, + ImmutableList ownBoundVars, + ImmutableList cmpBoundVars, + NameAbstractionTable nat) { + + if (t0 == t1 && ownBoundVars.equals(cmpBoundVars)) { + return true; + } + + Operator op = t0.op(); + if(op instanceof SortDependingFunction) { + var sdop = (SortDependingFunction) op; + if(sdop.getKind().equals(HOLE_NAME)) { + return true; + } + } else if(op.name().equals(HOLE_PREDICATE_NAME)) { + return true; + } + + + final Operator op0 = t0.op(); + + if (op0 instanceof QuantifiableVariable) { + return handleQuantifiableVariable(t0, t1, ownBoundVars, + cmpBoundVars); + } + + final Operator op1 = t1.op(); + + if (!(op0 instanceof ProgramVariable) && op0 != op1) { + return false; + } + + if (t0.sort() != t1.sort() || t0.arity() != t1.arity()) { + return false; + } + + nat = handleJava(t0, t1, nat); + if (nat == FAILED) { + return false; + } + + return descendRecursively(t0, t1, ownBoundVars, cmpBoundVars, nat); + } + + private static boolean handleQuantifiableVariable(Term t0, Term t1, + ImmutableList ownBoundVars, + ImmutableList cmpBoundVars) { + if (!((t1.op() instanceof QuantifiableVariable) && compareBoundVariables( + (QuantifiableVariable) t0.op(), (QuantifiableVariable) t1.op(), + ownBoundVars, cmpBoundVars))) { + return false; + } + return true; + } + + private static NameAbstractionTable handleJava(Term t0, Term t1, + NameAbstractionTable nat) { + + if (!t0.javaBlock().isEmpty() || !t1.javaBlock().isEmpty()) { + nat = checkNat(nat); + if (!t0.javaBlock().equalsModRenaming(t1.javaBlock(), nat)) { + return FAILED; + } + } + + if (!(t0.op() instanceof SchemaVariable) + && t0.op() instanceof ProgramVariable) { + if (!(t1.op() instanceof ProgramVariable)) { + return FAILED; + } + nat = checkNat(nat); + if (!((ProgramVariable) t0.op()).equalsModRenaming( + (ProgramVariable) t1.op(), nat)) { + return FAILED; + } + } + + return nat; + } + + /** + * compare two quantifiable variables if they are equal modulo renaming + * + * @param ownVar first QuantifiableVariable to be compared + * @param cmpVar second QuantifiableVariable to be compared + * @param ownBoundVars variables bound above the current position + * @param cmpBoundVars variables bound above the current position + */ + private static boolean compareBoundVariables(QuantifiableVariable ownVar, + QuantifiableVariable cmpVar, + ImmutableList ownBoundVars, + ImmutableList cmpBoundVars) { + + final int ownNum = indexOf(ownVar, ownBoundVars); + final int cmpNum = indexOf(cmpVar, cmpBoundVars); + + if (ownNum == -1 && cmpNum == -1) { + // if both variables are not bound the variables have to be the + // same object + return ownVar == cmpVar; + } + + // otherwise the variables have to be bound at the same point (and both + // be bound) + return ownNum == cmpNum; + } + + private static int indexOf(QuantifiableVariable var, + ImmutableList list) { + int res = 0; + while (!list.isEmpty()) { + if (list.head() == var) { + return res; + } + ++res; + list = list.tail(); + } + return -1; + } + + + private static NameAbstractionTable checkNat(NameAbstractionTable nat) { + if (nat == null) { + return new NameAbstractionTable(); + } + return nat; + } + + private static boolean descendRecursively(Term t0, Term t1, + ImmutableList ownBoundVars, + ImmutableList cmpBoundVars, + NameAbstractionTable nat) { + + for (int i = 0; i < t0.arity(); i++) { + ImmutableList subOwnBoundVars = ownBoundVars; + ImmutableList subCmpBoundVars = cmpBoundVars; + + if (t0.varsBoundHere(i).size() != t1.varsBoundHere(i).size()) { + return false; + } + for (int j = 0; j < t0.varsBoundHere(i).size(); j++) { + final QuantifiableVariable ownVar = t0.varsBoundHere(i).get(j); + final QuantifiableVariable cmpVar = t1.varsBoundHere(i).get(j); + if (ownVar.sort() != cmpVar.sort()) { + return false; + } + + subOwnBoundVars = subOwnBoundVars.prepend(ownVar); + subCmpBoundVars = subCmpBoundVars.prepend(cmpVar); + } + + boolean newConstraint = unifyHelp(t0.sub(i), t1.sub(i), + subOwnBoundVars, subCmpBoundVars, nat); + + if (!newConstraint) { + return false; + } + } + + return true; + } +} diff --git a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaHeader.key b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaHeader.key index 810c4692342..8bfd655a6c7 100644 --- a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaHeader.key +++ b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaHeader.key @@ -28,4 +28,11 @@ alpha alpha::cast(any); boolean alpha::exactInstance(any); boolean alpha::instance(any); + alpha alpha::_; // for matching in scripts +} + +\predicates { + __; // for matching scripts +} + } From ed0c89cf0037666d5a4418e501e7659ee37eb1ee Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sun, 11 Aug 2024 16:04:07 +0200 Subject: [PATCH 57/90] introducing witness command --- .../uka/ilkd/key/scripts/WitnessCommand.java | 118 ++++++++++++++++++ ...de.uka.ilkd.key.scripts.ProofScriptCommand | 1 + 2 files changed, 119 insertions(+) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java new file mode 100644 index 00000000000..a919145c2d9 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java @@ -0,0 +1,118 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ + +package de.uka.ilkd.key.scripts; + +import de.uka.ilkd.key.control.AbstractUserInterfaceControl; +import de.uka.ilkd.key.java.Services; +import de.uka.ilkd.key.logic.*; +import de.uka.ilkd.key.logic.op.Quantifier; +import de.uka.ilkd.key.logic.op.UpdateApplication; +import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.proof.Node; +import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.proof.RuleAppIndex; +import de.uka.ilkd.key.rule.*; +import de.uka.ilkd.key.rule.inst.SVInstantiations; +import org.key_project.logic.Name; +import org.key_project.util.collection.ImmutableList; +import org.key_project.util.collection.ImmutableSLList; + +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * witness "\exists int x; phi(x)" as="x_12" + *

    + * witness "\forall int x; phi(x)" as="x_13" + *

    + * witness "\exists int x; phi(x)" as="x_14" cut=true + * + * Possibly with an assertion before to make sure that the formula is on the sequent. + * + * @author mulbrich + */ +public class WitnessCommand extends AbstractCommand { + + private static final Pattern GOOD_NAME = Pattern.compile("[a-zA-Z][a-zA-Z0-9_]*"); + private static final Name ANTEC_TACLET = new Name("exLeft"); + private static final Name SUCC_TACLET = new Name("allRight"); + + public WitnessCommand() { + super(Parameters.class); + } + + @Override + public String getName() { + return "witness"; + } + + @Override + public void execute(ScriptCommandAst ast) throws ScriptException, InterruptedException { + + Parameters params = state().getValueInjector().inject(new Parameters(), ast); + + Goal goal = state.getFirstOpenAutomaticGoal(); + Services services = state.getProof().getServices(); + + TermComparisonWithHoles tc = new TermComparisonWithHoles(services); + Pair match = goal.node().sequent().antecedent().asList().stream() + .filter(f -> tc.compareModHoles(params.formula, f.formula())) + .map(f -> new Pair<>(true, f)) + .findFirst().orElse( + goal.node().sequent().succedent().asList().stream() + .filter(f -> tc.compareModHoles(params.formula, f.formula())) + .map(f -> new Pair<>(false, f)) + .findFirst().orElse(null) + ); + + if (match == null) { + throw new ScriptException("Cannot match the formula argument"); + } + + Operator op = match.second.formula().op(); + Operator expected = match.first ? Quantifier.EX : Quantifier.ALL; + if (op != expected) { + throw new ScriptException("Expected quantifier " + expected + ", but got " + op); + } + + if(!GOOD_NAME.matcher(params.as).matches()) { + throw new ScriptException("Invalid name: " + params.as); + } + + Name tacletName = match.first ? ANTEC_TACLET : SUCC_TACLET; + FindTaclet taclet = (FindTaclet) state.getProof().getEnv().getInitConfigForEnvironment() + .lookupActiveTaclet(tacletName); + PosInOccurrence pio = new PosInOccurrence(match.second, PosInTerm.getTopLevel(), match.first); + MatchConditions mc = new MatchConditions(); + TacletApp app = PosTacletApp.createPosTacletApp(taclet, mc, pio, services); + Set schemaVars = taclet.collectSchemaVars(); + app = app.addInstantiation(getSV(schemaVars, "u"), + services.getTermBuilder().tf().createTerm(match.second.formula().boundVars().get(0)), + true, services); + app = app.addInstantiation(getSV(schemaVars, "b"), match.second.formula().sub(0), true, services); + app = app.createSkolemConstant(params.as, getSV(schemaVars, "sk"), true, services); + + Goal g = state.getFirstOpenAutomaticGoal(); + g.apply(app); + } + + private static SchemaVariable getSV(Set schemaVars, String name) { + for (SchemaVariable schemaVar : schemaVars) { + if(schemaVar.name().toString().equals(name)) { + return schemaVar; + } + } + throw new NoSuchElementException("No schema variable with name " + name); + } + + public static class Parameters { + @Option(value = "as") + public String as; + @Option(value = "#2") + public Term formula; + } + +} diff --git a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand index 7799535bc13..5390423b7d6 100644 --- a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand +++ b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.scripts.ProofScriptCommand @@ -19,6 +19,7 @@ de.uka.ilkd.key.scripts.LeaveCommand de.uka.ilkd.key.scripts.TryCloseCommand de.uka.ilkd.key.scripts.ExitCommand de.uka.ilkd.key.scripts.InstantiateCommand +de.uka.ilkd.key.scripts.WitnessCommand de.uka.ilkd.key.scripts.SelectCommand de.uka.ilkd.key.scripts.ScriptCommand de.uka.ilkd.key.scripts.LetCommand From f1e5e3ed2b3f71bdc809974086bab691b0d8eb19 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Mon, 6 Oct 2025 15:02:05 +0200 Subject: [PATCH 58/90] adapting the cherry-picked commits --- .../key/scripts/TermComparisonWithHoles.java | 144 +++++++++++++----- .../uka/ilkd/key/scripts/WitnessCommand.java | 66 +++++--- .../uka/ilkd/key/proof/rules/javaHeader.key | 2 - .../key/scripts/TestProofScriptCommand.java | 24 ++- .../uka/ilkd/key/scripts/cases/witness1.yml | 6 + .../uka/ilkd/key/scripts/cases/witness2.yml | 8 + 6 files changed, 182 insertions(+), 68 deletions(-) create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness1.yml create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness2.yml diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java index 4057ebbebd0..7de4ebb06d0 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java @@ -1,21 +1,61 @@ -package de.uka.ilkd.key.macros.scripts; +package de.uka.ilkd.key.scripts; import de.uka.ilkd.key.java.NameAbstractionTable; -import de.uka.ilkd.key.logic.Name; -import de.uka.ilkd.key.logic.Term; +import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.op.*; +import org.jspecify.annotations.Nullable; +import org.key_project.logic.Name; +import org.key_project.logic.Property; +import org.key_project.logic.op.Operator; +import org.key_project.logic.op.QuantifiableVariable; +import org.key_project.prover.sequent.Sequent; +import org.key_project.prover.sequent.SequentFormula; import org.key_project.util.collection.ImmutableList; import org.key_project.util.collection.ImmutableSLList; - -/** This is more a temporary hack for scripts ... */ +import org.key_project.util.collection.Pair; + +import java.util.ArrayList; +import java.util.List; + +/** + * A property that can be used for comparisons for terms. + * All term labels are ignored in this equality check. Additionally, holes (represented by the + * SortDependingFunction with name "_" and the Predicate with name "__") are treated as wildcards that + * match any subterm. + *

    + * The single instance of this property can be accessed through + * {@link TermComparisonWithHoles#INSTANCE}. + * + * @author Mattias Ulbrich + */ public class TermComparisonWithHoles { private static final Name HOLE_NAME = new Name("_"); private static final Name HOLE_PREDICATE_NAME = new Name("__"); private static final NameAbstractionTable FAILED = new NameAbstractionTable(); + private final JTerm referenceTerm; + + TermComparisonWithHoles(JTerm referenceTerm) { + this.referenceTerm = referenceTerm; + } + + public static boolean compare(JTerm referenceTerm, JTerm concreteTerm) { + TermComparisonWithHoles comparator = new TermComparisonWithHoles(referenceTerm); + return comparator.compareTo(concreteTerm); + } - public static boolean compareModHoles(Term t1, Term t2) { + public final boolean compareTo(JTerm t) { + if (referenceTerm == t) { + return true; + } + return unifyHelp(referenceTerm, t, + ImmutableSLList.nil(), + ImmutableSLList.nil(), + null); + } + + public static boolean compareModHoles(JTerm t1, JTerm t2) { if (t1 == t2) { return true; } @@ -29,14 +69,14 @@ public static boolean compareModHoles(Term t1, Term t2) { /** * Compares two terms modulo bound renaming * - * @param t0 the first term + * @param t0 the first term -- potentially containing holes * @param t1 the second term * @param ownBoundVars variables bound above the current position * @param cmpBoundVars variables bound above the current position * @return true is returned iff the terms are equal modulo * bound renaming */ - private static boolean unifyHelp(Term t0, Term t1, + private static boolean unifyHelp(JTerm t0, JTerm t1, ImmutableList ownBoundVars, ImmutableList cmpBoundVars, NameAbstractionTable nat) { @@ -46,8 +86,7 @@ private static boolean unifyHelp(Term t0, Term t1, } Operator op = t0.op(); - if(op instanceof SortDependingFunction) { - var sdop = (SortDependingFunction) op; + if(op instanceof SortDependingFunction sdop) { if(sdop.getKind().equals(HOLE_NAME)) { return true; } @@ -73,15 +112,15 @@ private static boolean unifyHelp(Term t0, Term t1, return false; } - nat = handleJava(t0, t1, nat); - if (nat == FAILED) { - return false; - } +// nat = handleJava(t0, t1, nat); +// if (nat == FAILED) { +// return false; +// } return descendRecursively(t0, t1, ownBoundVars, cmpBoundVars, nat); } - private static boolean handleQuantifiableVariable(Term t0, Term t1, + private static boolean handleQuantifiableVariable(JTerm t0, JTerm t1, ImmutableList ownBoundVars, ImmutableList cmpBoundVars) { if (!((t1.op() instanceof QuantifiableVariable) && compareBoundVariables( @@ -92,30 +131,30 @@ private static boolean handleQuantifiableVariable(Term t0, Term t1, return true; } - private static NameAbstractionTable handleJava(Term t0, Term t1, - NameAbstractionTable nat) { - - if (!t0.javaBlock().isEmpty() || !t1.javaBlock().isEmpty()) { - nat = checkNat(nat); - if (!t0.javaBlock().equalsModRenaming(t1.javaBlock(), nat)) { - return FAILED; - } - } - - if (!(t0.op() instanceof SchemaVariable) - && t0.op() instanceof ProgramVariable) { - if (!(t1.op() instanceof ProgramVariable)) { - return FAILED; - } - nat = checkNat(nat); - if (!((ProgramVariable) t0.op()).equalsModRenaming( - (ProgramVariable) t1.op(), nat)) { - return FAILED; - } - } - - return nat; - } +// private static NameAbstractionTable handleJava(JTerm t0, JTerm t1, +// NameAbstractionTable nat) { +// +// if (!t0.javaBlock().isEmpty() || !t1.javaBlock().isEmpty()) { +// nat = checkNat(nat); +// if (!t0.javaBlock().equalsModRenaming(t1.javaBlock(), nat)) { +// return FAILED; +// } +// } +// +// if (!(t0.op() instanceof SchemaVariable) +// && t0.op() instanceof ProgramVariable) { +// if (!(t1.op() instanceof ProgramVariable)) { +// return FAILED; +// } +// nat = checkNat(nat); +// if (!((ProgramVariable) t0.op()).equalsModRenaming( +// (ProgramVariable) t1.op(), nat)) { +// return FAILED; +// } +// } +// +// return nat; +// } /** * compare two quantifiable variables if they are equal modulo renaming @@ -165,7 +204,7 @@ private static NameAbstractionTable checkNat(NameAbstractionTable nat) { return nat; } - private static boolean descendRecursively(Term t0, Term t1, + private static boolean descendRecursively(JTerm t0, JTerm t1, ImmutableList ownBoundVars, ImmutableList cmpBoundVars, NameAbstractionTable nat) { @@ -198,4 +237,29 @@ private static boolean descendRecursively(Term t0, Term t1, return true; } + + public List> findMatchesInSequent(Sequent sequent) { + List> matches = new ArrayList<>(); + for (SequentFormula sf : sequent.antecedent()) { + if (compareTo((JTerm) sf.formula())) { + matches.add(new Pair<>(true, sf)); + } + } + for (SequentFormula sf : sequent.succedent()) { + if (compareTo((JTerm) sf.formula())) { + matches.add(new Pair<>(false, sf)); + } + } + return matches; + } + + + public @Nullable Pair findUniqueMatchInSequent(Sequent sequent) { + List> matches = findMatchesInSequent(sequent); + if (matches.size() != 1) { + return null; + } else { + return matches.getFirst(); + } + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java index a919145c2d9..8e2031a3277 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java @@ -4,20 +4,22 @@ package de.uka.ilkd.key.scripts; -import de.uka.ilkd.key.control.AbstractUserInterfaceControl; import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.*; import de.uka.ilkd.key.logic.op.Quantifier; -import de.uka.ilkd.key.logic.op.UpdateApplication; import de.uka.ilkd.key.proof.Goal; -import de.uka.ilkd.key.proof.Node; -import de.uka.ilkd.key.proof.Proof; -import de.uka.ilkd.key.proof.RuleAppIndex; import de.uka.ilkd.key.rule.*; -import de.uka.ilkd.key.rule.inst.SVInstantiations; +import de.uka.ilkd.key.scripts.meta.Argument; +import de.uka.ilkd.key.scripts.meta.Documentation; +import de.uka.ilkd.key.scripts.meta.Option; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.key_project.logic.Name; -import org.key_project.util.collection.ImmutableList; -import org.key_project.util.collection.ImmutableSLList; +import org.key_project.logic.PosInTerm; +import org.key_project.logic.op.Operator; +import org.key_project.logic.op.sv.SchemaVariable; +import org.key_project.prover.sequent.PosInOccurrence; +import org.key_project.prover.sequent.SequentFormula; +import org.key_project.util.collection.Pair; import java.util.NoSuchElementException; import java.util.Set; @@ -57,19 +59,12 @@ public void execute(ScriptCommandAst ast) throws ScriptException, InterruptedExc Goal goal = state.getFirstOpenAutomaticGoal(); Services services = state.getProof().getServices(); - TermComparisonWithHoles tc = new TermComparisonWithHoles(services); - Pair match = goal.node().sequent().antecedent().asList().stream() - .filter(f -> tc.compareModHoles(params.formula, f.formula())) - .map(f -> new Pair<>(true, f)) - .findFirst().orElse( - goal.node().sequent().succedent().asList().stream() - .filter(f -> tc.compareModHoles(params.formula, f.formula())) - .map(f -> new Pair<>(false, f)) - .findFirst().orElse(null) - ); + TermComparisonWithHoles comp = new TermComparisonWithHoles(params.formula); + // First component: true for antecedent, false for succedent + Pair match = comp.findUniqueMatchInSequent(goal.node().sequent()); if (match == null) { - throw new ScriptException("Cannot match the formula argument"); + throw new ScriptException("Cannot unique match the formula argument"); } Operator op = match.second.formula().op(); @@ -82,6 +77,15 @@ public void execute(ScriptCommandAst ast) throws ScriptException, InterruptedExc throw new ScriptException("Invalid name: " + params.as); } + NamespaceSet nss = services.getNamespaces(); + Name asName = new Name(params.as); + if(nss.functions().lookup(asName) != null) { + throw new ScriptException("Name already used as function or predicate: " + params.as); + } + if(nss.programVariables().lookup(asName) != null) { + throw new ScriptException("Name already used as program variable: " + params.as); + } + Name tacletName = match.first ? ANTEC_TACLET : SUCC_TACLET; FindTaclet taclet = (FindTaclet) state.getProof().getEnv().getInitConfigForEnvironment() .lookupActiveTaclet(tacletName); @@ -93,7 +97,7 @@ public void execute(ScriptCommandAst ast) throws ScriptException, InterruptedExc services.getTermBuilder().tf().createTerm(match.second.formula().boundVars().get(0)), true, services); app = app.addInstantiation(getSV(schemaVars, "b"), match.second.formula().sub(0), true, services); - app = app.createSkolemConstant(params.as, getSV(schemaVars, "sk"), true, services); + app = app.createSkolemConstant(params.as, getSV(schemaVars, "sk"), match.second.formula().boundVars().get(0).sort(), true, services); Goal g = state.getFirstOpenAutomaticGoal(); g.apply(app); @@ -108,11 +112,27 @@ private static SchemaVariable getSV(Set schemaVars, String name) throw new NoSuchElementException("No schema variable with name " + name); } + @Documentation(category = "Fundamental", value = """ + Provides a witness symbol for an existential or universal quantifier. + The given formula must be present on the sequent. Placeholders are allowed. + The command fails if the formula cannot be uniquely matched on the sequent. + The witness symbol `as` must be a valid identifier and not already used as function, predicate, or + program variable name. The new function symbol is created as a Skolem constant. + + #### Example: + + If the sequent contains the formula `\\exists int x; x > 0` in the antecedent then the command + `witness "\\exists int x; x > 0" as="x_12"` will introduce the witness symbol `x_12` for which "x_12 > 0` + holds and is added to the antecedent. + """) public static class Parameters { + @Documentation("The name of the witness symbol to be created.") @Option(value = "as") - public String as; - @Option(value = "#2") - public Term formula; + public @MonotonicNonNull String as; + + @Documentation("The formula containing the quantifier for which a witness should be provided. Placeholders are allowed.") + @Argument + public @MonotonicNonNull JTerm formula; } } diff --git a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaHeader.key b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaHeader.key index 8bfd655a6c7..30620d309ba 100644 --- a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaHeader.key +++ b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaHeader.key @@ -34,5 +34,3 @@ \predicates { __; // for matching scripts } - -} diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java index 1fdb1b9f088..d9e8d2203b8 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java @@ -28,6 +28,9 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import recoder.util.Debug; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -36,15 +39,27 @@ * see {@link MasterHandlerTest} from where I copied quite a bit. */ public class TestProofScriptCommand { + + private static final String ONLY_CASE = "witness2.yml"; + private static final Logger LOGGER = LoggerFactory.getLogger(TestProofScriptCommand.class); + public record TestInstance( String name, - String key, String script, @Nullable String exception, - String[] goals, Integer selectedGoal) { + String key, + String script, + @Nullable String exception, + String[] goals, + Integer selectedGoal) { } public static List data() throws IOException, URISyntaxException { var folder = Paths.get("src/test/resources/de/uka/ilkd/key/scripts/cases") .toAbsolutePath(); + + if(ONLY_CASE != null) { + folder = folder.resolve(ONLY_CASE); + } + try (var walker = Files.walk(folder)) { List files = walker.filter(it -> it.getFileName().toString().endsWith(".yml")).toList(); @@ -56,7 +71,9 @@ public static List data() throws IOException, URISyntaxException { try { TestInstance instance = objectMapper.readValue(path.toFile(), TestInstance.class); - var name = instance.name == null ? path.getFileName().toString() : instance.name; + var name = instance.name == null ? + path.getFileName().toString().substring(0, path.getFileName().toString().length() - 4) : + instance.name; args.add(Arguments.of(instance, name)); } catch (Exception e) { System.out.println(path); @@ -72,6 +89,7 @@ public static List data() throws IOException, URISyntaxException { @MethodSource("data") void testProofScript(TestInstance data, String name) throws Exception { Path tmpKey = Files.createTempFile("proofscript_key_" + name, ".key"); + LOGGER.info("Testing {} using file", name, tmpKey); Files.writeString(tmpKey, data.key()); KeYEnvironment env = KeYEnvironment.load(tmpKey); diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness1.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness1.yml new file mode 100644 index 00000000000..6a6d3b9f5cb --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness1.yml @@ -0,0 +1,6 @@ +key: | + \problem { ==> \forall int x; x=x, \forall int x; (x > 0 | x < 2) } +script: | + witness (\forall int x; (__ | __)) as="y"; +goals: + - ==> \forall int x; x = x, y > 0 | y < 2 \ No newline at end of file diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness2.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness2.yml new file mode 100644 index 00000000000..9dada861b16 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness2.yml @@ -0,0 +1,8 @@ +key: | + \functions { int x; int x1; } + \predicates { x2; } + \programVariables { int x3; } + \problem { ==> \forall int x; x=x, \forall int x; (x > 0 | x < x1) } +script: | + witness (\forall int x; (__ | __)) as="x1"; +exception: "Name already used as function or predicate: x1" From 88ea9a275b3836054d77559971b97476d2f63f79 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Mon, 6 Oct 2025 16:03:46 +0200 Subject: [PATCH 59/90] allowing switching off rules in auto from 53286f761f547ae662e6275fb0693b92f2c502ba --- .../key/scripts/AdditionalRulesStrategy.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java index bb0609b0653..39240fdfc1f 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java @@ -12,6 +12,7 @@ import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.prover.strategy.costbased.NumberRuleAppCost; import org.key_project.prover.strategy.costbased.RuleAppCost; +import org.key_project.prover.strategy.costbased.TopRuleAppCost; import org.key_project.util.collection.Pair; import java.util.ArrayList; @@ -26,9 +27,9 @@ class AdditionalRulesStrategy extends FilterStrategy { private static final Map TRANSLATIONS = Map.of("high", "-50", "medium", "1000", "low", "10000"); - private static final int DEFAULT_PRIORITY = 1000; + private static final RuleAppCost DEFAULT_PRIORITY = NumberRuleAppCost.create(1000); - private final List> additionalRules; + private final List> additionalRules; private final boolean exclusive; public AdditionalRulesStrategy(Strategy delegate, String additionalRules, boolean exclusive) { @@ -37,16 +38,19 @@ public AdditionalRulesStrategy(Strategy delegate, String additionalRules, boolea this.exclusive = exclusive; } - private List> parseAddRules(String additionalRules) { - List> result = new ArrayList<>(); + private List> parseAddRules(String additionalRules) { + List> result = new ArrayList<>(); for (String entry : additionalRules.trim().split(" *, *")) { String[] parts = entry.split(" *= *", 2); - int prio; + RuleAppCost prio; if (parts.length == 2) { String prioStr = parts[1]; + if(prioStr.equals("off")) { + prio = TopRuleAppCost.INSTANCE; + } prioStr = TRANSLATIONS.getOrDefault(prioStr, prioStr); try { - prio = Integer.parseInt(prioStr); + prio = NumberRuleAppCost.create(Integer.parseInt(prioStr)); } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid value for additional rule: " + parts[1]); } @@ -88,9 +92,9 @@ public boolean isApprovedApp(RuleApp app, PosInOccurrence pio, Goal goal) { private @Nullable RuleAppCost computeLocalCost(Rule rule) { String name = rule.name().toString(); - Optional cost = lookup(name); + Optional cost = lookup(name); if(cost.isPresent()) { - return NumberRuleAppCost.create(cost.get()); + return cost.get(); } if (rule instanceof Taclet taclet) { @@ -98,7 +102,7 @@ public boolean isApprovedApp(RuleApp app, PosInOccurrence pio, Goal goal) { String rname = rs.name().toString(); cost = lookup(rname); if(cost.isPresent()) { - return NumberRuleAppCost.create(cost.get()); + return cost.get(); } } } @@ -106,7 +110,7 @@ public boolean isApprovedApp(RuleApp app, PosInOccurrence pio, Goal goal) { return null; } - private Optional lookup(String name) { + private Optional lookup(String name) { return additionalRules.stream() .filter(nameAndPrio -> name.matches(nameAndPrio.first)) .findFirst() From 393fe12f70d9591a81e4a69a1875b9b2867dbcd2 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Thu, 9 Oct 2025 16:31:43 +0200 Subject: [PATCH 60/90] a more robust term-with-holes implementation --- .../de/uka/ilkd/key/scripts/EngineState.java | 2 + .../key/scripts/TermComparisonWithHoles.java | 13 ++- .../uka/ilkd/key/scripts/TermWithHoles.java | 105 ++++++++++++++++++ .../uka/ilkd/key/scripts/WitnessCommand.java | 2 +- .../ilkd/key/scripts/meta/ValueInjector.java | 20 ++-- .../uka/ilkd/key/proof/rules/javaHeader.key | 4 +- .../key/scripts/TestProofScriptCommand.java | 3 +- .../de/uka/ilkd/key/scripts/cases/holes1.yml | 6 + .../ilkd/key/scripts/cases/instantiate1.yml | 6 + .../uka/ilkd/key/scripts/cases/witness1.yml | 4 +- .../uka/ilkd/key/scripts/cases/witness2.yml | 2 +- .../util/collection/ImmutableSet.java | 9 ++ 12 files changed, 153 insertions(+), 23 deletions(-) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/instantiate1.yml diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java index 0de9931d6cc..54f8a7b98f2 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java @@ -86,6 +86,8 @@ private ValueInjector createDefaultValueInjector() { v.addConverter(JTerm.class, String.class, (str) -> this.toTerm(str, null)); v.addConverter(Sequent.class, String.class, this::toSequent); v.addConverter(Sort.class, String.class, this::toSort); + v.addConverter(TermWithHoles.class, ProofScriptExpressionContext.class, + (ctx) -> TermWithHoles.fromParserContext(this, ctx)); addContextTranslator(v, String.class); addContextTranslator(v, JTerm.class); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java index 7de4ebb06d0..c9aa98bffa2 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java @@ -17,6 +17,8 @@ import java.util.ArrayList; import java.util.List; +import static de.uka.ilkd.key.scripts.TermWithHoles.*; + /** * A property that can be used for comparisons for terms. * All term labels are ignored in this equality check. Additionally, holes (represented by the @@ -30,9 +32,6 @@ */ public class TermComparisonWithHoles { - private static final Name HOLE_NAME = new Name("_"); - private static final Name HOLE_PREDICATE_NAME = new Name("__"); - private static final NameAbstractionTable FAILED = new NameAbstractionTable(); private final JTerm referenceTerm; @@ -40,6 +39,10 @@ public class TermComparisonWithHoles { this.referenceTerm = referenceTerm; } + TermComparisonWithHoles(TermWithHoles twh) { + this.referenceTerm = twh.term(); + } + public static boolean compare(JTerm referenceTerm, JTerm concreteTerm) { TermComparisonWithHoles comparator = new TermComparisonWithHoles(referenceTerm); return comparator.compareTo(concreteTerm); @@ -87,10 +90,10 @@ private static boolean unifyHelp(JTerm t0, JTerm t1, Operator op = t0.op(); if(op instanceof SortDependingFunction sdop) { - if(sdop.getKind().equals(HOLE_NAME)) { + if(sdop.getKind().equals(HOLE_SORT_DEP_NAME)) { return true; } - } else if(op.name().equals(HOLE_PREDICATE_NAME)) { + } else if(op.name().equals(HOLE_PREDICATE_NAME) || op.name().equals(HOLE_NAME)) { return true; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java new file mode 100644 index 00000000000..3c1ac754492 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java @@ -0,0 +1,105 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.scripts; + +import de.uka.ilkd.key.control.AbstractProofControl; +import de.uka.ilkd.key.java.Services; +import de.uka.ilkd.key.ldt.JavaDLTheory; +import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.logic.NamespaceSet; +import de.uka.ilkd.key.logic.op.JFunction; +import de.uka.ilkd.key.logic.op.SortDependingFunction; +import de.uka.ilkd.key.logic.sort.GenericSort; +import de.uka.ilkd.key.nparser.KeYParser; +import de.uka.ilkd.key.nparser.builder.ExpressionBuilder; +import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.proof.init.Profile; +import de.uka.ilkd.key.prover.impl.ApplyStrategy; +import de.uka.ilkd.key.scripts.meta.*; +import de.uka.ilkd.key.strategy.FocussedBreakpointRuleApplicationManager; +import de.uka.ilkd.key.strategy.Strategy; +import de.uka.ilkd.key.strategy.StrategyProperties; +import de.uka.ilkd.key.util.parsing.BuildingIssue; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +import org.key_project.logic.Name; +import org.key_project.logic.sort.AbstractSort; +import org.key_project.logic.sort.Sort; +import org.key_project.prover.engine.ProverCore; +import org.key_project.prover.strategy.RuleApplicationManager; +import org.key_project.util.collection.ImmutableArray; +import org.key_project.util.collection.ImmutableList; +import org.key_project.util.collection.ImmutableSLList; +import org.key_project.util.collection.ImmutableSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static de.uka.ilkd.key.strategy.StrategyProperties.*; + +@NullMarked +public record TermWithHoles(JTerm term) { + + public static final Name HOLE_NAME = new Name("_"); + public static final Name HOLE_PREDICATE_NAME = new Name("__"); + public static final Name HOLE_SORT_DEP_NAME = new Name("___"); + + private static final Logger LOGGER = LoggerFactory.getLogger(TermWithHoles.class); + + public boolean matches(JTerm other) { + return TermComparisonWithHoles.compareModHoles(term, other); + } + + private static class NothingSort extends AbstractSort { + private final Services services; + + public NothingSort(Services services) { + super(new Name("Nothing"), true); + this.services = services; + } + + @Override + public ImmutableSet extendsSorts() { + return ImmutableSet.from(services.getNamespaces().sorts().allElements()).remove(this); + } + + @Override + public boolean extendsTrans(Sort s) { + return true; + } + } + + + public static TermWithHoles fromParserContext(EngineState state, KeYParser.ProofScriptExpressionContext ctx) throws ScriptException { + var expressionBuilder = + new ExpressionBuilder(state.getProof().getServices(), enrichState(state)); + expressionBuilder.setAbbrevMap(state.getAbbreviations()); + JTerm t = (JTerm) ctx.accept(expressionBuilder); + List warnings = expressionBuilder.getBuildingIssues(); + warnings.forEach(it -> LOGGER.warn("{}", it)); + warnings.clear(); + return new TermWithHoles(t); + } + + private static NamespaceSet enrichState(EngineState state) { + NamespaceSet ns = state.getProof().getServices().getNamespaces().copy(); + + // Sort Nothing as bottom sort + NothingSort nothing = new NothingSort(state.getProof().getServices()); + ns.sorts().add(nothing); + + ns.functions().addSafely(new JFunction(HOLE_NAME, nothing)); + ns.functions().addSafely(new JFunction(HOLE_PREDICATE_NAME, JavaDLTheory.FORMULA)); + GenericSort g = new GenericSort(new Name("G")); + ns.functions().addSafely(SortDependingFunction.createFirstInstance(g, HOLE_SORT_DEP_NAME, g, + new Sort[0], false)); + return ns; + } + +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java index 8e2031a3277..4b4484c2a9f 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java @@ -132,7 +132,7 @@ public static class Parameters { @Documentation("The formula containing the quantifier for which a witness should be provided. Placeholders are allowed.") @Argument - public @MonotonicNonNull JTerm formula; + public @MonotonicNonNull TermWithHoles formula; } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java index d4a33e16384..fbe40fbac3d 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java @@ -6,6 +6,7 @@ import java.util.*; import java.util.stream.Collectors; +import de.uka.ilkd.key.nparser.KeYParser; import de.uka.ilkd.key.scripts.ProofScriptCommand; import de.uka.ilkd.key.scripts.ScriptCommandAst; @@ -287,16 +288,12 @@ private Object convert(ProofScriptArgument meta, Object val) public T convert(Class targetType, Object val) throws NoSpecifiedConverterException, ConversionException { var converter = (Converter) getConverter(targetType, val.getClass()); - if (converter == null) { - throw new NoSpecifiedConverterException( - "No converter registered for class: " + targetType + " from " + val.getClass()); - } try { return (T) converter.convert(val); } catch (Exception e) { throw new ConversionException( String.format("Could not convert value '%s' from type '%s' to type '%s'", - val, val.getClass(), targetType), + val, val.getClass().getSimpleName(), targetType.getSimpleName()), e); } } @@ -306,10 +303,6 @@ public T convert(Object val, Class type) @SuppressWarnings("unchecked") var converter = (Converter) getConverter(type, val.getClass()); - if (converter == null) { - throw new NoSpecifiedConverterException( - "No converter registered for class: " + type + " from " + val.getClass(), null); - } try { return converter.convert(val); } catch (Exception e) { @@ -347,11 +340,16 @@ public void addConverter(Converter conv) { * converter is known. */ @SuppressWarnings("unchecked") - public @Nullable Converter getConverter(Class ret, Class arg) { + public @NonNull Converter getConverter(Class ret, Class arg) throws NoSpecifiedConverterException { if (ret.isAssignableFrom(arg)) { return (T it) -> (R) it; } - return (Converter) converters.get(new ConverterKey<>(ret, arg)); + Converter result = (Converter) converters.get(new ConverterKey<>(ret, arg)); + if (result == null) { + throw new NoSpecifiedConverterException( + "No converter registered for class: " + ret.getName() + " from " + arg.getName()); + } + return result; } @Override diff --git a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaHeader.key b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaHeader.key index 30620d309ba..5b59a4205f2 100644 --- a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaHeader.key +++ b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaHeader.key @@ -28,9 +28,9 @@ alpha alpha::cast(any); boolean alpha::exactInstance(any); boolean alpha::instance(any); - alpha alpha::_; // for matching in scripts + // alpha alpha::_; // for matching in scripts } \predicates { - __; // for matching scripts + // __; // for matching scripts } diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java index d9e8d2203b8..1e80bf7ac56 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java @@ -40,7 +40,7 @@ */ public class TestProofScriptCommand { - private static final String ONLY_CASE = "witness2.yml"; + private static final String ONLY_CASE = null; private static final Logger LOGGER = LoggerFactory.getLogger(TestProofScriptCommand.class); public record TestInstance( @@ -103,6 +103,7 @@ void testProofScript(TestInstance data, String name) throws Exception { try { pse.execute(env.getUi(), script); } catch (ScriptException ex) { + ex.printStackTrace(); assertTrue(data.exception != null && !data.exception.isEmpty(), "An exception was not expected, but got " + ex.getMessage()); // weigl: fix spurious error on Windows machine due to different file endings. diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml new file mode 100644 index 00000000000..c23c2c93cc6 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml @@ -0,0 +1,6 @@ +key: | + \problem { ==> \forall int x; x=x, \forall int x; (x > 0 | x < 2) } +script: | + witness (\forall int x; (__ | x < _)) as="y"; +goals: + - ==> \forall int x; x = x, y > 0 | y < 2 \ No newline at end of file diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/instantiate1.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/instantiate1.yml new file mode 100644 index 00000000000..30f9b28acd0 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/instantiate1.yml @@ -0,0 +1,6 @@ +key: | + \problem { \exists int x; x>0 } +script: | + instantiate var="x" with=3 hide:true; +goals: + - ==> 3 > 0 diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness1.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness1.yml index 6a6d3b9f5cb..0edb8b5ccf6 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness1.yml +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness1.yml @@ -1,6 +1,6 @@ key: | \problem { ==> \forall int x; x=x, \forall int x; (x > 0 | x < 2) } script: | - witness (\forall int x; (__ | __)) as="y"; + witness (\forall int x; (x>0 | x<2)) as="y"; goals: - - ==> \forall int x; x = x, y > 0 | y < 2 \ No newline at end of file + - ==> \forall int x; x = x, y > 0 | y < 2 diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness2.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness2.yml index 9dada861b16..69e2d245925 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness2.yml +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness2.yml @@ -4,5 +4,5 @@ key: | \programVariables { int x3; } \problem { ==> \forall int x; x=x, \forall int x; (x > 0 | x < x1) } script: | - witness (\forall int x; (__ | __)) as="x1"; + witness (\forall int x; (x > 0 | x < x1)) as="x1"; exception: "Name already used as function or predicate: x1" diff --git a/key.util/src/main/java/org/key_project/util/collection/ImmutableSet.java b/key.util/src/main/java/org/key_project/util/collection/ImmutableSet.java index 7eab97f1b7a..2d633534012 100644 --- a/key.util/src/main/java/org/key_project/util/collection/ImmutableSet.java +++ b/key.util/src/main/java/org/key_project/util/collection/ImmutableSet.java @@ -9,6 +9,7 @@ import java.util.stream.Collector.Characteristics; import java.util.stream.Stream; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; /** @@ -43,6 +44,14 @@ public interface ImmutableSet return DefaultImmutableSet.nil(); } + static ImmutableSet from(Iterable ts) { + ImmutableSet result = DefaultImmutableSet.nil(); + for (T t : ts) { + result = result.add(t); + } + return result; + } + /** * @return a {@code Set} containing the same elements as this {@code ImmutableSet} */ From 4066f7c2065ba95d124be35d294306a97367fa5e Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 10 Oct 2025 13:49:47 +0200 Subject: [PATCH 61/90] improved treatment of terms with holes --- .../de/uka/ilkd/key/scripts/EngineState.java | 5 +++ .../de/uka/ilkd/key/scripts/RuleCommand.java | 6 +-- .../key/scripts/TermComparisonWithHoles.java | 28 ++---------- .../uka/ilkd/key/scripts/TermWithHoles.java | 43 +++++++++++++++++-- .../uka/ilkd/key/scripts/WitnessCommand.java | 2 +- .../de/uka/ilkd/key/scripts/cases/holes1.yml | 2 +- 6 files changed, 53 insertions(+), 33 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java index 54f8a7b98f2..60de949aea1 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java @@ -81,6 +81,9 @@ public EngineState(Proof proof, ProofScriptEngine engine) { this.engine = engine; } + // Problem is: This is all KeY specific and there should be different converters + // for JML. But how to separate this? Probably do this externally after PSE generation. + // via some "enrichValueInjector" method in a different class ... private ValueInjector createDefaultValueInjector() { var v = ValueInjector.createDefault(); v.addConverter(JTerm.class, String.class, (str) -> this.toTerm(str, null)); @@ -88,6 +91,8 @@ private ValueInjector createDefaultValueInjector() { v.addConverter(Sort.class, String.class, this::toSort); v.addConverter(TermWithHoles.class, ProofScriptExpressionContext.class, (ctx) -> TermWithHoles.fromParserContext(this, ctx)); + v.addConverter(TermWithHoles.class, String.class, + (str) -> TermWithHoles.fromString(this, str)); addContextTranslator(v, String.class); addContextTranslator(v, JTerm.class); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java index 3cfa634f7bf..9372eba5ef6 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java @@ -368,11 +368,11 @@ private static String formatTermString(String str) { private List filterList(Services services, Parameters p, ImmutableList list) { List matchingApps = new ArrayList<>(); + TermComparisonWithHoles matcher = p.on == null ? null : p.on.getMatcher(); for (TacletApp tacletApp : list) { if (tacletApp instanceof PosTacletApp pta) { JTerm term = (JTerm) pta.posInOccurrence().subTerm(); - boolean add = p.on == null - || TermComparisonWithHoles.compareModHoles(p.on, term); + boolean add = p.on == null || p.on.matches(term); for (var entry : pta.instantiations().getInstantiationMap()) { final SchemaVariable sv = entry.key(); @@ -410,7 +410,7 @@ public static class Parameters { @Option(value = "on") @Documentation("Term on which the rule should be applied to (matching the 'find' clause of the rule). " + "This may contain placeholders.") - public @Nullable JTerm on; + public @Nullable TermWithHoles on; @Option(value = "formula") @Documentation("Top-level formula in which the term appears. This may contain placeholders.") diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java index c9aa98bffa2..ce39ec9135d 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java @@ -4,8 +4,6 @@ import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.op.*; import org.jspecify.annotations.Nullable; -import org.key_project.logic.Name; -import org.key_project.logic.Property; import org.key_project.logic.op.Operator; import org.key_project.logic.op.QuantifiableVariable; import org.key_project.prover.sequent.Sequent; @@ -39,16 +37,7 @@ public class TermComparisonWithHoles { this.referenceTerm = referenceTerm; } - TermComparisonWithHoles(TermWithHoles twh) { - this.referenceTerm = twh.term(); - } - - public static boolean compare(JTerm referenceTerm, JTerm concreteTerm) { - TermComparisonWithHoles comparator = new TermComparisonWithHoles(referenceTerm); - return comparator.compareTo(concreteTerm); - } - - public final boolean compareTo(JTerm t) { + public final boolean matches(JTerm t) { if (referenceTerm == t) { return true; } @@ -58,17 +47,6 @@ public final boolean compareTo(JTerm t) { null); } - public static boolean compareModHoles(JTerm t1, JTerm t2) { - if (t1 == t2) { - return true; - } - return unifyHelp(t1, t2, - ImmutableSLList.nil(), - ImmutableSLList.nil(), - null); - } - - /** * Compares two terms modulo bound renaming * @@ -244,12 +222,12 @@ private static boolean descendRecursively(JTerm t0, JTerm t1, public List> findMatchesInSequent(Sequent sequent) { List> matches = new ArrayList<>(); for (SequentFormula sf : sequent.antecedent()) { - if (compareTo((JTerm) sf.formula())) { + if (matches((JTerm) sf.formula())) { matches.add(new Pair<>(true, sf)); } } for (SequentFormula sf : sequent.succedent()) { - if (compareTo((JTerm) sf.formula())) { + if (matches((JTerm) sf.formula())) { matches.add(new Pair<>(false, sf)); } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java index 3c1ac754492..c116a50c0c2 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java @@ -12,6 +12,8 @@ import de.uka.ilkd.key.logic.op.SortDependingFunction; import de.uka.ilkd.key.logic.sort.GenericSort; import de.uka.ilkd.key.nparser.KeYParser; +import de.uka.ilkd.key.nparser.KeyAst; +import de.uka.ilkd.key.nparser.ParsingFacade; import de.uka.ilkd.key.nparser.builder.ExpressionBuilder; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Proof; @@ -22,6 +24,8 @@ import de.uka.ilkd.key.strategy.Strategy; import de.uka.ilkd.key.strategy.StrategyProperties; import de.uka.ilkd.key.util.parsing.BuildingIssue; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.ParserRuleContext; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -43,8 +47,33 @@ import static de.uka.ilkd.key.strategy.StrategyProperties.*; +/* + +y < (x+0) + y + (x+0), +a -> y < (x+0) + y + (x+0) + + +rule plus_zero on: ????? + +---> a -> y < (x+0) + y + FOCUS(x+0) + +---> _ -> (... _ + (_+0) ...) + +---> _ -> _find(_ + _focus(_ + 0)) + +---> ? -> ?find(? + ?focus(? + 0)) + +---> ?_ -> ?find(?_ + ?focus(_ + 0)) + */ + @NullMarked -public record TermWithHoles(JTerm term) { +public class TermWithHoles { + + private final JTerm term; + + public TermWithHoles(JTerm term) { + this.term = term; + } public static final Name HOLE_NAME = new Name("_"); public static final Name HOLE_PREDICATE_NAME = new Name("__"); @@ -53,7 +82,11 @@ public record TermWithHoles(JTerm term) { private static final Logger LOGGER = LoggerFactory.getLogger(TermWithHoles.class); public boolean matches(JTerm other) { - return TermComparisonWithHoles.compareModHoles(term, other); + return getMatcher().matches(other); + } + + public TermComparisonWithHoles getMatcher() { + return new TermComparisonWithHoles(term); } private static class NothingSort extends AbstractSort { @@ -75,8 +108,12 @@ public boolean extendsTrans(Sort s) { } } + public static TermWithHoles fromString(EngineState engineState, String str) { + KeyAst.Term term = ParsingFacade.parseExpression(CharStreams.fromString(str)); + return fromParserContext(engineState, term.ctx); + } - public static TermWithHoles fromParserContext(EngineState state, KeYParser.ProofScriptExpressionContext ctx) throws ScriptException { + public static TermWithHoles fromParserContext(EngineState state, ParserRuleContext ctx) { var expressionBuilder = new ExpressionBuilder(state.getProof().getServices(), enrichState(state)); expressionBuilder.setAbbrevMap(state.getAbbreviations()); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java index 4b4484c2a9f..84b22f7382c 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java @@ -59,7 +59,7 @@ public void execute(ScriptCommandAst ast) throws ScriptException, InterruptedExc Goal goal = state.getFirstOpenAutomaticGoal(); Services services = state.getProof().getServices(); - TermComparisonWithHoles comp = new TermComparisonWithHoles(params.formula); + TermComparisonWithHoles comp = params.formula.getMatcher(); // First component: true for antecedent, false for succedent Pair match = comp.findUniqueMatchInSequent(goal.node().sequent()); diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml index c23c2c93cc6..9c8a4edb471 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml @@ -1,6 +1,6 @@ key: | \problem { ==> \forall int x; x=x, \forall int x; (x > 0 | x < 2) } script: | - witness (\forall int x; (__ | x < _)) as="y"; + witness (\forall int x; (_ | x < _)) as="y"; goals: - ==> \forall int x; x = x, y > 0 | y < 2 \ No newline at end of file From a46208fac473e8fabf7247017830d2ba89cf9479 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 10 Oct 2025 15:52:13 +0200 Subject: [PATCH 62/90] introduce match identifiers with leading '?' --- key.core/src/main/antlr4/KeYLexer.g4 | 1 + key.core/src/main/antlr4/KeYParser.g4 | 4 +++- .../de/uka/ilkd/key/nparser/builder/DefaultBuilder.java | 2 +- .../main/java/de/uka/ilkd/key/scripts/TermWithHoles.java | 6 +++--- .../test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/key.core/src/main/antlr4/KeYLexer.g4 b/key.core/src/main/antlr4/KeYLexer.g4 index 73572088b3a..f0e361daf69 100644 --- a/key.core/src/main/antlr4/KeYLexer.g4 +++ b/key.core/src/main/antlr4/KeYLexer.g4 @@ -394,6 +394,7 @@ LESSEQUAL: '<' '=' | '\u2264'; LGUILLEMETS: '<' '<' | '«' | '‹'; RGUILLEMETS: '>''>' | '»' | '›'; IMPLICIT_IDENT: '<' '$'? (LETTER)+ '>' ('$lmtd')? -> type(IDENT); +MATCH_IDENT: '?' IDENT?; EQV: '<->' | '\u2194'; CHAR_LITERAL diff --git a/key.core/src/main/antlr4/KeYParser.g4 b/key.core/src/main/antlr4/KeYParser.g4 index 5832a7238f7..355cf218cd1 100644 --- a/key.core/src/main/antlr4/KeYParser.g4 +++ b/key.core/src/main/antlr4/KeYParser.g4 @@ -7,6 +7,7 @@ parser grammar KeYParser; @members { private SyntaxErrorReporter errorReporter = new SyntaxErrorReporter(getClass()); public SyntaxErrorReporter getErrorReporter() { return errorReporter;} +public boolean allowMatchId = false; // used in proof script parsing } options { tokenVocab=KeYLexer; } // use tokens from STLexer.g4 @@ -154,6 +155,7 @@ string_value: STRING_LITERAL; simple_ident : id=IDENT + | {allowMatchId}? id=MATCH_IDENT ; simple_ident_comma_list @@ -901,7 +903,7 @@ proofScriptExpression: | integer | floatnum | string_literal - | LPAREN (term | seq) RPAREN + | LPAREN {allowMatchId=true;} (term | seq) {allowMatchId=false;} RPAREN | simple_ident | abbreviation | literals diff --git a/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/DefaultBuilder.java b/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/DefaultBuilder.java index 4ecd37159ca..2f409400c19 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/DefaultBuilder.java +++ b/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/DefaultBuilder.java @@ -322,7 +322,7 @@ public Object visitSimple_ident_dots_comma_list( @Override public String visitSimple_ident(KeYParser.Simple_identContext ctx) { - return ctx.IDENT().getText(); + return ctx.id.getText(); } @Override diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java index c116a50c0c2..c8645755c7c 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java @@ -75,9 +75,9 @@ public TermWithHoles(JTerm term) { this.term = term; } - public static final Name HOLE_NAME = new Name("_"); - public static final Name HOLE_PREDICATE_NAME = new Name("__"); - public static final Name HOLE_SORT_DEP_NAME = new Name("___"); + public static final Name HOLE_NAME = new Name("?"); + public static final Name HOLE_PREDICATE_NAME = new Name("?fml"); + public static final Name HOLE_SORT_DEP_NAME = new Name("?"); private static final Logger LOGGER = LoggerFactory.getLogger(TermWithHoles.class); diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml index 9c8a4edb471..d1b61bb7092 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml @@ -1,6 +1,6 @@ key: | \problem { ==> \forall int x; x=x, \forall int x; (x > 0 | x < 2) } script: | - witness (\forall int x; (_ | x < _)) as="y"; + witness (\forall int x; (? | x < ?)) as="y"; goals: - ==> \forall int x; x = x, y > 0 | y < 2 \ No newline at end of file From 3f306274e3bfd64193c7c245961f054390df658e Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Thu, 18 Jul 2024 16:44:04 +0200 Subject: [PATCH 63/90] introducing sort::FOCUS for usage in scripts --- .../de/uka/ilkd/key/scripts/RuleCommand.java | 3 +- .../key/scripts/TermComparisonWithHoles.java | 66 +++++++++++++++---- .../uka/ilkd/key/scripts/TermWithHoles.java | 14 +++- .../uka/ilkd/key/scripts/WitnessCommand.java | 2 +- .../de/uka/ilkd/key/scripts/cases/focus1.yml | 7 ++ .../java/org/key_project/logic/PosInTerm.java | 7 ++ 6 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focus1.yml diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java index 9372eba5ef6..88b328815a0 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java @@ -371,8 +371,7 @@ private List filterList(Services services, Parameters p, TermComparisonWithHoles matcher = p.on == null ? null : p.on.getMatcher(); for (TacletApp tacletApp : list) { if (tacletApp instanceof PosTacletApp pta) { - JTerm term = (JTerm) pta.posInOccurrence().subTerm(); - boolean add = p.on == null || p.on.matches(term); + boolean add = matcher == null || matcher.matches(pta.posInOccurrence()); for (var entry : pta.instantiations().getInstantiationMap()) { final SchemaVariable sv = entry.key(); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java index ce39ec9135d..9649514cb69 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java @@ -2,10 +2,14 @@ import de.uka.ilkd.key.java.NameAbstractionTable; import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.logic.equality.RenamingTermProperty; import de.uka.ilkd.key.logic.op.*; import org.jspecify.annotations.Nullable; +import org.key_project.logic.PosInTerm; +import org.key_project.logic.Term; import org.key_project.logic.op.Operator; import org.key_project.logic.op.QuantifiableVariable; +import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.prover.sequent.Sequent; import org.key_project.prover.sequent.SequentFormula; import org.key_project.util.collection.ImmutableList; @@ -22,9 +26,6 @@ * All term labels are ignored in this equality check. Additionally, holes (represented by the * SortDependingFunction with name "_" and the Predicate with name "__") are treated as wildcards that * match any subterm. - *

    - * The single instance of this property can be accessed through - * {@link TermComparisonWithHoles#INSTANCE}. * * @author Mattias Ulbrich */ @@ -37,16 +38,56 @@ public class TermComparisonWithHoles { this.referenceTerm = referenceTerm; } - public final boolean matches(JTerm t) { - if (referenceTerm == t) { + public final boolean matches(PosInOccurrence pio) { + JTerm term = (JTerm) pio.subTerm(); + if (term.equalsModProperty(referenceTerm, RenamingTermProperty.RENAMING_TERM_PROPERTY)) { return true; } - return unifyHelp(referenceTerm, t, + + PosInTerm focus = findFocus(referenceTerm); + if(focus != null) { + for(int i = focus.depth() -1; i >= 0; i--) { + int focusIdx = focus.getIndexAt(i); + int termIdx = pio.posInTerm().getIndexAt(pio.depth() - 1); + if(focusIdx != termIdx) { + // the focus is not at the same position as the current term + return false; + } + pio = pio.up(); + } + term = (JTerm) pio.subTerm(); + } + + return unifyHelp(referenceTerm, term, ImmutableSLList.nil(), ImmutableSLList.nil(), null); + + } + + public final boolean matchesToplevel(SequentFormula sf) { + // we use antecedent here since it does not matter and is never read ... + return matches(new PosInOccurrence(sf, PosInTerm.getTopLevel(), true)); + } + + private static @Nullable PosInTerm findFocus(Term pattern) { + var op = pattern.op(); + if (op instanceof JFunction) { + if(op.name().equals(TermWithHoles.FOCUS_NAME)) { + return PosInTerm.getTopLevel(); + } + } + for (int i = 0; i < pattern.arity(); i++) { + Term sub = pattern.sub(i); + PosInTerm subFocus = findFocus(sub); + if(subFocus != null) { + return PosInTerm.of((char)i, subFocus); + } + } + return null; } + /** * Compares two terms modulo bound renaming * @@ -73,6 +114,9 @@ private static boolean unifyHelp(JTerm t0, JTerm t1, } } else if(op.name().equals(HOLE_PREDICATE_NAME) || op.name().equals(HOLE_NAME)) { return true; + } else if(op.name().equals(FOCUS_NAME)) { + // ignore the "focus" annotation + return unifyHelp(t0.sub(0), t1, ownBoundVars, cmpBoundVars, nat); } @@ -219,15 +263,15 @@ private static boolean descendRecursively(JTerm t0, JTerm t1, return true; } - public List> findMatchesInSequent(Sequent sequent) { + public List> findTopLevelMatchesInSequent(Sequent sequent) { List> matches = new ArrayList<>(); for (SequentFormula sf : sequent.antecedent()) { - if (matches((JTerm) sf.formula())) { + if (matchesToplevel(sf)) { matches.add(new Pair<>(true, sf)); } } for (SequentFormula sf : sequent.succedent()) { - if (matches((JTerm) sf.formula())) { + if (matchesToplevel(sf)) { matches.add(new Pair<>(false, sf)); } } @@ -235,8 +279,8 @@ public List> findMatchesInSequent(Sequent sequent) } - public @Nullable Pair findUniqueMatchInSequent(Sequent sequent) { - List> matches = findMatchesInSequent(sequent); + public @Nullable Pair findUniqueToplevelMatchInSequent(Sequent sequent) { + List> matches = findTopLevelMatchesInSequent(sequent); if (matches.size() != 1) { return null; } else { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java index c8645755c7c..4062b3428f2 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java @@ -33,6 +33,8 @@ import org.key_project.logic.sort.AbstractSort; import org.key_project.logic.sort.Sort; import org.key_project.prover.engine.ProverCore; +import org.key_project.prover.sequent.PosInOccurrence; +import org.key_project.prover.sequent.SequentFormula; import org.key_project.prover.strategy.RuleApplicationManager; import org.key_project.util.collection.ImmutableArray; import org.key_project.util.collection.ImmutableList; @@ -70,19 +72,24 @@ public class TermWithHoles { private final JTerm term; - public TermWithHoles(JTerm term) { this.term = term; } + public static final Name HOLE_NAME = new Name("?"); public static final Name HOLE_PREDICATE_NAME = new Name("?fml"); public static final Name HOLE_SORT_DEP_NAME = new Name("?"); + public static final Name FOCUS_NAME = new Name("?focus"); private static final Logger LOGGER = LoggerFactory.getLogger(TermWithHoles.class); - public boolean matches(JTerm other) { - return getMatcher().matches(other); + public boolean matches(PosInOccurrence posInOccurrence) { + return getMatcher().matches(posInOccurrence); + } + + public boolean matchesToplevel(SequentFormula sf) { + return getMatcher().matchesToplevel(sf); } public TermComparisonWithHoles getMatcher() { @@ -133,6 +140,7 @@ private static NamespaceSet enrichState(EngineState state) { ns.functions().addSafely(new JFunction(HOLE_NAME, nothing)); ns.functions().addSafely(new JFunction(HOLE_PREDICATE_NAME, JavaDLTheory.FORMULA)); + ns.functions().addSafely(new JFunction(FOCUS_NAME, nothing, JavaDLTheory.ANY)); GenericSort g = new GenericSort(new Name("G")); ns.functions().addSafely(SortDependingFunction.createFirstInstance(g, HOLE_SORT_DEP_NAME, g, new Sort[0], false)); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java index 84b22f7382c..c5d7f94721e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java @@ -62,7 +62,7 @@ public void execute(ScriptCommandAst ast) throws ScriptException, InterruptedExc TermComparisonWithHoles comp = params.formula.getMatcher(); // First component: true for antecedent, false for succedent - Pair match = comp.findUniqueMatchInSequent(goal.node().sequent()); + Pair match = comp.findUniqueToplevelMatchInSequent(goal.node().sequent()); if (match == null) { throw new ScriptException("Cannot unique match the formula argument"); } diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focus1.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focus1.yml new file mode 100644 index 00000000000..3387b6cd31e --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focus1.yml @@ -0,0 +1,7 @@ +key: | + \programVariables { int a; } + \problem { ==> a + 0 = a + 0 & 1 = 1 } +script: | + rule add_zero_right on:(a+0 = ?focus(a+0)); +goals: + - ==> a + 0 = a & 1 = 1 \ No newline at end of file diff --git a/key.ncore/src/main/java/org/key_project/logic/PosInTerm.java b/key.ncore/src/main/java/org/key_project/logic/PosInTerm.java index b705e697bf2..b9b3c59ba1a 100644 --- a/key.ncore/src/main/java/org/key_project/logic/PosInTerm.java +++ b/key.ncore/src/main/java/org/key_project/logic/PosInTerm.java @@ -82,6 +82,13 @@ public static PosInTerm parseReverseString(String s) { : new PosInTerm(positions, (char) positions.length, true); } + public static PosInTerm of(char index, PosInTerm subPos) { + var newPositions = new char[subPos.size + 1]; + System.arraycopy(subPos.positions, 0, newPositions, 1, subPos.size); + newPositions[0] = index; + return new PosInTerm(newPositions, (char) (subPos.size + 1), true); + } + /// returns the instance representing the top level position /// /// @return the top level position From 607f47cdc9f54331120401700490b2ae2a7b4141 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Thu, 18 Jul 2024 17:04:14 +0200 Subject: [PATCH 64/90] introducing sort::ELLIP for ellipsis search patterns --- .../ilkd/key/scripts/TermComparisonWithHoles.java | 14 ++++++++++++++ .../de/uka/ilkd/key/scripts/TermWithHoles.java | 2 ++ .../de/uka/ilkd/key/scripts/cases/find1.yml | 7 +++++++ 3 files changed, 23 insertions(+) create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/find1.yml diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java index 9649514cb69..77c88905c79 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java @@ -17,7 +17,9 @@ import org.key_project.util.collection.Pair; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import static de.uka.ilkd.key.scripts.TermWithHoles.*; @@ -117,6 +119,13 @@ private static boolean unifyHelp(JTerm t0, JTerm t1, } else if(op.name().equals(FOCUS_NAME)) { // ignore the "focus" annotation return unifyHelp(t0.sub(0), t1, ownBoundVars, cmpBoundVars, nat); + } else if(op.name().equals(ELLIPSIS_NAME)) { + // return true if it hits one subterm ... + Set deepAllSubs = new HashSet<>(); + computeSubterms(t1, deepAllSubs); + var lookfor = t0.sub(0); + var finalNat = nat; + return deepAllSubs.stream().anyMatch(t -> unifyHelp(lookfor, t, ownBoundVars, cmpBoundVars, finalNat)); } @@ -145,6 +154,11 @@ private static boolean unifyHelp(JTerm t0, JTerm t1, return descendRecursively(t0, t1, ownBoundVars, cmpBoundVars, nat); } + private static void computeSubterms(JTerm t, Set deepAllSubs) { + deepAllSubs.add(t); + t.subs().stream().forEach(sub -> computeSubterms(sub, deepAllSubs)); + } + private static boolean handleQuantifiableVariable(JTerm t0, JTerm t1, ImmutableList ownBoundVars, ImmutableList cmpBoundVars) { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java index 4062b3428f2..70bb08d5e47 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java @@ -81,6 +81,7 @@ public TermWithHoles(JTerm term) { public static final Name HOLE_PREDICATE_NAME = new Name("?fml"); public static final Name HOLE_SORT_DEP_NAME = new Name("?"); public static final Name FOCUS_NAME = new Name("?focus"); + public static final Name ELLIPSIS_NAME = new Name("?find"); private static final Logger LOGGER = LoggerFactory.getLogger(TermWithHoles.class); @@ -141,6 +142,7 @@ private static NamespaceSet enrichState(EngineState state) { ns.functions().addSafely(new JFunction(HOLE_NAME, nothing)); ns.functions().addSafely(new JFunction(HOLE_PREDICATE_NAME, JavaDLTheory.FORMULA)); ns.functions().addSafely(new JFunction(FOCUS_NAME, nothing, JavaDLTheory.ANY)); + ns.functions().addSafely(new JFunction(ELLIPSIS_NAME, nothing, JavaDLTheory.ANY)); GenericSort g = new GenericSort(new Name("G")); ns.functions().addSafely(SortDependingFunction.createFirstInstance(g, HOLE_SORT_DEP_NAME, g, new Sort[0], false)); diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/find1.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/find1.yml new file mode 100644 index 00000000000..26ea900fee9 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/find1.yml @@ -0,0 +1,7 @@ +key: | + \programVariables { int a; } + \problem { ==> ((1 + 1) + 5) + 0 = 7 & ((0 + 2) + 5) + 0 = 7 } +script: | + rule add_zero_right on:(?find(1+1)); +goals: + - ==> 1 + 1 + 5 = 7 & 0 + 2 + 5 + 0 = 7 \ No newline at end of file From f5c1d230e887dcf1c490408c85a1544d91a4a75d Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 10 Oct 2025 22:18:54 +0200 Subject: [PATCH 65/90] improving sophisticated term matching there are failing cases ... --- .../ilkd/key/scripts/ScriptCommandAst.java | 4 +- .../key/scripts/TermComparisonWithHoles.java | 5 +- .../java/de/uka/ilkd/key/util/ANTLRUtil.java | 96 +++++++++++++++++++ .../key/scripts/TestProofScriptCommand.java | 10 +- .../de/uka/ilkd/key/scripts/cases/find2.yml | 7 ++ .../de/uka/ilkd/key/scripts/cases/focus2.yml | 8 ++ .../ilkd/key/scripts/cases/focusOfFind.yml | 7 ++ .../de/uka/ilkd/key/scripts/cases/holes1.yml | 2 +- .../ilkd/key/scripts/cases/instantiate1.yml | 2 +- .../uka/ilkd/key/scripts/cases/witness1.yml | 2 +- 10 files changed, 135 insertions(+), 8 deletions(-) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/util/ANTLRUtil.java create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/find2.yml create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focus2.yml create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focusOfFind.yml diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommandAst.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommandAst.java index 69bc8c728ea..4fe7bfc0f01 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommandAst.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommandAst.java @@ -11,6 +11,7 @@ import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.parser.Location; +import de.uka.ilkd.key.util.ANTLRUtil; import org.antlr.v4.runtime.ParserRuleContext; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -73,12 +74,11 @@ public static String asReadableString(Object value) { if (value instanceof ScriptBlock b) { return b.asCommandLine(); } - if (value instanceof KeYParser.ProofScriptCodeBlockContext ctx) { asReadableString(KeyAst.ProofScript.asAst(null, ctx)); } if (value instanceof ParserRuleContext ctx) { - return ctx.getText(); + return ANTLRUtil.reconstructOriginal(ctx); } return Objects.toString(value); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java index 77c88905c79..a498b4cd685 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java @@ -33,7 +33,6 @@ */ public class TermComparisonWithHoles { - private static final NameAbstractionTable FAILED = new NameAbstractionTable(); private final JTerm referenceTerm; TermComparisonWithHoles(JTerm referenceTerm) { @@ -49,6 +48,10 @@ public final boolean matches(PosInOccurrence pio) { PosInTerm focus = findFocus(referenceTerm); if(focus != null) { for(int i = focus.depth() -1; i >= 0; i--) { + if(pio.isTopLevel()) { + // focus is deeper than the current term + return false; + } int focusIdx = focus.getIndexAt(i); int termIdx = pio.posInTerm().getIndexAt(pio.depth() - 1); if(focusIdx != termIdx) { diff --git a/key.core/src/main/java/de/uka/ilkd/key/util/ANTLRUtil.java b/key.core/src/main/java/de/uka/ilkd/key/util/ANTLRUtil.java new file mode 100644 index 00000000000..c4be36280ae --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/util/ANTLRUtil.java @@ -0,0 +1,96 @@ +package de.uka.ilkd.key.util; + +import java.util.ArrayList; +import java.util.List; + +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.TerminalNode; +import org.jspecify.annotations.NullMarked; + +/** + * Utility to reconstruct the original source text from a given ANTLR4 parse tree. + * + * It is not 100% accurate, but should be good enough for most use cases. + * + * @author Mattias Ulbrich, or rather ChatGPT 5 mini + */ +@NullMarked +public final class ANTLRUtil { + private ANTLRUtil() { } + + /** + * Reconstructs the original source text from a given ANTLR4 parse tree. + */ + public static String reconstructOriginal(ParseTree tree) { + List terminals = new ArrayList<>(); + collectTerminals(tree, terminals); + + StringBuilder sb = new StringBuilder(); + int curLine = -1; + int curCol = 0; + + for (TerminalNode tn : terminals) { + Token t = tn.getSymbol(); + if (t == null) continue; + if (t.getType() == Token.EOF) continue; + + if(curLine == -1) { + // use first token to initialize line and column + curCol = t.getCharPositionInLine(); + curLine = t.getLine(); + } + + int line = t.getLine(); + int col = t.getCharPositionInLine(); + + // add newlines to reach the desired line + while (curLine < line) { + sb.append('\n'); + curLine++; + curCol = 0; + } + + // add spaces to reach the desired column + int pad = col - curCol; + if (pad > 0) { + sb.append(" ".repeat(pad)); + curCol = col; + } + + String text = t.getText(); + if (text == null) text = ""; + + sb.append(text); + + // update current position + int newlines = countOccurrences(text, '\n'); + if (newlines > 0) { + curLine += newlines; + int lastNl = text.lastIndexOf('\n'); + curCol = text.length() - lastNl - 1; + } else { + curCol += text.length(); + } + } + + return sb.toString(); + } + + private static void collectTerminals(ParseTree node, List out) { + if (node == null) return; + if (node instanceof TerminalNode) { + out.add((TerminalNode) node); + return; + } + for (int i = 0; i < node.getChildCount(); i++) { + collectTerminals(node.getChild(i), out); + } + } + + private static int countOccurrences(String s, char c) { + int cnt = 0; + for (int i = 0; i < s.length(); i++) if (s.charAt(i) == c) cnt++; + return cnt; + } +} \ No newline at end of file diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java index 1e80bf7ac56..5413957143d 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java @@ -15,6 +15,8 @@ import de.uka.ilkd.key.control.KeYEnvironment; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.nparser.ParsingFacade; +import de.uka.ilkd.key.pp.LogicPrinter; +import de.uka.ilkd.key.pp.NotationInfo; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.smt.newsmt2.MasterHandlerTest; @@ -30,7 +32,6 @@ import org.junit.jupiter.params.provider.MethodSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import recoder.util.Debug; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -124,7 +125,7 @@ void testProofScript(TestInstance data, String name) throws Exception { Assertions.assertEquals(expected, goals.size()); for (String expectedGoal : data.goals()) { - assertThat(goals.head().toString().trim()).isEqualTo(expectedGoal); + assertThat(normaliseSpace(goals.head().toString().trim())).isEqualTo(expectedGoal); goals = goals.tail(); } @@ -135,4 +136,9 @@ void testProofScript(TestInstance data, String name) throws Exception { } } + // For some layout reasons the toString may add linebreaks and spaces + private static String normaliseSpace(String str) { + return str.replaceAll("\\s+", " "); + } + } diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/find2.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/find2.yml new file mode 100644 index 00000000000..3af65ac6ad8 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/find2.yml @@ -0,0 +1,7 @@ +key: | + \programVariables { int a; } + \problem { ==> 1 + 0 = 1 & 2 + 0 = 2 & 3 + 0 = 3 } +script: | + rule add_zero_right on:(?find(2)); +goals: + - ==> 1 + 0 = 1 & 0 + 2 + 5 = 7 \ No newline at end of file diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focus2.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focus2.yml new file mode 100644 index 00000000000..f776bc20567 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focus2.yml @@ -0,0 +1,8 @@ +# Used to identify and hunt a bug. +# Raised a -1 illegal index exception +key: | + \problem { ==> 1 + 0 + 0 + 0 + 0 >= 0 } +script: | + rule add_zero_right on:(?focus(?) + ? + ? + ?); +goals: + - ==> 1 + 0 + 0 + 0 >= 0 \ No newline at end of file diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focusOfFind.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focusOfFind.yml new file mode 100644 index 00000000000..267325d1c12 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focusOfFind.yml @@ -0,0 +1,7 @@ +key: | + \programVariables { int a; } + \problem { ==> (0+3)+0 = (0+2+1)+0 & (0+1+1+1)+0 = (0+3)+0 } +script: | + rule add_zero_right on:(? = ?focus(?find(3))); +goals: + - ==> 0 + 3 + 0 = 0 + 2 + 1 + 0 & 0 + 1 + 1 + 1 + 0 = 0 + 3 \ No newline at end of file diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml index d1b61bb7092..c73fcf31c8d 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes1.yml @@ -3,4 +3,4 @@ key: | script: | witness (\forall int x; (? | x < ?)) as="y"; goals: - - ==> \forall int x; x = x, y > 0 | y < 2 \ No newline at end of file + - ==> \forall int x; x = x, y > 0 | y < 2 \ No newline at end of file diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/instantiate1.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/instantiate1.yml index 30f9b28acd0..66bebb3403e 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/instantiate1.yml +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/instantiate1.yml @@ -3,4 +3,4 @@ key: | script: | instantiate var="x" with=3 hide:true; goals: - - ==> 3 > 0 + - ==> 3 > 0 diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness1.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness1.yml index 0edb8b5ccf6..da74c45bd51 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness1.yml +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/witness1.yml @@ -3,4 +3,4 @@ key: | script: | witness (\forall int x; (x>0 | x<2)) as="y"; goals: - - ==> \forall int x; x = x, y > 0 | y < 2 + - ==> \forall int x; x = x, y > 0 | y < 2 From ad9fe1a7e6cc5f65ebbb4fd0098f366fcbd8ae26 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sat, 11 Oct 2025 00:15:54 +0200 Subject: [PATCH 66/90] accomodating "focus inside find" which is algorithmically not trivial! --- .../key/scripts/TermComparisonWithHoles.java | 139 +++++++++++------- .../key/scripts/TestProofScriptCommand.java | 6 +- .../de/uka/ilkd/key/scripts/cases/find2.yml | 3 +- .../ilkd/key/scripts/cases/findOfFocus.yml | 7 + .../java/org/key_project/logic/PosInTerm.java | 29 +++- 5 files changed, 117 insertions(+), 67 deletions(-) create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/findOfFocus.yml diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java index a498b4cd685..6a7e9575f31 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java @@ -4,6 +4,7 @@ import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.equality.RenamingTermProperty; import de.uka.ilkd.key.logic.op.*; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.key_project.logic.PosInTerm; import org.key_project.logic.Term; @@ -31,6 +32,7 @@ * * @author Mattias Ulbrich */ +@NullMarked public class TermComparisonWithHoles { private final JTerm referenceTerm; @@ -46,28 +48,53 @@ public final boolean matches(PosInOccurrence pio) { } PosInTerm focus = findFocus(referenceTerm); - if(focus != null) { - for(int i = focus.depth() -1; i >= 0; i--) { - if(pio.isTopLevel()) { - // focus is deeper than the current term - return false; - } - int focusIdx = focus.getIndexAt(i); - int termIdx = pio.posInTerm().getIndexAt(pio.depth() - 1); - if(focusIdx != termIdx) { - // the focus is not at the same position as the current term - return false; - } - pio = pio.up(); + if(focus == null) { + focus = PosInTerm.getTopLevel(); + } + + List focusPaths = new ArrayList<>(); + expandFocusPaths(focus, pio.posInTerm(), PosInTerm.getTopLevel(), focusPaths); + + for(PosInTerm fpath : focusPaths) { + PosInTerm pit = pio.posInTerm().firstN(pio.depth() - fpath.depth()); + JTerm startTerm = (JTerm) pit.getSubTerm(pio.sequentFormula().formula()); + + boolean result = unifyHelp(referenceTerm, startTerm, + ImmutableSLList.nil(), + ImmutableSLList.nil(), + fpath); + if (result) { + return true; } - term = (JTerm) pio.subTerm(); } - return unifyHelp(referenceTerm, term, - ImmutableSLList.nil(), - ImmutableSLList.nil(), - null); + return false; + } + + private void expandFocusPaths(PosInTerm focus, PosInTerm input, PosInTerm base, List collected) { + if(focus.isTopLevel()) { + // fully matched: + collected.add(base); + return; + } + + if(input.isTopLevel()) { + // input is too shallow + return; + } + if(focus.getIndex() == (char)-1) { + // ellipsis found, we need to expand + expandFocusPaths(focus.up(), input, base, collected); + expandFocusPaths(focus, input.up(), base.prepend((char) input.getIndex()), collected); + } else { + if(focus.getIndex() == input.getIndex()) { + expandFocusPaths(focus.up(), input.up(), base.prepend((char) input.getIndex()), collected); + } else { + // mismatch + return; + } + } } public final boolean matchesToplevel(SequentFormula sf) { @@ -81,12 +108,20 @@ public final boolean matchesToplevel(SequentFormula sf) { if(op.name().equals(TermWithHoles.FOCUS_NAME)) { return PosInTerm.getTopLevel(); } + if(op.name().equals(TermWithHoles.ELLIPSIS_NAME)) { + PosInTerm subFocus = findFocus(pattern.sub(0)); + if(subFocus != null) { + return subFocus.prepend((char)-1); + } else { + return null; + } + } } for (int i = 0; i < pattern.arity(); i++) { Term sub = pattern.sub(i); PosInTerm subFocus = findFocus(sub); if(subFocus != null) { - return PosInTerm.of((char)i, subFocus); + return subFocus.prepend((char)i); } } return null; @@ -106,7 +141,7 @@ public final boolean matchesToplevel(SequentFormula sf) { private static boolean unifyHelp(JTerm t0, JTerm t1, ImmutableList ownBoundVars, ImmutableList cmpBoundVars, - NameAbstractionTable nat) { + @Nullable PosInTerm expectedFocus) { if (t0 == t1 && ownBoundVars.equals(cmpBoundVars)) { return true; @@ -120,15 +155,17 @@ private static boolean unifyHelp(JTerm t0, JTerm t1, } else if(op.name().equals(HOLE_PREDICATE_NAME) || op.name().equals(HOLE_NAME)) { return true; } else if(op.name().equals(FOCUS_NAME)) { - // ignore the "focus" annotation - return unifyHelp(t0.sub(0), t1, ownBoundVars, cmpBoundVars, nat); + if(expectedFocus == null || !expectedFocus.isTopLevel()) { + // focus annotation not at expected position + return false; + } + return unifyHelp(t0.sub(0), t1, ownBoundVars, cmpBoundVars, null); } else if(op.name().equals(ELLIPSIS_NAME)) { // return true if it hits one subterm ... - Set deepAllSubs = new HashSet<>(); - computeSubterms(t1, deepAllSubs); + Set> deepAllSubs = new HashSet<>(); + computeSubterms(t1, expectedFocus, deepAllSubs); var lookfor = t0.sub(0); - var finalNat = nat; - return deepAllSubs.stream().anyMatch(t -> unifyHelp(lookfor, t, ownBoundVars, cmpBoundVars, finalNat)); + return deepAllSubs.stream().anyMatch(t -> unifyHelp(lookfor, t.first, ownBoundVars, cmpBoundVars, t.second)); } @@ -154,12 +191,14 @@ private static boolean unifyHelp(JTerm t0, JTerm t1, // return false; // } - return descendRecursively(t0, t1, ownBoundVars, cmpBoundVars, nat); + return descendRecursively(t0, t1, ownBoundVars, cmpBoundVars, expectedFocus); } - private static void computeSubterms(JTerm t, Set deepAllSubs) { - deepAllSubs.add(t); - t.subs().stream().forEach(sub -> computeSubterms(sub, deepAllSubs)); + private static void computeSubterms(JTerm t, @Nullable PosInTerm expectedPos, Set> deepAllSubs) { + deepAllSubs.add(new Pair<>(t, expectedPos)); + for(int i = 0; i < t.arity(); i++) { + computeSubterms(t.sub(i), nextFocusPos(expectedPos, i), deepAllSubs); + } } private static boolean handleQuantifiableVariable(JTerm t0, JTerm t1, @@ -173,31 +212,6 @@ private static boolean handleQuantifiableVariable(JTerm t0, JTerm t1, return true; } -// private static NameAbstractionTable handleJava(JTerm t0, JTerm t1, -// NameAbstractionTable nat) { -// -// if (!t0.javaBlock().isEmpty() || !t1.javaBlock().isEmpty()) { -// nat = checkNat(nat); -// if (!t0.javaBlock().equalsModRenaming(t1.javaBlock(), nat)) { -// return FAILED; -// } -// } -// -// if (!(t0.op() instanceof SchemaVariable) -// && t0.op() instanceof ProgramVariable) { -// if (!(t1.op() instanceof ProgramVariable)) { -// return FAILED; -// } -// nat = checkNat(nat); -// if (!((ProgramVariable) t0.op()).equalsModRenaming( -// (ProgramVariable) t1.op(), nat)) { -// return FAILED; -// } -// } -// -// return nat; -// } - /** * compare two quantifiable variables if they are equal modulo renaming * @@ -249,7 +263,7 @@ private static NameAbstractionTable checkNat(NameAbstractionTable nat) { private static boolean descendRecursively(JTerm t0, JTerm t1, ImmutableList ownBoundVars, ImmutableList cmpBoundVars, - NameAbstractionTable nat) { + PosInTerm expectedFocus) { for (int i = 0; i < t0.arity(); i++) { ImmutableList subOwnBoundVars = ownBoundVars; @@ -269,8 +283,10 @@ private static boolean descendRecursively(JTerm t0, JTerm t1, subCmpBoundVars = subCmpBoundVars.prepend(cmpVar); } + PosInTerm nextFocus = nextFocusPos(expectedFocus, i); + boolean newConstraint = unifyHelp(t0.sub(i), t1.sub(i), - subOwnBoundVars, subCmpBoundVars, nat); + subOwnBoundVars, subCmpBoundVars, nextFocus); if (!newConstraint) { return false; @@ -280,6 +296,15 @@ private static boolean descendRecursively(JTerm t0, JTerm t1, return true; } + private static @Nullable PosInTerm nextFocusPos(PosInTerm expectedFocus, int i) { + if(expectedFocus != null && !expectedFocus.isTopLevel() && expectedFocus.getIndexAt(0) == i) { + // we are on the path to the focus + return expectedFocus.lastN(expectedFocus.depth() - 1); + } else { + return null; + } + } + public List> findTopLevelMatchesInSequent(Sequent sequent) { List> matches = new ArrayList<>(); for (SequentFormula sf : sequent.antecedent()) { diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java index 5413957143d..3ae06ad1e50 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java @@ -125,20 +125,20 @@ void testProofScript(TestInstance data, String name) throws Exception { Assertions.assertEquals(expected, goals.size()); for (String expectedGoal : data.goals()) { - assertThat(normaliseSpace(goals.head().toString().trim())).isEqualTo(expectedGoal); + assertThat(normaliseSpace(goals.head().toString())).isEqualTo(expectedGoal); goals = goals.tail(); } if (data.selectedGoal() != null) { Goal goal = pse.getStateMap().getFirstOpenAutomaticGoal(); - assertThat(goal.toString().trim()).isEqualTo(data.goals()[data.selectedGoal()]); + assertThat(normaliseSpace(goal.toString())).isEqualTo(data.goals()[data.selectedGoal()]); } } } // For some layout reasons the toString may add linebreaks and spaces private static String normaliseSpace(String str) { - return str.replaceAll("\\s+", " "); + return str.replaceAll("\\s+", " ").trim(); } } diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/find2.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/find2.yml index 3af65ac6ad8..498842ca2e8 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/find2.yml +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/find2.yml @@ -1,7 +1,6 @@ key: | - \programVariables { int a; } \problem { ==> 1 + 0 = 1 & 2 + 0 = 2 & 3 + 0 = 3 } script: | rule add_zero_right on:(?find(2)); goals: - - ==> 1 + 0 = 1 & 0 + 2 + 5 = 7 \ No newline at end of file + - ==> 1 + 0 = 1 & 2 = 2 & 3 + 0 = 3 \ No newline at end of file diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/findOfFocus.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/findOfFocus.yml new file mode 100644 index 00000000000..3c9e122033b --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/findOfFocus.yml @@ -0,0 +1,7 @@ +key: | + \programVariables { int a; } + \problem { ==> 3 = (3+0)+0 & 3+0 = (3+0)+0 } +script: | + rule add_zero_right on:(? & ?find(?focus(3+0)+0)); +goals: + - ==> 3 = 3 + 0 + 0 & 3 + 0 = 3 + 0 \ No newline at end of file diff --git a/key.ncore/src/main/java/org/key_project/logic/PosInTerm.java b/key.ncore/src/main/java/org/key_project/logic/PosInTerm.java index b9b3c59ba1a..326e1697c63 100644 --- a/key.ncore/src/main/java/org/key_project/logic/PosInTerm.java +++ b/key.ncore/src/main/java/org/key_project/logic/PosInTerm.java @@ -82,11 +82,11 @@ public static PosInTerm parseReverseString(String s) { : new PosInTerm(positions, (char) positions.length, true); } - public static PosInTerm of(char index, PosInTerm subPos) { - var newPositions = new char[subPos.size + 1]; - System.arraycopy(subPos.positions, 0, newPositions, 1, subPos.size); + public PosInTerm prepend(char index) { + var newPositions = new char[size + 1]; + System.arraycopy(positions, 0, newPositions, 1, size); newPositions[0] = index; - return new PosInTerm(newPositions, (char) (subPos.size + 1), true); + return new PosInTerm(newPositions, (char) (size + 1), false); } /// returns the instance representing the top level position @@ -137,6 +137,25 @@ public PosInTerm firstN(int n) { return new PosInTerm(positions, (char) n, true); } + + /// returns the position of the suffix of length n + /// @param n the length of the suffix + /// @return the suffix of this position of length n + /// @throws IndexOutOfBoundsException if n is greater than the depth of this + /// position + public PosInTerm lastN(int n) { + if (n > size) { + throw new IndexOutOfBoundsException("Position is shorter than " + n); + } else if (n == 0) { + return getTopLevel(); + } else if (n == size) { + return this; + } + final char[] newPositions = new char[n]; + System.arraycopy(positions, size - n, newPositions, 0, n); + return new PosInTerm(newPositions, (char) n, false); + } + /// returns the position for the i-th subterm of the subterm described by this /// position /// @@ -244,7 +263,7 @@ public boolean equals(@Nullable Object o) { /// /// @param it the iterator /// @return the String with the list of integers - public String integerList(IntIterator it) { + public static String integerList(IntIterator it) { final StringBuilder list = new StringBuilder("["); while (it.hasNext()) { list.append(it.next()); From 39ab2d27181433ed47cc1d46d93aa876cffeb1a2 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 19 Jul 2024 16:23:17 +0200 Subject: [PATCH 67/90] add "assumes" parameter to rule command not functional yet after cherry-picking --- .../de/uka/ilkd/key/scripts/EngineState.java | 5 + .../de/uka/ilkd/key/scripts/RuleCommand.java | 24 ++++- .../ilkd/key/scripts/SequentWithHoles.java | 94 +++++++++++++++++++ .../key/scripts/TermComparisonWithHoles.java | 1 + .../uka/ilkd/key/scripts/TermWithHoles.java | 23 ----- .../de/uka/ilkd/key/scripts/cases/assumes.yml | 7 ++ 6 files changed, 130 insertions(+), 24 deletions(-) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/scripts/SequentWithHoles.java create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/assumes.yml diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java index 60de949aea1..5f13102e63b 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java @@ -94,6 +94,11 @@ private ValueInjector createDefaultValueInjector() { v.addConverter(TermWithHoles.class, String.class, (str) -> TermWithHoles.fromString(this, str)); + v.addConverter(SequentWithHoles.class, ProofScriptExpressionContext.class, + (ctx) -> SequentWithHoles.fromParserContext(this, ctx)); + v.addConverter(SequentWithHoles.class, String.class, + (str) -> SequentWithHoles.fromString(this, str)); + addContextTranslator(v, String.class); addContextTranslator(v, JTerm.class); addContextTranslator(v, Integer.class); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java index 88b328815a0..ec7b247db41 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java @@ -25,6 +25,7 @@ import org.key_project.prover.proof.rulefilter.TacletFilter; import org.key_project.prover.rules.RuleApp; import org.key_project.prover.rules.Taclet; +import org.key_project.prover.rules.instantiation.AssumesFormulaInstantiation; import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.prover.sequent.SequentFormula; import org.key_project.util.collection.ImmutableList; @@ -370,8 +371,9 @@ private List filterList(Services services, Parameters p, List matchingApps = new ArrayList<>(); TermComparisonWithHoles matcher = p.on == null ? null : p.on.getMatcher(); for (TacletApp tacletApp : list) { + boolean add = true; if (tacletApp instanceof PosTacletApp pta) { - boolean add = matcher == null || matcher.matches(pta.posInOccurrence()); + add = matcher == null || matcher.matches(pta.posInOccurrence()); for (var entry : pta.instantiations().getInstantiationMap()) { final SchemaVariable sv = entry.key(); @@ -383,6 +385,10 @@ private List filterList(Services services, Parameters p, || userInst.equalsModProperty(ptaInst, IRRELEVANT_TERM_LABELS_PROPERTY); } + if(tacletApp.assumesFormulaInstantiations() != null) { + add &= checkAssumes(p, tacletApp.assumesFormulaInstantiations(), services); + } + if (add) { matchingApps.add(pta); } @@ -391,6 +397,16 @@ private List filterList(Services services, Parameters p, return matchingApps; } + private boolean checkAssumes(Parameters p, ImmutableList ifFormulaInstantiations, Services services) { + if(p.assumes == null) { + // no "assumes" restrictions specified. + return true; + } + + return p.assumes.matches(ifFormulaInstantiations); + } + + @Documentation(category = "Fundamental", value = """ This command can be used to apply a calculus rule to the currently active open goal. @@ -428,6 +444,12 @@ public static class Parameters { "specified to match the toplevel formula.") @Option(value = "matches") public @Nullable String matches = null; + + @Option(value = "assumes") + @Documentation(""" + If the rule has an `\\assumes` clause, this can be used to restrict the instantiations + """) + public @Nullable SequentWithHoles assumes; @OptionalVarargs(as = JTerm.class, prefix = "inst_") @Documentation("Instantiations for term schema variables used in the rule.") diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SequentWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SequentWithHoles.java new file mode 100644 index 00000000000..18df53e38df --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SequentWithHoles.java @@ -0,0 +1,94 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.scripts; + +import de.uka.ilkd.key.java.Services; +import de.uka.ilkd.key.ldt.JavaDLTheory; +import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.logic.NamespaceSet; +import de.uka.ilkd.key.logic.op.JFunction; +import de.uka.ilkd.key.logic.op.SortDependingFunction; +import de.uka.ilkd.key.logic.sort.GenericSort; +import de.uka.ilkd.key.nparser.KeYParser; +import de.uka.ilkd.key.nparser.KeyAst; +import de.uka.ilkd.key.nparser.ParsingFacade; +import de.uka.ilkd.key.nparser.builder.ExpressionBuilder; +import de.uka.ilkd.key.util.parsing.BuildingIssue; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.ParserRuleContext; +import org.jspecify.annotations.NullMarked; +import org.key_project.logic.Name; +import org.key_project.logic.PosInTerm; +import org.key_project.logic.sort.AbstractSort; +import org.key_project.logic.sort.Sort; +import org.key_project.prover.rules.instantiation.AssumesFormulaInstantiation; +import org.key_project.prover.sequent.PosInOccurrence; +import org.key_project.prover.sequent.Sequent; +import org.key_project.prover.sequent.SequentFormula; +import org.key_project.util.collection.ImmutableList; +import org.key_project.util.collection.ImmutableSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +@NullMarked +public class SequentWithHoles { + + + private final List antecedent; + private final List succedent; + + private SequentWithHoles(List antecedent, List succedent) { + this.antecedent = antecedent; + this.succedent = succedent; + } + + private static final Logger LOGGER = LoggerFactory.getLogger(SequentWithHoles.class); + + public static SequentWithHoles fromString(EngineState engineState, String str) { + KeyAst.Seq seq = ParsingFacade.parseSequent(CharStreams.fromString(str)); + return fromParserContext(engineState, seq.ctx); + } + + public static SequentWithHoles fromParserContext(EngineState state, ParserRuleContext ctx) { + + if(ctx instanceof KeYParser.ProofScriptExpressionContext psctx) { + ctx = psctx.seq(); + } + + if(ctx instanceof KeYParser.SeqContext seqCtx) { + List antecedent = new java.util.ArrayList<>(); + KeYParser.SemisequentContext semseq = seqCtx.ant; + while(semseq != null && semseq.term() != null) { + antecedent.add(TermWithHoles.fromParserContext(state, semseq.term())); + semseq = semseq.semisequent(); + } + + List succedent = new java.util.ArrayList<>(); + semseq = seqCtx.suc; + while(semseq != null && semseq.term() != null) { + succedent.add(TermWithHoles.fromParserContext(state, semseq.term())); + semseq = semseq.semisequent(); + } + return new SequentWithHoles(antecedent, succedent); + } + + throw new IllegalArgumentException("Not a sequent: " + ctx.getText()); + } + + // TODO currently this does not check if the instantiation is on the correct side ... + public boolean matches(ImmutableList ifFormulaInstantiations) { + + for (AssumesFormulaInstantiation assF : ifFormulaInstantiations) { + var form = assF.getSequentFormula(); + if (antecedent.stream().noneMatch(f -> f.matchesToplevel(form)) && + succedent.stream().noneMatch(f -> f.matchesToplevel(form))) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java index 6a7e9575f31..1b54e528ea0 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java @@ -10,6 +10,7 @@ import org.key_project.logic.Term; import org.key_project.logic.op.Operator; import org.key_project.logic.op.QuantifiableVariable; +import org.key_project.prover.rules.instantiation.AssumesFormulaInstantiation; import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.prover.sequent.Sequent; import org.key_project.prover.sequent.SequentFormula; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java index 70bb08d5e47..d63358d6fb5 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java @@ -43,30 +43,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.HashMap; import java.util.List; -import java.util.Map; - -import static de.uka.ilkd.key.strategy.StrategyProperties.*; - -/* - -y < (x+0) + y + (x+0), -a -> y < (x+0) + y + (x+0) - - -rule plus_zero on: ????? - ----> a -> y < (x+0) + y + FOCUS(x+0) - ----> _ -> (... _ + (_+0) ...) - ----> _ -> _find(_ + _focus(_ + 0)) - ----> ? -> ?find(? + ?focus(? + 0)) - ----> ?_ -> ?find(?_ + ?focus(_ + 0)) - */ @NullMarked public class TermWithHoles { diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/assumes.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/assumes.yml new file mode 100644 index 00000000000..6374ed61d06 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/assumes.yml @@ -0,0 +1,7 @@ +key: | + \programVariables { int a,b,c,d,e,f; } + \problem { a=b, a=d ==> a=c } +script: | + rule "applyEq" on:(?focus(a) = c) assumes:( ? = b ==> ) ; +goals: + - a = b, a = d ==> b = c From f2a76d7a7db71989be7edcf2384ce2bb8884f0e2 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sat, 11 Oct 2025 01:26:12 +0200 Subject: [PATCH 68/90] bugfixing matching with program variables --- .../ilkd/key/scripts/TermComparisonWithHoles.java | 6 +----- .../uka/ilkd/key/scripts/cases/bugWithFunctions.yml | 12 ++++++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/bugWithFunctions.yml diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java index 1b54e528ea0..02b47be0e5c 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java @@ -179,11 +179,7 @@ private static boolean unifyHelp(JTerm t0, JTerm t1, final Operator op1 = t1.op(); - if (!(op0 instanceof ProgramVariable) && op0 != op1) { - return false; - } - - if (t0.sort() != t1.sort() || t0.arity() != t1.arity()) { + if (op0 != op1) { return false; } diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/bugWithFunctions.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/bugWithFunctions.yml new file mode 100644 index 00000000000..5f4b6929552 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/bugWithFunctions.yml @@ -0,0 +1,12 @@ +# Was a bug: b+0 would also match here ... +key: | + \programVariables { int a; int b; } + + \problem { a + 0 = a & b + 0 = b } + +script: | + rule add_zero_right on:(a+0); + +goals: + - "==> a = a & b + 0 = b" + From d7d8268076fc5ccff2e4cf89b3c363c8bdca8113 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sat, 11 Oct 2025 01:42:13 +0200 Subject: [PATCH 69/90] updating the focus rule --- .../de/uka/ilkd/key/scripts/FocusCommand.java | 37 ++++--------------- .../ilkd/key/scripts/SequentWithHoles.java | 8 ++++ .../uka/ilkd/key/scripts/cases/focus_rule.yml | 6 +++ 3 files changed, 22 insertions(+), 29 deletions(-) create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focus_rule.yml diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java index df279f4afea..0b12d0eaeda 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java @@ -67,15 +67,13 @@ public FocusCommand() { static class Parameters { @Argument @Documentation("The sequent containing the formulas to keep. It may contain placeholder symbols.") - public @MonotonicNonNull Sequent toKeep; + public @MonotonicNonNull SequentWithHoles toKeep; } @Override public void execute(ScriptCommandAst args) throws ScriptException, InterruptedException { - var s = state().getValueInjector().inject(new Parameters(), args); - - Sequent toKeep = s.toKeep; - hideAll(toKeep); + Parameters s = state().getValueInjector().inject(new Parameters(), args); + hideAll(s.toKeep); } @Override @@ -89,38 +87,19 @@ public String getName() { * @param toKeep sequent containing formulas to keep * @throws ScriptException if no goal is currently open */ - private void hideAll(Sequent toKeep) throws ScriptException { + private void hideAll(SequentWithHoles toKeep) throws ScriptException { Goal goal = state.getFirstOpenAutomaticGoal(); assert goal != null : "not null by contract of the method"; - // The formulas to keep in the antecedent - ImmutableList keepAnte = toKeep.antecedent().asList() - .map(SequentFormula::formula); - ImmutableList ante = - goal.sequent().antecedent().asList(); - - for (SequentFormula seqFormula : ante) { - // This means "!keepAnte.contains(seqFormula.formula)" but with equality mod renaming! - if (!keepAnte.exists( - it -> { - Term formula = seqFormula.formula(); - return RENAMING_TERM_PROPERTY.equalsModThisProperty(it, formula); - })) { + for (SequentFormula seqFormula : goal.sequent().antecedent().asList()) { + if(!toKeep.containsAntecendent(seqFormula)) { Taclet tac = getHideTaclet("left"); makeTacletApp(goal, seqFormula, tac, true); } } - ImmutableList keepSucc = - toKeep.succedent().asList().map(SequentFormula::formula); - ImmutableList succ = - goal.sequent().succedent().asList(); - for (SequentFormula seqFormula : succ) { - if (!keepSucc.exists( - it -> { - Term formula = seqFormula.formula(); - return RENAMING_TERM_PROPERTY.equalsModThisProperty(it, formula); - })) { + for (SequentFormula seqFormula : goal.sequent().succedent().asList()) { + if(!toKeep.containsSuccedent(seqFormula)) { Taclet tac = getHideTaclet("right"); makeTacletApp(goal, seqFormula, tac, false); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SequentWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SequentWithHoles.java index 18df53e38df..6b67ab25ab2 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SequentWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SequentWithHoles.java @@ -91,4 +91,12 @@ public boolean matches(ImmutableList ifFormulaInsta return true; } + + public boolean containsAntecendent(SequentFormula seqFormula) { + return antecedent.stream().anyMatch(f -> f.matchesToplevel(seqFormula)); + } + + public boolean containsSuccedent(SequentFormula seqFormula) { + return succedent.stream().anyMatch(f -> f.matchesToplevel(seqFormula)); + } } \ No newline at end of file diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focus_rule.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focus_rule.yml new file mode 100644 index 00000000000..f7ad12aab7d --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/focus_rule.yml @@ -0,0 +1,6 @@ +key: | + \problem { 1 > 0, 2 > 1 ==> 3 > 2, 4 > 2, 5 > 1 } +script: | + focus (? > 1 ==> ? > 2); +goals: + - 2 > 1 ==> 3 > 2, 4 > 2 From e1eb8c17193da2815f6436666c35c87c1dc05f4a Mon Sep 17 00:00:00 2001 From: Wolfram Pfeifer Date: Tue, 13 Aug 2024 19:18:19 +0200 Subject: [PATCH 70/90] allow terms with holes in expand command adapted to new branch by MU --- .../de/uka/ilkd/key/scripts/ExpandDefCommand.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java index cebb06bb82f..8eca2a44b61 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java @@ -76,18 +76,15 @@ private TacletApp makeRuleApp(Parameters p, EngineState state) throws ScriptExce if (p.on != null) { apps = apps.filter( it -> it instanceof PosTacletApp && - ((JTerm) it.posInOccurrence().subTerm()).equalsModProperty(p.on, - TermLabelsProperty.TERM_LABELS_PROPERTY)); + p.on.matches(it.posInOccurrence())); } else if (p.formula != null) { apps = apps.filter( it -> it instanceof PosTacletApp && - ((JTerm) it.posInOccurrence().sequentFormula().formula()).equalsModProperty( - p.formula, TermLabelsProperty.TERM_LABELS_PROPERTY)); + p.formula.matchesToplevel(it.posInOccurrence().sequentFormula())); } else { throw new ScriptException("Either 'formula' or 'on' must be specified"); } - if (apps.isEmpty()) { throw new ScriptException("There is no expansion rule app that matches 'on'"); } else if (p.occ != null && p.occ >= 0) { @@ -107,11 +104,11 @@ private TacletApp makeRuleApp(Parameters p, EngineState state) throws ScriptExce public static class Parameters { @Option(value = "on") - public @Nullable JTerm on; + public @Nullable TermWithHoles on; @Option(value = "occ") public @Nullable Integer occ; @Option(value = "formula") - public @Nullable JTerm formula; + public @Nullable TermWithHoles formula; } From 25b1ca5b7d86fa7c652e5033e1883a702cb891b7 Mon Sep 17 00:00:00 2001 From: Wolfram Pfeifer Date: Wed, 28 Aug 2024 17:05:28 +0200 Subject: [PATCH 71/90] added convenience macro to close all NPE and Out-of-bounds branches --- .../key/macros/TryCloseSideBranchesMacro.java | 77 +++++++++++++++++++ .../de.uka.ilkd.key.macros.ProofMacro | 1 + 2 files changed, 78 insertions(+) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/macros/TryCloseSideBranchesMacro.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/TryCloseSideBranchesMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/TryCloseSideBranchesMacro.java new file mode 100644 index 00000000000..1b0dcd06674 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/TryCloseSideBranchesMacro.java @@ -0,0 +1,77 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ + +package de.uka.ilkd.key.macros; + + +import de.uka.ilkd.key.control.UserInterfaceControl; +import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.proof.Node; +import de.uka.ilkd.key.proof.Proof; +import org.key_project.prover.engine.ProverTaskListener; +import org.key_project.prover.sequent.PosInOccurrence; +import org.key_project.util.collection.ImmutableList; + +public class TryCloseSideBranchesMacro extends TryCloseMacro { + + /** + * Instantiates a new try close macro. + * No changes to the max number of steps. + */ + public TryCloseSideBranchesMacro() { + super(-1); + } + + /** + * Instantiates a new try close macro. + * + * @param numberSteps + * the max number of steps. -1 means no change. + */ + public TryCloseSideBranchesMacro(int numberSteps) { + super(numberSteps); + } + + @Override + public String getName() { + return "Close Provable Goals Below (Only side branches)"; + } + + @Override + public String getScriptCommandName() { + return "tryclose-sidebranches"; + } + + @Override + public String getDescription() { + return "Closes closable goals, leave rest untouched (see settings AutoPrune). " + + "Applies only to supposedly easy side goals (null reference, index out of bounds) " + + "beneath the selected node."; + } + + @Override + public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, + Proof proof, + ImmutableList goals, + PosInOccurrence posInOcc, + ProverTaskListener listener) throws InterruptedException { + ImmutableList sideGoals = goals.filter(TryCloseSideBranchesMacro::isSideGoal); + + return super.applyTo(uic, proof, sideGoals, posInOcc, listener); + } + + private static boolean isSideGoal(Goal g) { + Node node = g.node(); + while (node != null && node.getNodeInfo() != null + && node.getNodeInfo().getBranchLabel() != null) { + String label = node.getNodeInfo().getBranchLabel(); + if (label.contains("Null Reference") + || label.contains("Index Out of Bounds")) { + return true; + } + node = node.parent(); + } + return false; + } +} diff --git a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.macros.ProofMacro b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.macros.ProofMacro index 6ec02ca7559..cfb5e6aaee3 100644 --- a/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.macros.ProofMacro +++ b/key.core/src/main/resources/META-INF/services/de.uka.ilkd.key.macros.ProofMacro @@ -12,6 +12,7 @@ de.uka.ilkd.key.macros.PropositionalExpansionMacro # de.uka.ilkd.key.macros.PropositionalExpansionWithSimplificationMacro de.uka.ilkd.key.macros.FullPropositionalExpansionMacro de.uka.ilkd.key.macros.TryCloseMacro +de.uka.ilkd.key.macros.TryCloseSideBranchesMacro de.uka.ilkd.key.macros.FinishSymbolicExecutionMacro de.uka.ilkd.key.macros.AutoMacro #de.uka.ilkd.key.macros.FinishSymbolicExecutionUntilJoinPointMacro From 111533121c839e0ef233d427fea04c8ddb0734d3 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sat, 11 Oct 2025 12:26:04 +0200 Subject: [PATCH 72/90] bug fixes in script engine --- key.core/build.gradle | 1 + .../uka/ilkd/key/nparser/ParsingFacade.java | 2 +- .../ilkd/key/proof/init/AbstractProfile.java | 2 + .../de/uka/ilkd/key/scripts/EngineState.java | 75 ++------- .../uka/ilkd/key/scripts/ExprEvaluator.java | 156 +++++++++++------- .../de/uka/ilkd/key/scripts/LetCommand.java | 51 +++--- .../key/scripts/TermComparisonWithHoles.java | 7 +- .../uka/ilkd/key/scripts/TermWithHoles.java | 28 +++- .../ilkd/key/scripts/meta/ValueInjector.java | 26 +-- .../key/scripts/TestProofScriptCommand.java | 18 +- .../key/scripts/meta/ValueInjectorTest.java | 37 ++++- .../de/uka/ilkd/key/scripts/cases/holes2.yml | 7 + .../de/uka/ilkd/key/scripts/cases/let.yml | 7 + .../ilkd/key/scripts/cases/termsAsStrings.yml | 8 + 14 files changed, 247 insertions(+), 178 deletions(-) create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes2.yml create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/let.yml create mode 100644 key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/termsAsStrings.yml diff --git a/key.core/build.gradle b/key.core/build.gradle index eb7714f746a..323d8417b65 100644 --- a/key.core/build.gradle +++ b/key.core/build.gradle @@ -97,6 +97,7 @@ classes.dependsOn << generateSolverPropsList tasks.withType(Test) { enableAssertions = true + systemProperties(System.properties.findAll { key, value -> key.startsWith("key.") }) } diff --git a/key.core/src/main/java/de/uka/ilkd/key/nparser/ParsingFacade.java b/key.core/src/main/java/de/uka/ilkd/key/nparser/ParsingFacade.java index b758e24d92e..d0e7cbc9c8f 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/nparser/ParsingFacade.java +++ b/key.core/src/main/java/de/uka/ilkd/key/nparser/ParsingFacade.java @@ -108,7 +108,7 @@ private static KeYParser createParser(TokenSource lexer) { } - private static KeYParser createParser(CharStream stream) { + public static KeYParser createParser(CharStream stream) { return createParser(createLexer(stream)); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/init/AbstractProfile.java b/key.core/src/main/java/de/uka/ilkd/key/proof/init/AbstractProfile.java index 98eef285de0..5b643384ca5 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/init/AbstractProfile.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/init/AbstractProfile.java @@ -61,6 +61,8 @@ protected AbstractProfile(String standardRuleFilename) { final var ruleSource = RuleSourceFactory.fromDefaultLocation(standardRuleFilename); standardRules = new RuleCollection(ImmutableList.of(ruleSource), initBuiltInRules()); strategies = getStrategyFactories(); + // NPEs in tests revealed that strategies could contain null elements + assert !strategies.contains(null); this.supportedGCB = computeSupportedGoalChooserBuilder(); this.supportedGC = extractNames(supportedGCB); this.prototype = getDefaultGoalChooserBuilder(); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java index 5f13102e63b..0581050043a 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java @@ -54,7 +54,6 @@ public class EngineState { private final AbbrevMap abbrevMap = new AbbrevMap(); private final ValueInjector valueInjector = createDefaultValueInjector(); - private final ExprEvaluator exprEvaluator = new ExprEvaluator(this); private @Nullable Consumer observer; private Path baseFileName = Paths.get("."); @@ -81,73 +80,31 @@ public EngineState(Proof proof, ProofScriptEngine engine) { this.engine = engine; } - // Problem is: This is all KeY specific and there should be different converters - // for JML. But how to separate this? Probably do this externally after PSE generation. - // via some "enrichValueInjector" method in a different class ... + /// add converters for types used in proof scripts, + /// add support for parse trees private ValueInjector createDefaultValueInjector() { - var v = ValueInjector.createDefault(); + ValueInjector v = ValueInjector.createDefault(); + + // from string to ... v.addConverter(JTerm.class, String.class, (str) -> this.toTerm(str, null)); v.addConverter(Sequent.class, String.class, this::toSequent); v.addConverter(Sort.class, String.class, this::toSort); - v.addConverter(TermWithHoles.class, ProofScriptExpressionContext.class, - (ctx) -> TermWithHoles.fromParserContext(this, ctx)); + + // to terms with holes v.addConverter(TermWithHoles.class, String.class, - (str) -> TermWithHoles.fromString(this, str)); + str -> TermWithHoles.fromString(this, str)); - v.addConverter(SequentWithHoles.class, ProofScriptExpressionContext.class, - (ctx) -> SequentWithHoles.fromParserContext(this, ctx)); + // to sequents with holes v.addConverter(SequentWithHoles.class, String.class, - (str) -> SequentWithHoles.fromString(this, str)); - - addContextTranslator(v, String.class); - addContextTranslator(v, JTerm.class); - addContextTranslator(v, Integer.class); - addContextTranslator(v, Byte.class); - addContextTranslator(v, Long.class); - addContextTranslator(v, Boolean.class); - addContextTranslator(v, Character.class); - addContextTranslator(v, Sequent.class); - addContextTranslator(v, Integer.TYPE); - addContextTranslator(v, Byte.TYPE); - addContextTranslator(v, Long.TYPE); - addContextTranslator(v, Boolean.TYPE); - addContextTranslator(v, Character.TYPE); - addContextTranslator(v, JTerm.class); - addContextTranslator(v, Sequent.class); - addContextTranslator(v, Semisequent.class); - addContextTranslator(v, ScriptBlock.class); - return v; - } + str -> SequentWithHoles.fromString(this, str)); - private void addContextTranslator(ValueInjector v, Class aClass) { - Converter converter = - (ProofScriptExpressionContext a) -> convertToString(v, aClass, a); - v.addConverter(aClass, ProofScriptExpressionContext.class, converter); + // from KeY parse tree to everything + ExprEvaluator exprEvaluator = new ExprEvaluator(this); + exprEvaluator.addConvertersToValueInjector(v); + return v; } @SuppressWarnings("unchecked") - private R convertToString(ValueInjector inj, Class aClass, - ProofScriptExpressionContext ctx) - throws Exception { - try { - if (aClass == String.class && ctx.string_literal() != null) { - return inj.getConverter(aClass, String.class) - .convert(StringUtil.trim(ctx.string_literal().getText(), '"')); - } - if (aClass == String.class) { - return inj.getConverter(aClass, String.class).convert(ctx.getText()); - } - - T value = (T) ctx.accept(exprEvaluator); - Class tClass = (Class) value.getClass(); - if (aClass.isAssignableFrom(value.getClass())) { - return aClass.cast(value); - } - return inj.getConverter(aClass, tClass).convert(value); - } catch (ConversionException | NoSpecifiedConverterException e) { - return inj.getConverter(aClass, String.class).convert(ctx.getText()); - } - } protected static Goal getGoal(ImmutableList openGoals, Node node) { for (Goal goal : openGoals) { @@ -374,10 +331,6 @@ public NamespaceSet getCurrentNamespaces() { } } - public ExprEvaluator getEvaluator() { - return exprEvaluator; - } - public void putUserData(String key, @Nullable Object val) { userData.put(key, val); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java index 4862f59dcad..9466c3edabb 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java @@ -5,15 +5,28 @@ import java.net.URI; +import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.nparser.KeYParser; import de.uka.ilkd.key.nparser.KeYParser.*; import de.uka.ilkd.key.nparser.KeYParserBaseVisitor; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.nparser.builder.ExpressionBuilder; +import de.uka.ilkd.key.proof.calculus.JavaDLSequentKit; +import de.uka.ilkd.key.scripts.meta.ConversionException; +import de.uka.ilkd.key.scripts.meta.Converter; +import de.uka.ilkd.key.scripts.meta.NoSpecifiedConverterException; +import de.uka.ilkd.key.scripts.meta.ValueInjector; +import de.uka.ilkd.key.util.ANTLRUtil; +import org.key_project.logic.sort.Sort; +import org.key_project.prover.sequent.Semisequent; import org.key_project.prover.sequent.Sequent; import org.antlr.v4.runtime.ParserRuleContext; +import org.key_project.prover.sequent.SequentFormula; +import org.key_project.prover.sequent.SequentKit; +import org.key_project.util.collection.ImmutableList; +import org.key_project.util.java.StringUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,7 +34,6 @@ /// Evaluates expression inside of proof script to their appropriate type. /// -/// - [JmlParser.ExpressionContext]: [Term] /// - [SeqContext]: [Sequent] /// - [Boolean_literalContext]: [Boolean] /// - [IntegerContext]: [Integer] @@ -31,7 +43,7 @@ /// @author Alexander Weigl /// @version 1 (18.01.25) /// @see de.uka.ilkd.key.nparser.KeYParser.ProofScriptExpressionContext -class ExprEvaluator extends KeYParserBaseVisitor { +class ExprEvaluator { private static final Logger LOGGER = LoggerFactory.getLogger(ExprEvaluator.class); private final EngineState state; @@ -39,78 +51,102 @@ class ExprEvaluator extends KeYParserBaseVisitor { this.state = engineState; } - @Override - public Object visitProofScriptCodeBlock(ProofScriptCodeBlockContext ctx) { - URI uri = KeyAst.ProofScript.getUri(ctx.start); - return KeyAst.ProofScript.asAst(uri, ctx); + private Object evaluateExpression(ParserRuleContext ctx) { + var expressionBuilder = + new ExpressionBuilder(state.getProof().getServices(), state.getCurrentNamespaces()); + expressionBuilder.setAbbrevMap(state.getAbbreviations()); + var t = ctx.accept(expressionBuilder); + var warnings = expressionBuilder.getBuildingIssues(); + warnings.forEach(it -> LOGGER.warn("{}", it)); + warnings.clear(); + return t; } - @Override - public Object visitBoolean_literal(Boolean_literalContext ctx) { - return Boolean.parseBoolean(ctx.getText()); - } + public void addConvertersToValueInjector(ValueInjector v) { + v.addConverter(String.class, ProofScriptExpressionContext.class, this::convertToString); + v.addConverter(Boolean.class, ProofScriptExpressionContext.class, this::convertToBoolean); + v.addConverter(boolean.class, ProofScriptExpressionContext.class, this::convertToBoolean); + v.addConverter(Integer.class, ProofScriptExpressionContext.class, this::convertToInteger); + v.addConverter(int.class, ProofScriptExpressionContext.class, this::convertToInteger); + v.addConverter(JTerm.class, ProofScriptExpressionContext.class, this::convertToTerm); + v.addConverter(Sequent.class, ProofScriptExpressionContext.class, this::convertToSequent); + v.addConverter(TermWithHoles.class, ProofScriptExpressionContext.class, + ctx -> TermWithHoles.fromProofScriptExpression(state, ctx)); + v.addConverter(SequentWithHoles.class, ProofScriptExpressionContext.class, + ctx -> SequentWithHoles.fromParserContext(state, ctx)); + v.addConverter(ScriptBlock.class, ProofScriptExpressionContext.class, this::convertToScriptBlock); - @Override - public Object visitChar_literal(KeYParser.Char_literalContext ctx) { - return ctx.getText().charAt(1); // skip "'" } - @Override - public Object visitInteger(IntegerContext ctx) { - return Integer.parseInt(ctx.getText()); + private ScriptBlock convertToScriptBlock(ProofScriptExpressionContext ctx) throws ConversionException { + if(ctx.proofScriptCodeBlock() == null) { + throw new ConversionException("Need a script block here, not: " + ANTLRUtil.reconstructOriginal(ctx)); + } + URI uri = KeyAst.ProofScript.getUri(ctx.start); + return KeyAst.ProofScript.asAst(uri, ctx.proofScriptCodeBlock()); } - @Override - public Object visitFloatLiteral(KeYParser.FloatLiteralContext ctx) { - return Float.parseFloat(ctx.getText()); + private JTerm convertToTerm(ProofScriptExpressionContext ctx) throws ConversionException { + try { + if (ctx.string_literal() != null) { + String text = StringUtil.trim(ctx.string_literal().getText(), '"'); + return state.toTerm(text, null); + } else if (ctx.proofScriptCodeBlock() != null) { + throw new ConversionException("A block cannot be used as a term"); + } else { + return (JTerm) evaluateExpression((ParserRuleContext) ctx.getChild(0)); + } + } catch (Exception e) { + throw new ConversionException("Cannot convert expression to term: " + ANTLRUtil.reconstructOriginal(ctx)); + } } - @Override - public Object visitDoubleLiteral(DoubleLiteralContext ctx) { - return Double.parseDouble(ctx.getText()); + private Sequent convertToSequent(ProofScriptExpressionContext ctx) throws ConversionException { + try { + if (ctx.string_literal() != null) { + String text = StringUtil.trim(ctx.string_literal().getText(), '"'); + return state.toSequent(text); + } else if (ctx.proofScriptCodeBlock() != null) { + throw new ConversionException("A block cannot be used as a sequent"); + } else if (ctx.seq() != null) { + return (Sequent) evaluateExpression(ctx.seq()); + } else { + JTerm term = (JTerm) evaluateExpression((ParserRuleContext) ctx.getChild(0)); + return JavaDLSequentKit.createSuccSequent(ImmutableList.of(new SequentFormula(term))); + } + } catch (Exception e) { + throw new ConversionException("Cannot convert expression to sequent: " + ANTLRUtil.reconstructOriginal(ctx)); + } } - @Override - public String visitString_literal(String_literalContext ctx) { - return trim(ctx.getText(), '"'); + private Boolean convertToBoolean(ProofScriptExpressionContext ctx) throws ConversionException { + if (ctx.boolean_literal() != null) { + return Boolean.parseBoolean(ctx.boolean_literal().getText()); + } else if (ctx.string_literal() != null) { + String text = StringUtil.trim(ctx.string_literal().getText(), '"'); + return Boolean.parseBoolean(text); + } else { + throw new ConversionException("Cannot convert expression to boolean: " + ANTLRUtil.reconstructOriginal(ctx)); + } } - @Override - public Sequent visitSeq(SeqContext ctx) { - var expressionBuilder = - new ExpressionBuilder(state.getProof().getServices(), state.getCurrentNamespaces()); - expressionBuilder.setAbbrevMap(state.getAbbreviations()); - var t = (Sequent) ctx.accept(expressionBuilder); - var warnings = expressionBuilder.getBuildingIssues(); - warnings.forEach(it -> LOGGER.warn("{}", it)); - warnings.clear(); - return t; - - } - - @Override - public Object visitSimple_ident(Simple_identContext ctx) { - return evaluateExpression(ctx); + private Integer convertToInteger(ProofScriptExpressionContext proofScriptExpressionContext) throws ConversionException { + if (proofScriptExpressionContext.integer() != null) { + return Integer.parseInt(proofScriptExpressionContext.integer().getText()); + } else if (proofScriptExpressionContext.string_literal() != null) { + String text = StringUtil.trim(proofScriptExpressionContext.string_literal().getText(), '"'); + return Integer.parseInt(text); + } else { + throw new ConversionException("Cannot convert expression to integer: " + ANTLRUtil.reconstructOriginal(proofScriptExpressionContext)); + } } - @Override - public Object visitTerm(KeYParser.TermContext ctx) { - return evaluateExpression(ctx); + private String convertToString(ProofScriptExpressionContext ctx) { + if (ctx.string_literal() != null) { + return StringUtil.trim(ctx.string_literal().getText(), '"'); + } else { + return ANTLRUtil.reconstructOriginal(ctx).trim(); + } } - private Object evaluateExpression(ParserRuleContext ctx) { - var expressionBuilder = - new ExpressionBuilder(state.getProof().getServices(), state.getCurrentNamespaces()); - expressionBuilder.setAbbrevMap(state.getAbbreviations()); - var t = ctx.accept(expressionBuilder); - var warnings = expressionBuilder.getBuildingIssues(); - warnings.forEach(it -> LOGGER.warn("{}", it)); - warnings.clear(); - return t; - } - - @Override - protected Object aggregateResult(Object aggregate, Object nextResult) { - return nextResult == null ? aggregate : nextResult; - } -} +} \ No newline at end of file diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java index b061d0aba42..0faa7026522 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java @@ -11,9 +11,11 @@ import de.uka.ilkd.key.nparser.KeYParser; import de.uka.ilkd.key.pp.AbbrevMap; import de.uka.ilkd.key.scripts.meta.Documentation; +import de.uka.ilkd.key.scripts.meta.OptionalVarargs; import de.uka.ilkd.key.scripts.meta.ProofScriptArgument; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /// The *let* command lets you introduce entries to the abbreviation table. /// ``` @@ -33,34 +35,46 @@ /// * Apr,2025 (weigl): remove {@code force} in favor of {@code letf}. /// * Jan,2025 (weigl): add new parameter {@code force} to override bindings. @NullMarked -@Documentation(""" +public class LetCommand extends AbstractCommand { + + @Documentation(category = "Fundamental", value = """ The let command lets you introduce entries to the abbreviation table. - let @abbrev1=term1 ... @abbrev2=term2; + + let @abbrev1=term1 ... @abbrev2=term2; + or - letf @abbrev1=term1 ... @abbrev2=term2; + + letf @abbrev1=term1 ... @abbrev2=term2; + One or more key-value pairs are supported where key starts is @ followed by an identifier and value is a term. If letf if used instead of let, the let bindings are overridden otherwise conflicts results into an exception.""") -public class LetCommand implements ProofScriptCommand { + + + public static class Parameters { + @Documentation("Key-value pairs where key is the name of the abbreviation (starting with @) and value is a term.") + @OptionalVarargs(as = JTerm.class) + public Map namedArgs = Map.of(); + } + + public LetCommand() { + super(Parameters.class); + } @Override public List getArguments() { return List.of(); } - @Override - public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst args, - EngineState stateMap) throws ScriptException, InterruptedException { + public void execute(ScriptCommandAst ast) throws ScriptException, InterruptedException { + var args = state().getValueInjector().inject(new Parameters(), ast); - AbbrevMap abbrMap = stateMap.getAbbreviations(); + AbbrevMap abbrMap = state().getAbbreviations(); - boolean force = "letf".equals(args.commandName()); + boolean force = "letf".equals(ast.commandName()); - for (Map.Entry entry : args.namedArgs().entrySet()) { + for (Map.Entry entry : args.namedArgs.entrySet()) { String key = entry.getKey(); - if (key.startsWith("#") || key.equals("force")) { - continue; - } if (key.startsWith("@")) { // get rid of @ @@ -70,11 +84,9 @@ public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst arg if (abbrMap.containsAbbreviation(key) && !force) { throw new ScriptException(key + " is already fixed in this script"); } + try { - final var termCtx = (KeYParser.ProofScriptExpressionContext) entry.getValue(); - final var value = termCtx.accept(stateMap.getEvaluator()); - final var term = stateMap.getValueInjector().convert(value, JTerm.class); - abbrMap.put(term, key, true); + abbrMap.put(entry.getValue(), key, true); } catch (Exception e) { throw new ScriptException(e); } @@ -87,11 +99,6 @@ public String getName() { return "let"; } - @Override - public String getDocumentation() { - return ""; - } - @Override public List getAliases() { return List.of(getName(), "letf"); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java index 02b47be0e5c..984c44bf9f1 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java @@ -18,10 +18,7 @@ import org.key_project.util.collection.ImmutableSLList; import org.key_project.util.collection.Pair; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import static de.uka.ilkd.key.scripts.TermWithHoles.*; @@ -39,7 +36,7 @@ public class TermComparisonWithHoles { private final JTerm referenceTerm; TermComparisonWithHoles(JTerm referenceTerm) { - this.referenceTerm = referenceTerm; + this.referenceTerm = Objects.requireNonNull(referenceTerm); } public final boolean matches(PosInOccurrence pio) { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java index d63358d6fb5..c52e1115c6d 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java @@ -26,6 +26,7 @@ import de.uka.ilkd.key.util.parsing.BuildingIssue; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ParseTree; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -40,20 +41,21 @@ import org.key_project.util.collection.ImmutableList; import org.key_project.util.collection.ImmutableSLList; import org.key_project.util.collection.ImmutableSet; +import org.key_project.util.java.StringUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; +import java.util.Objects; @NullMarked public class TermWithHoles { private final JTerm term; public TermWithHoles(JTerm term) { - this.term = term; + this.term = Objects.requireNonNull(term); } - public static final Name HOLE_NAME = new Name("?"); public static final Name HOLE_PREDICATE_NAME = new Name("?fml"); public static final Name HOLE_SORT_DEP_NAME = new Name("?"); @@ -94,11 +96,27 @@ public boolean extendsTrans(Sort s) { } public static TermWithHoles fromString(EngineState engineState, String str) { - KeyAst.Term term = ParsingFacade.parseExpression(CharStreams.fromString(str)); - return fromParserContext(engineState, term.ctx); + KeYParser p = ParsingFacade.createParser(CharStreams.fromString(str)); + p.allowMatchId = true; + KeYParser.TermContext term = p.termEOF().term(); + p.getErrorReporter().throwException(); + return fromParserContext(engineState, term); + } + + public static TermWithHoles fromProofScriptExpression(EngineState engineState, KeYParser.ProofScriptExpressionContext ctx) throws ConversionException { + if(ctx.string_literal() != null) { + String text = StringUtil.stripQuotes(ctx.string_literal().getText()); + return fromString(engineState, text); + } else if(ctx.proofScriptCodeBlock() != null) { + throw new ConversionException("A block cannot be used as a term"); + } else if(ctx.seq() != null) { + throw new ConversionException("A sequent cannot be used as a term"); + } else { + return fromParserContext(engineState, ctx.getRuleContext(ParserRuleContext.class, 0)); + } } - public static TermWithHoles fromParserContext(EngineState state, ParserRuleContext ctx) { + public static TermWithHoles fromParserContext(EngineState state, ParseTree ctx) { var expressionBuilder = new ExpressionBuilder(state.getProof().getServices(), enrichState(state)); expressionBuilder.setAbbrevMap(state.getAbbreviations()); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java index fbe40fbac3d..ea9088b1023 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java @@ -225,18 +225,19 @@ private List injectIntoField(ProofScriptArgument meta, ScriptCommandAst args, val = args.namedArgs().get(meta.getName()); if (val == null) { // can also be given w/o colon or equal sign, e.g., "command hide;" - var stringStream = args.positionalArgs().stream() - .map(it -> { - try { - return convert(it, String.class); - } catch (NoSpecifiedConverterException | ConversionException e) { - return ""; - } - }); - // val == true iff the name of the flag appear as a positional argument. - val = stringStream.anyMatch(it -> Objects.equals(it, meta.getName())); + int argNo = 0; + for (Object arg : args.positionalArgs()) { + String s = convert(arg, String.class); + if (s.equals(meta.getName())) { + val = Boolean.TRUE; + handled = List.of(argNo); + break; + } + argNo++; + } + } else { + handled = List.of(meta.getName()); } - handled = List.of(meta.getName()); } try { @@ -293,7 +294,8 @@ public T convert(Class targetType, Object val) } catch (Exception e) { throw new ConversionException( String.format("Could not convert value '%s' from type '%s' to type '%s'", - val, val.getClass().getSimpleName(), targetType.getSimpleName()), + ScriptCommandAst.asReadableString(val), + val.getClass().getSimpleName(), targetType.getSimpleName()), e); } } diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java index 3ae06ad1e50..4eb88209a5c 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java @@ -10,6 +10,9 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Stream; import de.uka.ilkd.key.control.DefaultUserInterfaceControl; import de.uka.ilkd.key.control.KeYEnvironment; @@ -21,6 +24,7 @@ import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.smt.newsmt2.MasterHandlerTest; +import org.jspecify.annotations.NonNull; import org.key_project.util.collection.ImmutableList; import com.fasterxml.jackson.databind.ObjectMapper; @@ -41,7 +45,7 @@ */ public class TestProofScriptCommand { - private static final String ONLY_CASE = null; + private static final String ONLY_CASES = System.getProperty("key.testProofScript.only"); private static final Logger LOGGER = LoggerFactory.getLogger(TestProofScriptCommand.class); public record TestInstance( @@ -57,8 +61,13 @@ public static List data() throws IOException, URISyntaxException { var folder = Paths.get("src/test/resources/de/uka/ilkd/key/scripts/cases") .toAbsolutePath(); - if(ONLY_CASE != null) { - folder = folder.resolve(ONLY_CASE); + Predicate filter; + if(ONLY_CASES != null && !ONLY_CASES.isEmpty()) { + // if ONLY_CASES is set, only run those cases (comma separated) + Set only = Set.of(ONLY_CASES.split(" *, *")); + filter = p -> only.contains(p.getFileName().toString().substring(0, p.getFileName().toString().length() - 4)); + } else { + filter = p -> true; } try (var walker = Files.walk(folder)) { @@ -69,6 +78,9 @@ public static List data() throws IOException, URISyntaxException { List args = new ArrayList<>(files.size()); for (Path path : files) { + if(!filter.test(path)) { + continue; + } try { TestInstance instance = objectMapper.readValue(path.toFile(), TestInstance.class); diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/ValueInjectorTest.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/ValueInjectorTest.java index 991dc9c3af4..c2949ca1b59 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/ValueInjectorTest.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/meta/ValueInjectorTest.java @@ -67,6 +67,7 @@ public void testUnknownArguments() { null); args.put("i", "42"); args.put("b", "true"); + args.put("q", "requiredValue"); args.put("unknownParameter", "unknownValue"); assertThrows(UnknownArgumentException.class, () -> ValueInjector.injection(new PPCommand(), pp, ast)); @@ -79,21 +80,21 @@ public void testVarargsOld() throws Exception { Map args = new HashMap<>(); ScriptCommandAst ast = new ScriptCommandAst("pp", args, new LinkedList<>(), null); - args.put("#literal", "here goes the entire string..."); args.put("i", "42"); args.put("b", "true"); args.put("var_21", "21"); + args.put("q", "requiredValue"); args.put("var_other", "otherString"); ValueInjector.injection(new PPCommand(), pp, ast); - assertEquals("21", pp.varargs.get("21")); - assertEquals("otherString", pp.varargs.get("other")); + assertEquals("21", pp.varargs.get("var_21")); + assertEquals("otherString", pp.varargs.get("var_other")); assertEquals(2, pp.varargs.size()); } @Test public void testInferScriptArguments() throws NoSuchFieldException { List meta = ArgumentsLifter.inferScriptArguments(PP.class); - assertEquals(4, meta.size()); + assertEquals(5, meta.size()); { ProofScriptArgument b = meta.getFirst(); @@ -112,11 +113,29 @@ public void testInferScriptArguments() throws NoSuchFieldException { } { - ProofScriptArgument i = meta.get(2); - assertEquals("s", i.getName()); - assertEquals(PP.class.getDeclaredField("s"), i.getField()); - assertEquals(String.class, i.getType()); - assertFalse(i.isRequired()); + ProofScriptArgument s = meta.get(2); + assertEquals("s", s.getName()); + assertEquals(PP.class.getDeclaredField("s"), s.getField()); + assertEquals(String.class, s.getType()); + assertFalse(s.isRequired()); + } + + { + ProofScriptArgument q = meta.get(3); + assertEquals("q", q.getName()); + assertEquals(PP.class.getDeclaredField("required"), q.getField()); + assertEquals(String.class, q.getType()); + assertTrue(q.isRequired()); + } + + { + ProofScriptArgument vars = meta.get(4); + assertEquals("varargs", vars.getName()); + assertEquals(PP.class.getDeclaredField("varargs"), vars.getField()); + assertEquals(Map.class, vars.getType()); + assertEquals("var_", vars.getOptionalVarArgs().prefix()); + assertSame(String.class, vars.getOptionalVarArgs().as()); + assertTrue(vars.isOptionalVarArgs()); } } diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes2.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes2.yml new file mode 100644 index 00000000000..00bbff5a86b --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/holes2.yml @@ -0,0 +1,7 @@ +# Holes in strings ... requires a bit of care +key: | + \problem { ==> \forall int x; x=x, \forall int x; (x > 0 | x < 2) } +script: | + witness "(\forall int x; (? | x < ?))" as="y"; +goals: + - ==> \forall int x; x = x, y > 0 | y < 2 \ No newline at end of file diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/let.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/let.yml new file mode 100644 index 00000000000..990640ac6a1 --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/let.yml @@ -0,0 +1,7 @@ +key: | + \problem { 1+0 = 2+0 } +script: | + let @p1: "1+0"; + rule add_zero_right on: @p1; +goals: + - ==> 1 = 2 + 0 diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/termsAsStrings.yml b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/termsAsStrings.yml new file mode 100644 index 00000000000..5d36b39c5db --- /dev/null +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/cases/termsAsStrings.yml @@ -0,0 +1,8 @@ +# Give arguments as strings ... requires a bit of care +key: | + \problem { ==> true } +script: | + cut "1 > 0"; +goals: + - ==> 1 > 0, true + - 1 > 0 ==> true \ No newline at end of file From af4df5058b548582af70b06269e09cbd1bd98f1c Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Sat, 11 Oct 2025 14:13:23 +0200 Subject: [PATCH 73/90] repairing JML script tests --- .../java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java | 4 ++++ .../src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java | 7 ++++++- .../test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java | 2 +- .../resources/de/uka/ilkd/key/scripts/jml/AutoOnly2.java | 1 - 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index e9987ca40f1..d1ee6f6148c 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -27,6 +27,7 @@ import de.uka.ilkd.key.scripts.ProofScriptEngine; import de.uka.ilkd.key.scripts.ScriptCommandAst; import de.uka.ilkd.key.scripts.ScriptException; +import de.uka.ilkd.key.scripts.TermWithHoles; import de.uka.ilkd.key.speclang.njml.JmlLexer; import de.uka.ilkd.key.speclang.njml.JmlParser; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofArgContext; @@ -187,6 +188,9 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, pse.getStateMap().putUserData("jml.obtainVarMap", obtainMap); pse.getStateMap().getValueInjector().addConverter(JTerm.class, ObtainAwareTerm.class, oat -> oat.resolve(obtainMap, goal.proof().getServices())); + // TODO: Perhaps have holes also in JML? + pse.getStateMap().getValueInjector().addConverter(TermWithHoles.class, ObtainAwareTerm.class, + oat -> new TermWithHoles(oat.resolve(obtainMap, goal.proof().getServices()))); pse.getStateMap().getValueInjector().addConverter(boolean.class, ObtainAwareTerm.class, oat -> Boolean.parseBoolean(oat.term.toString())); LOGGER.debug("---- Script"); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java index ec7b247db41..510584fefc7 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/RuleCommand.java @@ -4,6 +4,7 @@ package de.uka.ilkd.key.scripts; import java.util.*; +import java.util.stream.Collectors; import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.*; @@ -255,7 +256,11 @@ private TacletApp findTacletApp(Parameters p, EngineState state) throws ScriptEx if (p.occ < 0) { if (matchingApps.size() > 1) { - throw new ScriptException("More than one applicable occurrence"); + // todo make a nice string here! + throw new ScriptException("More than one applicable occurrence:\n" + + matchingApps.stream().map( + ap -> ap.posInOccurrence().subTerm() + " " + ap.matchConditions()) + .collect(Collectors.joining("\n") )); } return matchingApps.get(0); } else { diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java index 0f67cc7ecc3..fa53950a474 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java @@ -37,7 +37,7 @@ public class JmlScriptTest { private static final Logger LOGGER = LoggerFactory.getLogger(JmlScriptTest.class); // Set this to a specific case to only run that case for debugging - private static final String ONLY_CASE = null; + private static final String ONLY_CASE = System.getProperty("key.testJmlScript.only"); // Set this to true to save the proof after running the script private static final boolean SAVE_PROOF = false; diff --git a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AutoOnly2.java b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AutoOnly2.java index a3c11a11113..8be9e20db47 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AutoOnly2.java +++ b/key.core/src/test/resources/de/uka/ilkd/key/scripts/jml/AutoOnly2.java @@ -11,7 +11,6 @@ void test() { /*@ assert b && c ==> c || d \by { auto only:"beta"; - rule "close"; } */ } From adb3c31619cfd14da9ac125c919eb08e9260f579 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Wed, 15 Oct 2025 09:52:10 +0200 Subject: [PATCH 74/90] fixing a NPE bug in JavaProfile solution by Alexander Weigl --- .../strategy/SimplifyTermStrategy.java | 2 +- .../uka/ilkd/key/proof/init/JavaProfile.java | 22 +++++++++++++++++-- .../ilkd/key/gui/StrategySelectionView.java | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/symbolic_execution/strategy/SimplifyTermStrategy.java b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/symbolic_execution/strategy/SimplifyTermStrategy.java index 5ad174975fd..8f9b35a737b 100644 --- a/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/symbolic_execution/strategy/SimplifyTermStrategy.java +++ b/key.core.symbolic_execution/src/main/java/de/uka/ilkd/key/symbolic_execution/strategy/SimplifyTermStrategy.java @@ -117,7 +117,7 @@ public Strategy create(Proof proof, StrategyProperties sp) { */ @Override public StrategySettingsDefinition getSettingsDefinition() { - return JavaProfile.DEFAULT.getSettingsDefinition(); + return JavaProfile.getDefault().getSettingsDefinition(); } } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/init/JavaProfile.java b/key.core/src/main/java/de/uka/ilkd/key/proof/init/JavaProfile.java index 267f4176035..0e76f8a6ef9 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/init/JavaProfile.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/init/JavaProfile.java @@ -20,6 +20,7 @@ import de.uka.ilkd.key.rule.label.TermLabelRefactoring; import de.uka.ilkd.key.rule.merge.MergeRule; import de.uka.ilkd.key.smt.newsmt2.DefinedSymbolsHandler; +import de.uka.ilkd.key.strategy.JavaCardDLStrategyFactory; import de.uka.ilkd.key.strategy.ModularJavaDLStrategyFactory; import de.uka.ilkd.key.strategy.StrategyFactory; @@ -71,7 +72,24 @@ public String description() { public static JavaProfile defaultInstance; public static JavaProfile defaultInstancePermissions; - public static final StrategyFactory DEFAULT = new ModularJavaDLStrategyFactory(); + /** + * The default strategy factory to be used if no other strategy factory is + * specified. + * + * Caution: This used to be constructed at class load time, but cyclic reference between + * clauses made the field be read while the class was not yet fully initialized leading to + * null pointer exceptions. So we now use lazy initialization. + * + * (solution suggested by AW) + */ + private static StrategyFactory DEFAULT; + + public static StrategyFactory getDefault() { + if (DEFAULT == null) { + DEFAULT = new ModularJavaDLStrategyFactory(); + } + return DEFAULT; + } private boolean permissions = false; @@ -144,7 +162,7 @@ protected ImmutableList computeTermLabelConfiguration() @Override protected ImmutableSet getStrategyFactories() { ImmutableSet set = super.getStrategyFactories(); - set = set.add(DEFAULT); + set = set.add(getDefault()); return set; } diff --git a/key.ui/src/main/java/de/uka/ilkd/key/gui/StrategySelectionView.java b/key.ui/src/main/java/de/uka/ilkd/key/gui/StrategySelectionView.java index 37141d6090e..442e098cc4d 100644 --- a/key.ui/src/main/java/de/uka/ilkd/key/gui/StrategySelectionView.java +++ b/key.ui/src/main/java/de/uka/ilkd/key/gui/StrategySelectionView.java @@ -63,7 +63,7 @@ public final class StrategySelectionView extends JPanel implements TabPanel { /** * The always used {@link StrategyFactory}. */ - private static final StrategyFactory FACTORY = JavaProfile.DEFAULT; + private static final StrategyFactory FACTORY = JavaProfile.getDefault(); /** * The {@link StrategySettingsDefinition} of {@link #FACTORY} which defines the UI controls to From cc29e8132cbcabf281cd0a69cfe66c4afba928df Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 24 Oct 2025 15:18:07 +0200 Subject: [PATCH 75/90] improving symbex only macro --- .../ilkd/key/macros/SymbolicExecutionOnlyMacro.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java index 1b876922f45..61496fa5cfa 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java @@ -4,6 +4,7 @@ package de.uka.ilkd.key.macros; import de.uka.ilkd.key.control.TermLabelVisibilityManager; +import de.uka.ilkd.key.logic.op.Junctor; import de.uka.ilkd.key.logic.op.ObserverFunction; import de.uka.ilkd.key.logic.op.UpdateApplication; import de.uka.ilkd.key.proof.Goal; @@ -119,11 +120,20 @@ public static boolean isAdmittedRule(RuleApp ruleApp) { return false; } + /** + * return true if there is a boolean combination of updated modalities + */ private static boolean isUpdatedModality(Term term) { while(term.op() instanceof UpdateApplication) { term = term.sub(1); } - return term.op() instanceof Modality; + if(term.op() instanceof Modality) {; + return true; + } + if(term.op() == Junctor.IMP || term.op() == Junctor.AND) { + return term.subs().stream().allMatch(SymbolicExecutionOnlyMacro::isUpdatedModality); + } + return false; } private static class FilterSymbexStrategy extends FilterStrategy { From 3332915bad8c14a36d2c678a4a13f5b20d986b48 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Fri, 24 Oct 2025 16:29:24 +0200 Subject: [PATCH 76/90] adding an infinity catch on "throw null" in symb ex. --- .../macros/SymbolicExecutionOnlyMacro.java | 8 ++++- .../de/uka/ilkd/key/proof/rules/javaRules.key | 30 ++++++++++++------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java index 61496fa5cfa..52d33cf2ab2 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/SymbolicExecutionOnlyMacro.java @@ -152,12 +152,18 @@ public Name name() { @Override public boolean isApprovedApp(RuleApp app, PosInOccurrence pio, Goal goal) { - if(!hasModality(goal)) { + if(!hasModality(goal) || isThrowNullBranch(goal)) { return false; } return isAdmittedRule(app) && super.isApprovedApp(app, pio, goal); } + /// Special case: Avoid infinite recursion on the "throw null" branch of tryCatchThrow + /// by forbidding continuation on this branch. + private boolean isThrowNullBranch(Goal goal) { + return "Null reference in throw".equals(goal.node().getNodeInfo().getBranchLabel()); + } + private boolean hasModality(Goal goal) { return modalityCache.computeIfAbsent(goal.node().sequent(), this::hasModality); } diff --git a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaRules.key b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaRules.key index 97257fcb1b2..8a496ace751 100644 --- a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaRules.key +++ b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaRules.key @@ -519,16 +519,26 @@ tryCatchThrow { \find(\modality{#allmodal}{.. try { throw #se; #slist } catch ( #t #v0 ) { #slist1 } ...}\endmodality (post)) - \replacewith(\modality{#allmodal}{.. if ( #se == null ) { - try { throw new java.lang.NullPointerException (); } - catch ( #t #v0 ) { #slist1 } - } else if ( #se instanceof #t ) { - #t #v0; - #v0 = (#t) #se; - #slist1 - } else { - throw #se; - } ...}\endmodality (post)) + + // Case NPE: throwing null fails: + "Null reference in throw": + \replacewith(\modality{#allmodal}{.. + try { throw new java.lang.NullPointerException (); } + catch ( #t #v0 ) { #slist1 } ... }\endmodality (post)) + \add( #se = null ==> ) ; + + // Case catch or throw: + "Normal execution" [main]: + \replacewith(\modality{#allmodal}{.. + if ( #se instanceof #t ) { + #t #v0; + #v0 = (#t) #se; + #slist1 + } else { + throw #se; + } ...}\endmodality (post)) + \add( ==> #se = null ) + \heuristics(simplify_prog) \displayname "tryCatchThrow" }; From a7cc2ff296a247f65ba1f985b67b2366a4f923b8 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Tue, 17 Feb 2026 21:43:16 +0100 Subject: [PATCH 77/90] making the assume command use a local taclet. --- .../uka/ilkd/key/scripts/AssumeCommand.java | 54 ++++++++++++++++--- .../util/collection/ImmutableSet.java | 9 +++- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssumeCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssumeCommand.java index c997292a3cb..5781941f9eb 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssumeCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssumeCommand.java @@ -4,14 +4,27 @@ package de.uka.ilkd.key.scripts; import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.logic.TermFactory; +import de.uka.ilkd.key.logic.op.SchemaVariableFactory; +import de.uka.ilkd.key.proof.calculus.JavaDLSequentKit; +import de.uka.ilkd.key.rule.NoFindTaclet; import de.uka.ilkd.key.rule.NoPosTacletApp; import de.uka.ilkd.key.rule.Taclet; import de.uka.ilkd.key.rule.TacletApp; +import de.uka.ilkd.key.rule.tacletbuilder.TacletGoalTemplate; import de.uka.ilkd.key.scripts.meta.Argument; import de.uka.ilkd.key.scripts.meta.Documentation; +import org.key_project.logic.ChoiceExpr; import org.key_project.logic.Name; import org.key_project.logic.op.sv.SchemaVariable; +import org.key_project.prover.rules.ApplicationRestriction; +import org.key_project.prover.rules.TacletApplPart; +import org.key_project.prover.rules.TacletAttributes; +import org.key_project.prover.sequent.SequentFormula; +import org.key_project.util.collection.DefaultImmutableMap; +import org.key_project.util.collection.ImmutableList; +import org.key_project.util.collection.ImmutableSet; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.jspecify.annotations.NullMarked; @@ -23,7 +36,32 @@ */ @NullMarked public class AssumeCommand extends AbstractCommand { - private static final Name TACLET_NAME = new Name("UNSOUND_ASSUME"); + + /** + * The taclet for the assume command is a local constructed taclet that is not available in the + * usual + * KeY taclet base. This is on purpose. Proofs can be conducted and saved with this command, + * but they cannot be reloaded. + */ + private static final Taclet ASSUME_TACLET; + + static { + SchemaVariable sv = SchemaVariableFactory.createFormulaSV(new Name("condition")); + JavaDLSequentKit kit = JavaDLSequentKit.getInstance(); + TacletApplPart applPart = + new TacletApplPart(kit.getEmptySequent(), + new ApplicationRestriction(ApplicationRestriction.IN_SEQUENT_STATE), + ImmutableList.of(), + ImmutableList.of(), ImmutableList.of(), ImmutableList.of()); + SequentFormula sf = new SequentFormula(new TermFactory().createTerm(sv)); + TacletGoalTemplate goal = new TacletGoalTemplate(kit.newAntecedent(ImmutableList.of(sf)), + ImmutableList.of(), ImmutableSet.of()); + ASSUME_TACLET = + new NoFindTaclet(new Name("CHEAT_ASSUME"), applPart, ImmutableList.of(goal), + ImmutableList.of(), + new TacletAttributes("assume", null), DefaultImmutableMap.nilMap(), ChoiceExpr.TRUE, + ImmutableSet.empty()); + } public AssumeCommand() { super(FormulaParameter.class); @@ -37,9 +75,7 @@ public String getName() { public void execute(ScriptCommandAst arguments) throws ScriptException, InterruptedException { var parameter = state().getValueInjector() .inject(new FormulaParameter(), arguments); - Taclet cut = - state.getProof().getEnv().getInitConfigForEnvironment().lookupActiveTaclet(TACLET_NAME); - TacletApp app = NoPosTacletApp.createNoPosTacletApp(cut); + TacletApp app = NoPosTacletApp.createNoPosTacletApp(ASSUME_TACLET); SchemaVariable sv = app.uninstantiatedVars().iterator().next(); app = app.addCheckedInstantiation(sv, parameter.formula, state.getProof().getServices(), @@ -47,10 +83,12 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup state.getFirstOpenAutomaticGoal().apply(app); } - @Documentation(category = "Control", value = """ - The assume command is an **unsound** taclet rule and adds a formula to the antecedent of the current goal - Can be used for debug and proof exploration purposes. - """) + @Documentation(category = "Control", + value = """ + The assume command is an **unsound** taclet rule and adds a formula to the antecedent of the current goal + Can be used for debug and proof exploration purposes. Proof files for proofs with this command cannot + be reloaded. + """) public static class FormulaParameter { @Argument @Documentation("The formula to be assumed.") diff --git a/key.util/src/main/java/org/key_project/util/collection/ImmutableSet.java b/key.util/src/main/java/org/key_project/util/collection/ImmutableSet.java index 2d633534012..0168b88da93 100644 --- a/key.util/src/main/java/org/key_project/util/collection/ImmutableSet.java +++ b/key.util/src/main/java/org/key_project/util/collection/ImmutableSet.java @@ -9,7 +9,6 @@ import java.util.stream.Collector.Characteristics; import java.util.stream.Stream; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; /** @@ -44,6 +43,14 @@ public interface ImmutableSet return DefaultImmutableSet.nil(); } + static ImmutableSet of() { + return empty(); + } + + static ImmutableSet of(T... elems) { + return DefaultImmutableSet.fromImmutableList(ImmutableList.of(elems)); + } + static ImmutableSet from(Iterable ts) { ImmutableSet result = DefaultImmutableSet.nil(); for (T t : ts) { From 5189bd114b32cd8d9355809c380b0a7c215f0eff Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Tue, 17 Feb 2026 23:22:28 +0100 Subject: [PATCH 78/90] correcting conversion in JML scripts --- .../src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java index 9466c3edabb..b2960f06edc 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java @@ -94,10 +94,10 @@ private JTerm convertToTerm(ProofScriptExpressionContext ctx) throws ConversionE } else if (ctx.proofScriptCodeBlock() != null) { throw new ConversionException("A block cannot be used as a term"); } else { - return (JTerm) evaluateExpression((ParserRuleContext) ctx.getChild(0)); + return (JTerm) evaluateExpression((ParserRuleContext) ctx.getChild(ParserRuleContext.class, 0)); } } catch (Exception e) { - throw new ConversionException("Cannot convert expression to term: " + ANTLRUtil.reconstructOriginal(ctx)); + throw new ConversionException("Cannot convert expression to term: " + ANTLRUtil.reconstructOriginal(ctx), e); } } From 3de59bb8948ad433e62bc1cd36b53608b9eeb280 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Tue, 17 Feb 2026 23:38:32 +0100 Subject: [PATCH 79/90] adapting taclets.old.txt to changed throws treatment --- .../de/uka/ilkd/key/nparser/taclets.old.txt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/key.core/src/test/resources/de/uka/ilkd/key/nparser/taclets.old.txt b/key.core/src/test/resources/de/uka/ilkd/key/nparser/taclets.old.txt index ffb4cb55fc2..eaba7dd834f 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/nparser/taclets.old.txt +++ b/key.core/src/test/resources/de/uka/ilkd/key/nparser/taclets.old.txt @@ -17991,21 +17991,22 @@ tryCatchThrow { #slist1 } ... }}| (post)) -\replacewith(#allmodal ((modal operator))|{{ .. - if (#se == null) { - try { - throw new java.lang.NullPointerException(); - } catch (#t #v0) { - #slist1 - } - } else if (#se instanceof #t) { +\add []==>[equals(#se,null)] \replacewith(#allmodal ((modal operator))|{{ .. + if (#se instanceof #t) { #t #v0; #v0 = (#t) #se; #slist1 } else { throw #se; } -... }}| (post)) +... }}| (post)) ; +\add [equals(#se,null)]==>[] \replacewith(#allmodal ((modal operator))|{{ .. + try { + throw new java.lang.NullPointerException(); + } catch (#t #v0) { + #slist1 + } +... }}| (post)) \heuristics(simplify_prog) Choices: programRules:Java} ----------------------------------------------------- From 43f04ab43d09740f708b7509d79a826f130e76a6 Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Tue, 17 Feb 2026 23:51:18 +0100 Subject: [PATCH 80/90] Repaired a merge test case --- .../test/java/de/uka/ilkd/key/rule/merge/MergeRuleTests.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/key.core/src/test/java/de/uka/ilkd/key/rule/merge/MergeRuleTests.java b/key.core/src/test/java/de/uka/ilkd/key/rule/merge/MergeRuleTests.java index 4eee365188f..cac0c44c9fe 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/rule/merge/MergeRuleTests.java +++ b/key.core/src/test/java/de/uka/ilkd/key/rule/merge/MergeRuleTests.java @@ -147,6 +147,10 @@ public void testDoAutomaticGcdProofWithMergePointStatementAndBlockContract() { * has to result in a renaming. An interactive cut in the proof should make sure that the * renaming works and resolves the clashes. The test case includes a "is weakening" goal. * Underlying Java file: "A.java". + * + * Unfortunately, this test case was broken by a change in the taclet database. I replaced the + * proof file by the automatic proof for the same proof obligation, hoping that the same issue + * is still covered. M.U. 2/2026 */ @Test public void testLoadProofWithDiffVarsWithSameNameAndMPS() { From aebcce2f0157e71b3c4534ee8073ec8b0c17aa9b Mon Sep 17 00:00:00 2001 From: Mattias Ulbrich Date: Wed, 18 Feb 2026 00:11:49 +0100 Subject: [PATCH 81/90] missing sameUpdateLevel for tryCatchThrow --- .../src/main/resources/de/uka/ilkd/key/proof/rules/javaRules.key | 1 + 1 file changed, 1 insertion(+) diff --git a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaRules.key b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaRules.key index 8a496ace751..288b84c4884 100644 --- a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaRules.key +++ b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/javaRules.key @@ -519,6 +519,7 @@ tryCatchThrow { \find(\modality{#allmodal}{.. try { throw #se; #slist } catch ( #t #v0 ) { #slist1 } ...}\endmodality (post)) + \sameUpdateLevel // Case NPE: throwing null fails: "Null reference in throw": From f8f38d036665a14f80dec97fb59ac5971822b8f2 Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Mon, 23 Feb 2026 13:00:59 +0100 Subject: [PATCH 82/90] spotless --- .../java/de/uka/ilkd/key/java/JavaInfo.java | 1 - .../key/java/ast/statement/JmlAssert.java | 2 +- .../ilkd/key/macros/ApplyScriptsMacro.java | 80 ++++++----- .../ilkd/key/macros/ScriptAwarePrepMacro.java | 3 +- .../macros/SymbolicExecutionOnlyMacro.java | 48 ++++--- .../key/macros/TryCloseSideBranchesMacro.java | 10 +- .../java/de/uka/ilkd/key/nparser/KeyAst.java | 6 +- .../java/de/uka/ilkd/key/proof/NodeInfo.java | 2 +- .../proof/mgt/SpecificationRepository.java | 5 +- .../key/scripts/AdditionalRulesStrategy.java | 32 +++-- .../de/uka/ilkd/key/scripts/AllCommand.java | 2 +- .../key/scripts/AssertOpenGoalsCommand.java | 11 +- .../de/uka/ilkd/key/scripts/AutoCommand.java | 37 ++--- .../de/uka/ilkd/key/scripts/AxiomCommand.java | 2 +- .../uka/ilkd/key/scripts/BranchesCommand.java | 2 +- .../de/uka/ilkd/key/scripts/CheatCommand.java | 7 +- .../de/uka/ilkd/key/scripts/CutCommand.java | 15 ++- .../scripts/DependencyContractCommand.java | 19 +-- .../de/uka/ilkd/key/scripts/EchoCommand.java | 2 +- .../de/uka/ilkd/key/scripts/EngineState.java | 10 +- .../ilkd/key/scripts/ExpandDefCommand.java | 3 - .../uka/ilkd/key/scripts/ExprEvaluator.java | 57 ++++---- .../de/uka/ilkd/key/scripts/FocusCommand.java | 16 +-- .../de/uka/ilkd/key/scripts/HideCommand.java | 11 +- .../ilkd/key/scripts/InstantiateCommand.java | 29 ++-- .../ilkd/key/scripts/JavascriptCommand.java | 37 ++--- .../de/uka/ilkd/key/scripts/LeaveCommand.java | 9 +- .../de/uka/ilkd/key/scripts/LetCommand.java | 28 ++-- .../de/uka/ilkd/key/scripts/MacroCommand.java | 22 +-- .../uka/ilkd/key/scripts/ObtainCommand.java | 76 +++++++---- .../key/scripts/OneStepSimplifierCommand.java | 46 ++++--- .../ilkd/key/scripts/ProofScriptEngine.java | 5 +- .../de/uka/ilkd/key/scripts/RuleCommand.java | 55 ++++---- .../de/uka/ilkd/key/scripts/SMTCommand.java | 14 +- .../uka/ilkd/key/scripts/SaveInstCommand.java | 13 +- .../ilkd/key/scripts/SaveNewNameCommand.java | 17 +-- .../ilkd/key/scripts/SchemaVarCommand.java | 2 +- .../uka/ilkd/key/scripts/ScriptCommand.java | 2 +- .../ilkd/key/scripts/ScriptCommandAst.java | 2 +- .../uka/ilkd/key/scripts/SelectCommand.java | 26 ++-- .../ilkd/key/scripts/SequentWithHoles.java | 38 ++---- .../uka/ilkd/key/scripts/SetEchoCommand.java | 2 +- .../key/scripts/SetFailOnClosedCommand.java | 2 +- .../key/scripts/TermComparisonWithHoles.java | 127 ++++++++++-------- .../uka/ilkd/key/scripts/TermWithHoles.java | 46 +++---- .../uka/ilkd/key/scripts/UnhideCommand.java | 11 +- .../uka/ilkd/key/scripts/WitnessCommand.java | 62 +++++---- .../key/scripts/meta/ArgumentsLifter.java | 85 ++++++------ .../ilkd/key/scripts/meta/Documentation.java | 1 + .../key/scripts/meta/ProofScriptArgument.java | 7 +- .../ilkd/key/scripts/meta/ValueInjector.java | 7 +- .../de/uka/ilkd/key/speclang/njml/JmlIO.java | 2 +- .../java/de/uka/ilkd/key/util/ANTLRUtil.java | 25 ++-- .../key/scripts/DocumentationGenerator.java | 89 +++++++----- .../uka/ilkd/key/scripts/JmlScriptTest.java | 61 +++++---- .../key/scripts/TestProofScriptCommand.java | 19 ++- .../uka/ilkd/key/gui/ProofScriptWorker.java | 1 - 57 files changed, 716 insertions(+), 635 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/JavaInfo.java b/key.core/src/main/java/de/uka/ilkd/key/java/JavaInfo.java index 94458e38498..17aedd86f73 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/JavaInfo.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/JavaInfo.java @@ -23,7 +23,6 @@ import de.uka.ilkd.key.speclang.HeapContext; import de.uka.ilkd.key.speclang.SpecificationElement; -import org.jspecify.annotations.Nullable; import org.key_project.logic.Name; import org.key_project.logic.sort.Sort; import org.key_project.util.LRUCache; diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java index 2ab17a9fd59..94888f8c258 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java @@ -11,9 +11,9 @@ import de.uka.ilkd.key.logic.op.LocationVariable; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.speclang.jml.pretranslation.TextualJMLAssertStatement; - import de.uka.ilkd.key.speclang.njml.JmlIO; import de.uka.ilkd.key.speclang.njml.JmlParser; + import org.key_project.util.ExtList; import org.key_project.util.collection.ImmutableList; diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index d1ee6f6148c..8404754fda9 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -33,8 +33,8 @@ import de.uka.ilkd.key.speclang.njml.JmlParser.ProofArgContext; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdCaseContext; import de.uka.ilkd.key.speclang.njml.JmlParser.ProofCmdContext; - import de.uka.ilkd.key.util.MiscTools; + import org.key_project.logic.Term; import org.key_project.logic.op.Modality; import org.key_project.prover.engine.ProverTaskListener; @@ -90,12 +90,14 @@ JTerm resolve(Map obtainMap, Services services) { return result; } - private void assertNoObtainVarsLeft(JTerm term, Map obtainMap) { + private void assertNoObtainVarsLeft(JTerm term, + Map obtainMap) { var v = new DefaultVisitor() { @Override public void visit(Term visited) { - if(obtainMap.containsKey(term.op())) { - throw new RuntimeException("Use of obtain variable before it being obtained: " + term.op()); + if (obtainMap.containsKey(term.op())) { + throw new RuntimeException( + "Use of obtain variable before it being obtained: " + term.op()); } } }; @@ -133,17 +135,19 @@ private static JmlAssert getJmlAssert(Node node) { } private static void collectUpdates(JTerm update, Map updates, Services services) { - switch(update.op()) { + switch (update.op()) { case ElementaryUpdate eu -> - updates.put(services.getTermBuilder().var((ProgramVariable) eu.lhs()), update.sub(0)); + updates.put(services.getTermBuilder().var((ProgramVariable) eu.lhs()), + update.sub(0)); - case UpdateJunctor uj-> { + case UpdateJunctor uj -> { collectUpdates(update.sub(0), updates, services); collectUpdates(update.sub(1), updates, services); } default -> - throw new IllegalStateException("Unexpected update operation: " + update.op().getClass()); + throw new IllegalStateException( + "Unexpected update operation: " + update.op().getClass()); } } @@ -177,22 +181,27 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, "Running attached script from goal " + goal.node().serialNr(), 0)); KeyAst.JMLProofScript proofScript = jmlAssert.getAssertionProof(); - Map termMap = getTermMap(jmlAssert, getJavaBlock(goal), proof.getServices()); - // We heavily rely on that variables have been computed before, otherwise this will raise an NPE. - Map obtainMap = makeObtainVarMap(jmlAssert.collectVariablesInProof(null)); - @Nullable OpReplacer updateReplacer = getUpdateReplacer(goal); + Map termMap = + getTermMap(jmlAssert, getJavaBlock(goal), proof.getServices()); + // We heavily rely on that variables have been computed before, otherwise this will + // raise an NPE. + Map obtainMap = + makeObtainVarMap(jmlAssert.collectVariablesInProof(null)); + @Nullable + OpReplacer updateReplacer = getUpdateReplacer(goal); List renderedProof = renderProof(proofScript, termMap, updateReplacer, proof.getServices()); ProofScriptEngine pse = new ProofScriptEngine(proof); pse.setInitiallySelectedGoal(goal); pse.getStateMap().putUserData("jml.obtainVarMap", obtainMap); pse.getStateMap().getValueInjector().addConverter(JTerm.class, ObtainAwareTerm.class, - oat -> oat.resolve(obtainMap, goal.proof().getServices())); + oat -> oat.resolve(obtainMap, goal.proof().getServices())); // TODO: Perhaps have holes also in JML? - pse.getStateMap().getValueInjector().addConverter(TermWithHoles.class, ObtainAwareTerm.class, - oat -> new TermWithHoles(oat.resolve(obtainMap, goal.proof().getServices()))); + pse.getStateMap().getValueInjector().addConverter(TermWithHoles.class, + ObtainAwareTerm.class, + oat -> new TermWithHoles(oat.resolve(obtainMap, goal.proof().getServices()))); pse.getStateMap().getValueInjector().addConverter(boolean.class, ObtainAwareTerm.class, - oat -> Boolean.parseBoolean(oat.term.toString())); + oat -> Boolean.parseBoolean(oat.term.toString())); LOGGER.debug("---- Script"); LOGGER.debug(renderedProof.stream().map(ScriptCommandAst::asCommandLine) .collect(Collectors.joining("\n"))); @@ -216,7 +225,8 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, } - private Map getTermMap(JmlAssert jmlAssert, JavaBlock javaBlock, Services services) { + private Map getTermMap(JmlAssert jmlAssert, JavaBlock javaBlock, + Services services) { SpecificationRepository.@Nullable JmlStatementSpec jmlspec = services.getSpecificationRepository().getStatementSpec(jmlAssert); if (jmlspec == null) { @@ -237,17 +247,20 @@ private Map getTermMap(JmlAssert jmlAssert, JavaBlock } /** - * For some reason, the self variable in the spec is not the same as the self variable and needs to + * For some reason, the self variable in the spec is not the same as the self variable and needs + * to * be corrected. */ - private JTerm correctSelfVar(int index, JavaBlock javaBlock, SpecificationRepository.JmlStatementSpec spec, Services services) { + private JTerm correctSelfVar(int index, JavaBlock javaBlock, + SpecificationRepository.JmlStatementSpec spec, Services services) { final MethodFrame frame = JavaTools.getInnermostMethodFrame(javaBlock, services); final JTerm self = MiscTools.getSelfTerm(frame, services); return spec.getTerm(services, self, index); } - private Map makeObtainVarMap(ImmutableList locationVariables) { + private Map makeObtainVarMap( + ImmutableList locationVariables) { HashMap result = new HashMap<>(); for (LocationVariable lv : locationVariables) { result.put(lv, null); @@ -256,7 +269,8 @@ private Map makeObtainVarMap(ImmutableList renderProof(KeyAst.JMLProofScript script, - Map termMap, @Nullable OpReplacer update, Services services) throws ScriptException { + Map termMap, @Nullable OpReplacer update, Services services) + throws ScriptException { List result = new ArrayList<>(); // Push current settings onto the settings stack result.add(new ScriptCommandAst("set", Map.of("stack", "push"), List.of())); @@ -271,15 +285,15 @@ private static List renderProof(KeyAst.JMLProofScript script, } private static List renderProofCmd(ProofCmdContext ctx, - Map termMap, - @Nullable OpReplacer update, Services services) throws ScriptException { + Map termMap, + @Nullable OpReplacer update, Services services) throws ScriptException { List result = new ArrayList<>(); // Push the current branch context result.add(new ScriptCommandAst("branches", Map.of(), List.of("push"))); // Compose the command itself - if(ctx.obtain != null) { + if (ctx.obtain != null) { ScriptCommandAst command = renderObtainCommand(ctx, termMap, update, services); result.add(command); } else { @@ -289,7 +303,7 @@ private static List renderProofCmd(ProofCmdContext ctx, // handle followup proofCmd if present JmlParser.ProofCmdSuffixContext suffix = ctx.proofCmdSuffix(); - if(suffix != null) { + if (suffix != null) { if (!suffix.proofCmd().isEmpty()) { result.add(new ScriptCommandAst("branches", Map.of(), List.of("single"))); for (ProofCmdContext proofCmdContext : suffix.proofCmd()) { @@ -301,7 +315,7 @@ private static List renderProofCmd(ProofCmdContext ctx, for (ProofCmdCaseContext pcase : suffix.proofCmdCase()) { String label = StringUtil.stripQuotes(pcase.label.getText()); result.add(new ScriptCommandAst("branches", Map.of("branch", label), - List.of("select"))); + List.of("select"))); for (ProofCmdContext proofCmdContext : pcase.proofCmd()) { result.addAll(renderProofCmd(proofCmdContext, termMap, update, services)); } @@ -314,11 +328,12 @@ private static List renderProofCmd(ProofCmdContext ctx, return result; } - private static ScriptCommandAst renderObtainCommand(ProofCmdContext ctx, Map termMap, - @Nullable OpReplacer update, Services services) throws ScriptException { + private static ScriptCommandAst renderObtainCommand(ProofCmdContext ctx, + Map termMap, + @Nullable OpReplacer update, Services services) throws ScriptException { Map named = new HashMap<>(); - String argName = switch(ctx.obtKind.getType()) { + String argName = switch (ctx.obtKind.getType()) { case JmlLexer.SUCH_THAT -> "such_that"; case JmlLexer.EQUAL_SINGLE -> "equals"; case JmlLexer.FROM_GOAL -> "from_goal"; @@ -327,7 +342,7 @@ private static ScriptCommandAst renderObtainCommand(ProofCmdContext ctx, Map termMap, @Nullable OpReplacer update, Services services) { + private static @NonNull ScriptCommandAst renderRegularCommand(ProofCmdContext ctx, + Map termMap, @Nullable OpReplacer update, Services services) { Map named = new HashMap<>(); List positional = new ArrayList<>(); for (ProofArgContext argContext : ctx.proofArg()) { @@ -372,7 +388,7 @@ private static ScriptCommandAst renderObtainCommand(ProofCmdContext ctx, Map getObtainedProgramVars(JmlIO io) { - if(obtainedProgramVars == null) { + if (obtainedProgramVars == null) { var visitor = new ObtainedVarsVisitor(io); ctx.accept(visitor); obtainedProgramVars = visitor.collectedVars; diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/NodeInfo.java b/key.core/src/main/java/de/uka/ilkd/key/proof/NodeInfo.java index 4b77d3a1cf2..8d68d933f5f 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/NodeInfo.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/NodeInfo.java @@ -26,7 +26,6 @@ import de.uka.ilkd.key.rule.Taclet; import de.uka.ilkd.key.rule.TacletApp; -import org.jspecify.annotations.Nullable; import org.key_project.logic.Name; import org.key_project.proof.LocationVariableTracker; import org.key_project.prover.rules.RuleApp; @@ -34,6 +33,7 @@ import org.key_project.prover.sequent.SequentChangeInfo; import org.key_project.util.collection.ImmutableList; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/mgt/SpecificationRepository.java b/key.core/src/main/java/de/uka/ilkd/key/proof/mgt/SpecificationRepository.java index 9b728bd04b9..0a329227ce1 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/mgt/SpecificationRepository.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/mgt/SpecificationRepository.java @@ -167,7 +167,7 @@ private static Taclet getLimitedToUnlimitedTaclet(IObserverFunction limited, ImmutableSLList.nil(), unlimitedTerm)); tacletBuilder.setName( MiscTools.toValidTacletName("unlimit " + getUniqueNameForObserver(unlimited))); - //tacletBuilder.addRuleSet(new RuleSet(new Name("unlimitObserver"))); + // tacletBuilder.addRuleSet(new RuleSet(new Name("unlimitObserver"))); return tacletBuilder.getTaclet(); } @@ -1617,7 +1617,8 @@ public void processJavaType(KeYJavaType kjt) { * list of terms, in * an immutable fasion. Updates require to create instances. *

    - * Note: There is an immutability hole in {@link ProgramVariableCollection} due to mutable + * Note: There is an immutability hole in {@link ProgramVariableCollection} due to + * mutable * {@link Map} *

    * For {@link de.uka.ilkd.key.java.ast.statement.JmlAssert} this is the formula behind the diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java index 39240fdfc1f..1ce2df56b39 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AdditionalRulesStrategy.java @@ -1,12 +1,20 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.scripts; -import org.jspecify.annotations.Nullable; -import org.key_project.prover.rules.Rule; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + import de.uka.ilkd.key.macros.FilterStrategy; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.rule.Taclet; import de.uka.ilkd.key.strategy.Strategy; + import org.key_project.logic.Name; +import org.key_project.prover.rules.Rule; import org.key_project.prover.rules.RuleApp; import org.key_project.prover.rules.RuleSet; import org.key_project.prover.sequent.PosInOccurrence; @@ -15,18 +23,15 @@ import org.key_project.prover.strategy.costbased.TopRuleAppCost; import org.key_project.util.collection.Pair; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import org.jspecify.annotations.Nullable; class AdditionalRulesStrategy extends FilterStrategy { /** Name of that strategy */ private static final Name NAME = new Name( - AdditionalRulesStrategy.class.getSimpleName()); + AdditionalRulesStrategy.class.getSimpleName()); private static final Map TRANSLATIONS = - Map.of("high", "-50", "medium", "1000", "low", "10000"); + Map.of("high", "-50", "medium", "1000", "low", "10000"); private static final RuleAppCost DEFAULT_PRIORITY = NumberRuleAppCost.create(1000); private final List> additionalRules; @@ -45,14 +50,15 @@ private List> parseAddRules(String additionalRules) { RuleAppCost prio; if (parts.length == 2) { String prioStr = parts[1]; - if(prioStr.equals("off")) { + if (prioStr.equals("off")) { prio = TopRuleAppCost.INSTANCE; } prioStr = TRANSLATIONS.getOrDefault(prioStr, prioStr); try { prio = NumberRuleAppCost.create(Integer.parseInt(prioStr)); } catch (NumberFormatException e) { - throw new IllegalArgumentException("Invalid value for additional rule: " + parts[1]); + throw new IllegalArgumentException( + "Invalid value for additional rule: " + parts[1]); } } else { prio = DEFAULT_PRIORITY; @@ -93,7 +99,7 @@ public boolean isApprovedApp(RuleApp app, PosInOccurrence pio, Goal goal) { private @Nullable RuleAppCost computeLocalCost(Rule rule) { String name = rule.name().toString(); Optional cost = lookup(name); - if(cost.isPresent()) { + if (cost.isPresent()) { return cost.get(); } @@ -101,7 +107,7 @@ public boolean isApprovedApp(RuleApp app, PosInOccurrence pio, Goal goal) { for (RuleSet rs : taclet.getRuleSets()) { String rname = rs.name().toString(); cost = lookup(rname); - if(cost.isPresent()) { + if (cost.isPresent()) { return cost.get(); } } @@ -123,4 +129,4 @@ public boolean isStopAtFirstNonCloseableGoal() { } -} \ No newline at end of file +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AllCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AllCommand.java index aad15803e3f..2d1f4012279 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AllCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AllCommand.java @@ -14,7 +14,7 @@ Executes a given block of script commands on all open goals. The current goal is set to each open goal in turn while executing the block. It expects exactly one positional argument, which is the block to be executed on each goal. - + #### Examples: * `onAll { smt solver="z3"; }` * `onAll { auto; }` diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertOpenGoalsCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertOpenGoalsCommand.java index f4aa0ff05ab..d4821e8c04a 100755 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertOpenGoalsCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AssertOpenGoalsCommand.java @@ -45,12 +45,13 @@ public String getName() { /** * The Assigned parameters (currently only the passed goals). */ - @Documentation(category = "Control", value = """ - The assert command checks if the number of open and enabled goals is equal to the given number. - If not, the script is halted with an error message. + @Documentation(category = "Control", + value = """ + The assert command checks if the number of open and enabled goals is equal to the given number. + If not, the script is halted with an error message. - Note: This command was called "assert" originally. - """) + Note: This command was called "assert" originally. + """) public static class Parameters { /** * The number of open and enabled goals. diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java index c6199a2c88a..2d259dd1023 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AutoCommand.java @@ -95,10 +95,12 @@ public void execute(ScriptCommandAst args) throws ScriptException, InterruptedEx final Strategy originalStrategy = state.getProof().getActiveStrategy(); if (arguments.additionalRules != null) { - state.getProof().setActiveStrategy(new AdditionalRulesStrategy(originalStrategy, arguments.additionalRules, false)); + state.getProof().setActiveStrategy( + new AdditionalRulesStrategy(originalStrategy, arguments.additionalRules, false)); } if (arguments.onlyRules != null) { - state.getProof().setActiveStrategy(new AdditionalRulesStrategy(originalStrategy, arguments.onlyRules, true)); + state.getProof().setActiveStrategy( + new AdditionalRulesStrategy(originalStrategy, arguments.onlyRules, true)); } // Give some feedback @@ -171,7 +173,7 @@ private void setupFocussedBreakpointStrategy(final String maybeMatchesRegEx, new AbstractProofControl.FocussedAutoModeTaskListener(services.getProof())); } - @Documentation(category = "Fundamental", value =""" + @Documentation(category = "Fundamental", value = """ The AutoCommand invokes the automatic strategy "Auto" of KeY (which is also launched by when clicking the "Auto" button in the GUI). It can be used to try to automatically prove the current goal. @@ -201,8 +203,9 @@ public static class Parameters implements ValueInjector.VerifyableParameters { * Run on formula matching the given regex */ @Option(value = "breakpoint") - @Documentation("When doing symbolic execution by auto, this option can be used to set a Java statement at which " + - "symbolic execution has to stop.") + @Documentation("When doing symbolic execution by auto, this option can be used to set a Java statement at which " + + + "symbolic execution has to stop.") public @Nullable String breakpoint = null; @Flag(value = "modelsearch") @@ -228,26 +231,26 @@ may be a showstopper (if expansion increases the complexity on the sequent too m @Option(value = "add") @Documentation(""" - Additional rules to be used by the auto strategy. The rules have to be given as a - comma-separated list of rule names and rule set names. Each entry can be assigned to a priority - (high, low, medium or a natural number) using an equals sign. - Cannot be combined with the 'only' parameter. - """) + Additional rules to be used by the auto strategy. The rules have to be given as a + comma-separated list of rule names and rule set names. Each entry can be assigned to a priority + (high, low, medium or a natural number) using an equals sign. + Cannot be combined with the 'only' parameter. + """) public @Nullable String additionalRules; @Option(value = "only") @Documentation(""" - Limit the rules to be used by the auto strategy. The rules have to be given as a - comma-separated list of rule names and rule set names. Each entry can be assigned to a priority - (high, low, medium or a natural number) using an equals sign. - All rules application which do not match the given names will be disabled. - Cannot be combined with the 'add' parameter. - """) + Limit the rules to be used by the auto strategy. The rules have to be given as a + comma-separated list of rule names and rule set names. Each entry can be assigned to a priority + (high, low, medium or a natural number) using an equals sign. + All rules application which do not match the given names will be disabled. + Cannot be combined with the 'add' parameter. + """) public @Nullable String onlyRules; @Override public void verifyParameters() throws IllegalArgumentException, InjectionException { - if(onlyRules != null && additionalRules != null) { + if (onlyRules != null && additionalRules != null) { throw new InjectionException("Parameters 'add' and 'only' are mutually exclusive."); } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/AxiomCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/AxiomCommand.java index 5da8270eb51..c12d723c08d 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/AxiomCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/AxiomCommand.java @@ -9,8 +9,8 @@ import de.uka.ilkd.key.rule.Taclet; import de.uka.ilkd.key.rule.TacletApp; import de.uka.ilkd.key.scripts.meta.Argument; - import de.uka.ilkd.key.scripts.meta.Documentation; + import org.key_project.logic.Name; import org.key_project.logic.op.sv.SchemaVariable; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java index 9d6c186b7b9..2bc0a829b88 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java @@ -117,7 +117,7 @@ private Goal findGoalByName(Node root, String branch) throws ScriptException { if (branch.equals(label)) { return findGoalByNode(root.proof(), node); } - number ++; + number++; } throw new ScriptException( "Unknown branch " + branch + ". Known branches are " + knownBranchLabels); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java index e3194efcb71..0e1a0959044 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/CheatCommand.java @@ -9,8 +9,8 @@ import de.uka.ilkd.key.rule.NoPosTacletApp; import de.uka.ilkd.key.rule.Taclet; import de.uka.ilkd.key.rule.TacletApp; - import de.uka.ilkd.key.scripts.meta.Documentation; + import org.key_project.logic.ChoiceExpr; import org.key_project.logic.Name; import org.key_project.prover.rules.ApplicationRestriction; @@ -31,8 +31,9 @@ public class CheatCommand extends NoArgumentCommand { static { TacletApplPart applPart = new TacletApplPart(JavaDLSequentKit.getInstance().getEmptySequent(), - new ApplicationRestriction(ApplicationRestriction.IN_SEQUENT_STATE), ImmutableList.of(), - ImmutableList.of(), ImmutableList.of(), ImmutableList.of()); + new ApplicationRestriction(ApplicationRestriction.IN_SEQUENT_STATE), + ImmutableList.of(), + ImmutableList.of(), ImmutableList.of(), ImmutableList.of()); CHEAT_TACLET = new NoFindTaclet(new Name("CHEAT"), applPart, ImmutableList.of(), ImmutableList.of(), new TacletAttributes("cheat", null), DefaultImmutableMap.nilMap(), ChoiceExpr.TRUE, diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java index cc89557ddcc..7fb7396fc97 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/CutCommand.java @@ -10,8 +10,8 @@ import de.uka.ilkd.key.rule.Taclet; import de.uka.ilkd.key.rule.TacletApp; import de.uka.ilkd.key.scripts.meta.Argument; - import de.uka.ilkd.key.scripts.meta.Documentation; + import org.key_project.logic.Name; import org.key_project.logic.op.sv.SchemaVariable; @@ -51,18 +51,19 @@ static void execute(EngineState state, Parameters args) throws ScriptException { TacletApp app = NoPosTacletApp.createNoPosTacletApp(cut); SchemaVariable sv = app.uninstantiatedVars().iterator().next(); - var formula = state.getProof().getServices().getTermBuilder().convertToFormula(args.formula); + var formula = + state.getProof().getServices().getTermBuilder().convertToFormula(args.formula); app = app.addCheckedInstantiation(sv, formula, state.getProof().getServices(), true); state.getFirstOpenAutomaticGoal().apply(app); } @Documentation(category = "Fundamental", value = """ - The cut command makes a case distinction (a cut) on a formula on the current proof goal. - From within JML scripts, the alias 'assert' is more common than using 'cut'. - If followed by a `\\by proof` suffix in JML, it refers the sequent where - the cut formula is introduced to the succedent (i.e. where it is to be established). - """) + The cut command makes a case distinction (a cut) on a formula on the current proof goal. + From within JML scripts, the alias 'assert' is more common than using 'cut'. + If followed by a `\\by proof` suffix in JML, it refers the sequent where + the cut formula is introduced to the succedent (i.e. where it is to be established). + """) public static class Parameters { @Argument @Documentation("The formula to make the case distinction on.") diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java index 587b7bf5799..4cfbcf323e7 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/DependencyContractCommand.java @@ -14,7 +14,6 @@ import de.uka.ilkd.key.scripts.meta.Documentation; import de.uka.ilkd.key.scripts.meta.Option; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.key_project.logic.PosInTerm; import org.key_project.logic.Term; import org.key_project.prover.sequent.PosInOccurrence; @@ -23,6 +22,7 @@ import org.key_project.util.collection.ImmutableArray; import org.key_project.util.collection.ImmutableList; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.jspecify.annotations.Nullable; /** @@ -110,17 +110,18 @@ private void apply(Goal goal, UseDependencyContractApp ruleApp, Parameters argum goal.apply(ruleApp); } - @Documentation(category = "Fundamental", value = """ - The dependency command applies a dependency contract to a specified term in the current goal. - Dependency contracts allow you to do modular reasoning. If for a heap-dependent function symbol, - no changes occur inside the dependency set of this function, the result remains the same. - This can be applied to model methods, model fields or invariants. - """) + @Documentation(category = "Fundamental", + value = """ + The dependency command applies a dependency contract to a specified term in the current goal. + Dependency contracts allow you to do modular reasoning. If for a heap-dependent function symbol, + no changes occur inside the dependency set of this function, the result remains the same. + This can be applied to model methods, model fields or invariants. + """) public static class Parameters { @Documentation("The term to which the dependency contract should be applied. " + - "This term must occur in the current goal. " + - "And it must be the invocation of a heap-dependent observer function symbol.") + "This term must occur in the current goal. " + + "And it must be the invocation of a heap-dependent observer function symbol.") @Option(value = "on") public @MonotonicNonNull JTerm on; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/EchoCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/EchoCommand.java index 1095212d468..b9ef830fcbe 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/EchoCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/EchoCommand.java @@ -5,8 +5,8 @@ import de.uka.ilkd.key.scripts.meta.Argument; - import de.uka.ilkd.key.scripts.meta.Documentation; + import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java index 0581050043a..f5a1c350b0d 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java @@ -15,24 +15,18 @@ import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.NamespaceSet; -import de.uka.ilkd.key.nparser.KeYParser.ProofScriptExpressionContext; import de.uka.ilkd.key.nparser.KeyIO; import de.uka.ilkd.key.parser.ParserException; import de.uka.ilkd.key.pp.AbbrevMap; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Node; import de.uka.ilkd.key.proof.Proof; -import de.uka.ilkd.key.scripts.meta.ConversionException; -import de.uka.ilkd.key.scripts.meta.Converter; -import de.uka.ilkd.key.scripts.meta.NoSpecifiedConverterException; import de.uka.ilkd.key.scripts.meta.ValueInjector; import de.uka.ilkd.key.settings.ProofSettings; import org.key_project.logic.sort.Sort; -import org.key_project.prover.sequent.Semisequent; import org.key_project.prover.sequent.Sequent; import org.key_project.util.collection.ImmutableList; -import org.key_project.util.java.StringUtil; import org.antlr.v4.runtime.CharStreams; import org.jspecify.annotations.NonNull; @@ -92,11 +86,11 @@ private ValueInjector createDefaultValueInjector() { // to terms with holes v.addConverter(TermWithHoles.class, String.class, - str -> TermWithHoles.fromString(this, str)); + str -> TermWithHoles.fromString(this, str)); // to sequents with holes v.addConverter(SequentWithHoles.class, String.class, - str -> SequentWithHoles.fromString(this, str)); + str -> SequentWithHoles.fromString(this, str)); // from KeY parse tree to everything ExprEvaluator exprEvaluator = new ExprEvaluator(this); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java index 8eca2a44b61..295dff68096 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExpandDefCommand.java @@ -3,8 +3,6 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.scripts; -import de.uka.ilkd.key.logic.JTerm; -import de.uka.ilkd.key.logic.equality.TermLabelsProperty; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.rule.PosTacletApp; @@ -12,7 +10,6 @@ import de.uka.ilkd.key.scripts.meta.Option; import org.key_project.logic.PosInTerm; -import org.key_project.logic.Term; import org.key_project.prover.proof.rulefilter.TacletFilter; import org.key_project.prover.rules.Taclet; import org.key_project.prover.sequent.PosInOccurrence; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java index b2960f06edc..4d380292023 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java @@ -6,31 +6,23 @@ import java.net.URI; import de.uka.ilkd.key.logic.JTerm; -import de.uka.ilkd.key.nparser.KeYParser; import de.uka.ilkd.key.nparser.KeYParser.*; -import de.uka.ilkd.key.nparser.KeYParserBaseVisitor; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.nparser.builder.ExpressionBuilder; - import de.uka.ilkd.key.proof.calculus.JavaDLSequentKit; import de.uka.ilkd.key.scripts.meta.ConversionException; -import de.uka.ilkd.key.scripts.meta.Converter; -import de.uka.ilkd.key.scripts.meta.NoSpecifiedConverterException; import de.uka.ilkd.key.scripts.meta.ValueInjector; import de.uka.ilkd.key.util.ANTLRUtil; -import org.key_project.logic.sort.Sort; -import org.key_project.prover.sequent.Semisequent; -import org.key_project.prover.sequent.Sequent; -import org.antlr.v4.runtime.ParserRuleContext; +import org.key_project.prover.sequent.Sequent; import org.key_project.prover.sequent.SequentFormula; -import org.key_project.prover.sequent.SequentKit; import org.key_project.util.collection.ImmutableList; import org.key_project.util.java.StringUtil; + +import org.antlr.v4.runtime.ParserRuleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.key_project.util.java.StringUtil.trim; /// Evaluates expression inside of proof script to their appropriate type. /// @@ -53,7 +45,7 @@ class ExprEvaluator { private Object evaluateExpression(ParserRuleContext ctx) { var expressionBuilder = - new ExpressionBuilder(state.getProof().getServices(), state.getCurrentNamespaces()); + new ExpressionBuilder(state.getProof().getServices(), state.getCurrentNamespaces()); expressionBuilder.setAbbrevMap(state.getAbbreviations()); var t = ctx.accept(expressionBuilder); var warnings = expressionBuilder.getBuildingIssues(); @@ -71,16 +63,19 @@ public void addConvertersToValueInjector(ValueInjector v) { v.addConverter(JTerm.class, ProofScriptExpressionContext.class, this::convertToTerm); v.addConverter(Sequent.class, ProofScriptExpressionContext.class, this::convertToSequent); v.addConverter(TermWithHoles.class, ProofScriptExpressionContext.class, - ctx -> TermWithHoles.fromProofScriptExpression(state, ctx)); + ctx -> TermWithHoles.fromProofScriptExpression(state, ctx)); v.addConverter(SequentWithHoles.class, ProofScriptExpressionContext.class, - ctx -> SequentWithHoles.fromParserContext(state, ctx)); - v.addConverter(ScriptBlock.class, ProofScriptExpressionContext.class, this::convertToScriptBlock); + ctx -> SequentWithHoles.fromParserContext(state, ctx)); + v.addConverter(ScriptBlock.class, ProofScriptExpressionContext.class, + this::convertToScriptBlock); } - private ScriptBlock convertToScriptBlock(ProofScriptExpressionContext ctx) throws ConversionException { - if(ctx.proofScriptCodeBlock() == null) { - throw new ConversionException("Need a script block here, not: " + ANTLRUtil.reconstructOriginal(ctx)); + private ScriptBlock convertToScriptBlock(ProofScriptExpressionContext ctx) + throws ConversionException { + if (ctx.proofScriptCodeBlock() == null) { + throw new ConversionException( + "Need a script block here, not: " + ANTLRUtil.reconstructOriginal(ctx)); } URI uri = KeyAst.ProofScript.getUri(ctx.start); return KeyAst.ProofScript.asAst(uri, ctx.proofScriptCodeBlock()); @@ -94,10 +89,12 @@ private JTerm convertToTerm(ProofScriptExpressionContext ctx) throws ConversionE } else if (ctx.proofScriptCodeBlock() != null) { throw new ConversionException("A block cannot be used as a term"); } else { - return (JTerm) evaluateExpression((ParserRuleContext) ctx.getChild(ParserRuleContext.class, 0)); + return (JTerm) evaluateExpression( + (ParserRuleContext) ctx.getChild(ParserRuleContext.class, 0)); } } catch (Exception e) { - throw new ConversionException("Cannot convert expression to term: " + ANTLRUtil.reconstructOriginal(ctx), e); + throw new ConversionException( + "Cannot convert expression to term: " + ANTLRUtil.reconstructOriginal(ctx), e); } } @@ -112,10 +109,12 @@ private Sequent convertToSequent(ProofScriptExpressionContext ctx) throws Conver return (Sequent) evaluateExpression(ctx.seq()); } else { JTerm term = (JTerm) evaluateExpression((ParserRuleContext) ctx.getChild(0)); - return JavaDLSequentKit.createSuccSequent(ImmutableList.of(new SequentFormula(term))); + return JavaDLSequentKit + .createSuccSequent(ImmutableList.of(new SequentFormula(term))); } } catch (Exception e) { - throw new ConversionException("Cannot convert expression to sequent: " + ANTLRUtil.reconstructOriginal(ctx)); + throw new ConversionException( + "Cannot convert expression to sequent: " + ANTLRUtil.reconstructOriginal(ctx)); } } @@ -126,18 +125,22 @@ private Boolean convertToBoolean(ProofScriptExpressionContext ctx) throws Conver String text = StringUtil.trim(ctx.string_literal().getText(), '"'); return Boolean.parseBoolean(text); } else { - throw new ConversionException("Cannot convert expression to boolean: " + ANTLRUtil.reconstructOriginal(ctx)); + throw new ConversionException( + "Cannot convert expression to boolean: " + ANTLRUtil.reconstructOriginal(ctx)); } } - private Integer convertToInteger(ProofScriptExpressionContext proofScriptExpressionContext) throws ConversionException { + private Integer convertToInteger(ProofScriptExpressionContext proofScriptExpressionContext) + throws ConversionException { if (proofScriptExpressionContext.integer() != null) { return Integer.parseInt(proofScriptExpressionContext.integer().getText()); } else if (proofScriptExpressionContext.string_literal() != null) { - String text = StringUtil.trim(proofScriptExpressionContext.string_literal().getText(), '"'); + String text = + StringUtil.trim(proofScriptExpressionContext.string_literal().getText(), '"'); return Integer.parseInt(text); } else { - throw new ConversionException("Cannot convert expression to integer: " + ANTLRUtil.reconstructOriginal(proofScriptExpressionContext)); + throw new ConversionException("Cannot convert expression to integer: " + + ANTLRUtil.reconstructOriginal(proofScriptExpressionContext)); } } @@ -149,4 +152,4 @@ private String convertToString(ProofScriptExpressionContext ctx) { } } -} \ No newline at end of file +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java index 0b12d0eaeda..e8fd5cc9c81 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java @@ -14,20 +14,16 @@ import de.uka.ilkd.key.rule.TacletApp; import de.uka.ilkd.key.rule.inst.SVInstantiations; import de.uka.ilkd.key.scripts.meta.Argument; - import de.uka.ilkd.key.scripts.meta.Documentation; + import org.key_project.logic.Name; import org.key_project.logic.PosInTerm; -import org.key_project.logic.Term; import org.key_project.logic.op.sv.SchemaVariable; import org.key_project.prover.sequent.PosInOccurrence; -import org.key_project.prover.sequent.Sequent; import org.key_project.prover.sequent.SequentFormula; -import org.key_project.util.collection.ImmutableList; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import static de.uka.ilkd.key.logic.equality.RenamingTermProperty.RENAMING_TERM_PROPERTY; /** * The command "focus" allows you to select formulas from the current sequent @@ -53,13 +49,13 @@ public FocusCommand() { The command "focus" allows you to select formulas from the current sequent to focus verification on. This means that all other formulas are discarded (i.e. hidden using `hide_right`, `hide_left`). - + Benefits are: The automation is guided into focussing on a relevant set of formulas. - + The selected set of sequent formulas can be regarded as an equivalent to a believed "unsat core" of the sequent. - + #### Examples: - `focus x > 2 ==> x > 1` only keeps the mentioned to formulas in the current goal removing all other formulas that could distract the automation. @@ -92,14 +88,14 @@ private void hideAll(SequentWithHoles toKeep) throws ScriptException { assert goal != null : "not null by contract of the method"; for (SequentFormula seqFormula : goal.sequent().antecedent().asList()) { - if(!toKeep.containsAntecendent(seqFormula)) { + if (!toKeep.containsAntecendent(seqFormula)) { Taclet tac = getHideTaclet("left"); makeTacletApp(goal, seqFormula, tac, true); } } for (SequentFormula seqFormula : goal.sequent().succedent().asList()) { - if(!toKeep.containsSuccedent(seqFormula)) { + if (!toKeep.containsSuccedent(seqFormula)) { Taclet tac = getHideTaclet("right"); makeTacletApp(goal, seqFormula, tac, false); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/HideCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/HideCommand.java index 77ef42f4302..a4aa880fb0f 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/HideCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/HideCommand.java @@ -10,8 +10,8 @@ import de.uka.ilkd.key.rule.Taclet; import de.uka.ilkd.key.rule.TacletApp; import de.uka.ilkd.key.scripts.meta.Argument; - import de.uka.ilkd.key.scripts.meta.Documentation; + import org.key_project.logic.Name; import org.key_project.logic.PosInTerm; import org.key_project.logic.Term; @@ -100,10 +100,11 @@ public String getName() { return "hide"; } - @Documentation(category = "Control", value = """ - The hide command hides all formulas of the current proof goal that are in the given sequent. - The formulas in the given sequent are hidden using the taclets hide_left and hide_right. - """) + @Documentation(category = "Control", + value = """ + The hide command hides all formulas of the current proof goal that are in the given sequent. + The formulas in the given sequent are hidden using the taclets hide_left and hide_right. + """) public static class Parameters { @Argument @MonotonicNonNull diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java index 45424416ce6..56b841a8e5a 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/InstantiateCommand.java @@ -222,19 +222,20 @@ public String getName() { return "instantiate"; } - @Documentation(category = "Fundamental", value = """ - Instantiate a universally quantified formula (in the antecedent; - or an existentially quantified formula in succedent) by a term. - One of `var` or `formula` must be specified. If `var` is given, the formula is determined by looking for - a particular occurrence of a quantifier over that variable name. - If `formula` is given, that quantified formula is used directly. - `with` must be specified. - - #### Examples: - - * `instantiate var:a occ:2 with:a_8 hide` - * `instantiate formula:"\\forall int a; phi(a)" with="a_8"` - """) + @Documentation(category = "Fundamental", + value = """ + Instantiate a universally quantified formula (in the antecedent; + or an existentially quantified formula in succedent) by a term. + One of `var` or `formula` must be specified. If `var` is given, the formula is determined by looking for + a particular occurrence of a quantifier over that variable name. + If `formula` is given, that quantified formula is used directly. + `with` must be specified. + + #### Examples: + + * `instantiate var:a occ:2 with:a_8 hide` + * `instantiate formula:"\\forall int a; phi(a)" with="a_8"` + """) public static class Parameters { @Documentation("The toplevel quantified formula to instantiate. Placeholder matching symbols can be used.") @Option(value = "formula") @@ -251,7 +252,7 @@ public static class Parameters { public @Nullable int occ = 1; @Documentation("If given, the rule used for instantiation is the one that hides the instantiated formula to " - + "prevent it from being used for further automatic proof steps.") + + "prevent it from being used for further automatic proof steps.") @Flag("hide") public boolean hide; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/JavascriptCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/JavascriptCommand.java index 77ef4459391..dc7466c2a67 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/JavascriptCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/JavascriptCommand.java @@ -12,8 +12,8 @@ import de.uka.ilkd.key.pp.AbbrevException; import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.scripts.meta.Argument; - import de.uka.ilkd.key.scripts.meta.Documentation; + import org.key_project.prover.sequent.Sequent; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -71,23 +71,24 @@ public String getName() { return "javascript"; } - @Documentation(category = "Internal", value = """ - This command allows to execute arbitrary JavaScript code. The code is executed in a context where - the current selected goal is available as `goal` and a function `setVar(v,t)` is - available to set an abbreviation (where `v` is the name of the variable including the - leading `@` and `t` is either a term or a string that can be parsed as a term). - - #### Example: - ``` - javascript { - var x = goal.getAntecedent().get(0).getFormula(); - setVar("@myVar", x); - } - ``` - - This command is powerful but should be used with care, as it can easily lead to unsound proofs if - used incorrectly. - """) + @Documentation(category = "Internal", + value = """ + This command allows to execute arbitrary JavaScript code. The code is executed in a context where + the current selected goal is available as `goal` and a function `setVar(v,t)` is + available to set an abbreviation (where `v` is the name of the variable including the + leading `@` and `t` is either a term or a string that can be parsed as a term). + + #### Example: + ``` + javascript { + var x = goal.getAntecedent().get(0).getFormula(); + setVar("@myVar", x); + } + ``` + + This command is powerful but should be used with care, as it can easily lead to unsound proofs if + used incorrectly. + """) public static class Parameters { @Documentation("The JavaScript code to execute.") @Argument diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/LeaveCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/LeaveCommand.java index a7d3689e61f..3f0c01eba9b 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/LeaveCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/LeaveCommand.java @@ -5,14 +5,15 @@ import de.uka.ilkd.key.control.AbstractUserInterfaceControl; import de.uka.ilkd.key.proof.Goal; - import de.uka.ilkd.key.scripts.meta.Documentation; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@Documentation(category = "Control", value = """ - Leave the current goal as it is. Technically, this - marks the current goal to be 'interactive' that is ignored by script commands or calls to automation.""") +@Documentation(category = "Control", + value = """ + Leave the current goal as it is. Technically, this + marks the current goal to be 'interactive' that is ignored by script commands or calls to automation.""") public class LeaveCommand extends NoArgumentCommand { private static final Logger LOGGER = LoggerFactory.getLogger(LeaveCommand.class.getName()); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java index 0faa7026522..0e369bfac1e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/LetCommand.java @@ -6,16 +6,13 @@ import java.util.List; import java.util.Map; -import de.uka.ilkd.key.control.AbstractUserInterfaceControl; import de.uka.ilkd.key.logic.JTerm; -import de.uka.ilkd.key.nparser.KeYParser; import de.uka.ilkd.key.pp.AbbrevMap; import de.uka.ilkd.key.scripts.meta.Documentation; import de.uka.ilkd.key.scripts.meta.OptionalVarargs; import de.uka.ilkd.key.scripts.meta.ProofScriptArgument; import org.jspecify.annotations.NullMarked; -import org.jspecify.annotations.Nullable; /// The *let* command lets you introduce entries to the abbreviation table. /// ``` @@ -37,18 +34,19 @@ @NullMarked public class LetCommand extends AbstractCommand { - @Documentation(category = "Fundamental", value = """ - The let command lets you introduce entries to the abbreviation table. - - let @abbrev1=term1 ... @abbrev2=term2; - - or - - letf @abbrev1=term1 ... @abbrev2=term2; - - One or more key-value pairs are supported where key starts is @ followed by an identifier and - value is a term. - If letf if used instead of let, the let bindings are overridden otherwise conflicts results into an exception.""") + @Documentation(category = "Fundamental", + value = """ + The let command lets you introduce entries to the abbreviation table. + + let @abbrev1=term1 ... @abbrev2=term2; + + or + + letf @abbrev1=term1 ... @abbrev2=term2; + + One or more key-value pairs are supported where key starts is @ followed by an identifier and + value is a term. + If letf if used instead of let, the let bindings are overridden otherwise conflicts results into an exception.""") public static class Parameters { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/MacroCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/MacroCommand.java index cffb75e9f57..3788587a8a8 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/MacroCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/MacroCommand.java @@ -19,13 +19,14 @@ import de.uka.ilkd.key.scripts.meta.Option; import de.uka.ilkd.key.scripts.meta.OptionalVarargs; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.key_project.logic.PosInTerm; import org.key_project.prover.engine.TaskStartedInfo; import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.prover.sequent.Sequent; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.jspecify.annotations.Nullable; + /** * Command to invoke a user-defined macro (like from UI) * @@ -169,18 +170,19 @@ private static String formatTermString(String str) { .replace(" +", " "); } - @Documentation(category = "Fundamental", value = """ - The MacroCommand invokes one of KeY's macros. The macro must be registered to KeY's services. + @Documentation(category = "Fundamental", + value = """ + The MacroCommand invokes one of KeY's macros. The macro must be registered to KeY's services. - The command takes the name of the macro as first argument, followed by optional - parameters to configure the macro. + The command takes the name of the macro as first argument, followed by optional + parameters to configure the macro. - The macro is applied to the first open automatic goal in the proof. + The macro is applied to the first open automatic goal in the proof. - #### Examples: - * `macro "prop-split"` - * `macro "auto-pilot"` - """) + #### Examples: + * `macro "prop-split"` + * `macro "auto-pilot"` + """) public static class Parameters { @Argument @Documentation("Macro name") diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ObtainCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ObtainCommand.java index 2576e9250d5..7e4a0b6d3aa 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ObtainCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ObtainCommand.java @@ -3,18 +3,19 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.scripts; +import java.util.*; + import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.op.*; import de.uka.ilkd.key.proof.*; import de.uka.ilkd.key.rule.*; +import de.uka.ilkd.key.rule.Taclet; import de.uka.ilkd.key.scripts.meta.*; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.jspecify.annotations.Nullable; + import org.key_project.logic.Name; import org.key_project.logic.PosInTerm; import org.key_project.logic.op.sv.SchemaVariable; -import de.uka.ilkd.key.rule.Taclet; import org.key_project.prover.sequent.FormulaChangeInfo; import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.prover.sequent.SemisequentChangeInfo; @@ -22,7 +23,8 @@ import org.key_project.util.collection.ImmutableList; import org.key_project.util.collection.ImmutableSet; -import java.util.*; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.jspecify.annotations.Nullable; /** * Command that applies a calculus rule All parameters are passed as strings and converted by the @@ -55,14 +57,16 @@ public void execute(ScriptCommandAst ast) throws ScriptException, InterruptedException { var args = state().getValueInjector().inject(new Parameters(), ast); - var obtainMap = (Map)state().getUserData("jml.obtainVarMap"); - if(obtainMap == null) { - throw new ScriptException("No obtain variable map found. This command must be used within a JML proof."); + var obtainMap = (Map) state().getUserData("jml.obtainVarMap"); + if (obtainMap == null) { + throw new ScriptException( + "No obtain variable map found. This command must be used within a JML proof."); } LocationVariable var = obtainMap.keySet().stream() .filter(lv -> lv.name().toString().equals(args.var)) - .findAny().orElseThrow(() -> new ScriptException("No such obtain variable registered: " + args.var)); + .findAny().orElseThrow( + () -> new ScriptException("No such obtain variable registered: " + args.var)); JTerm skolem; if (args.equals != null) { @@ -72,7 +76,8 @@ public void execute(ScriptCommandAst ast) } else if (args.fromGoal) { skolem = executeFromGoal(var); } else { - throw new ScriptException("Exactly one of 'such_that', 'equals', or 'from_goal' must be given."); + throw new ScriptException( + "Exactly one of 'such_that', 'equals', or 'from_goal' must be given."); } obtainMap.put(var, skolem.op(JFunction.class)); @@ -85,11 +90,12 @@ private JTerm executeFromGoal(LocationVariable var) throws ScriptException { // This works under the assumption that the first succedent formula is the "goal" formula. SequentFormula sequentFormula = identifySequentFormula(goal.node()); JTerm formula = (JTerm) sequentFormula.formula(); - while(formula.op() instanceof UpdateApplication) { + while (formula.op() instanceof UpdateApplication) { formula = formula.sub(1); } - if(formula.op() != Quantifier.ALL) { - throw new ScriptException("For 'obtain \\from_goal, the goal formula needs to be a universally quantified formula."); + if (formula.op() != Quantifier.ALL) { + throw new ScriptException( + "For 'obtain \\from_goal, the goal formula needs to be a universally quantified formula."); } Services services = state().getProof().getServices(); @@ -98,48 +104,56 @@ private JTerm executeFromGoal(LocationVariable var) throws ScriptException { TacletApp app = NoPosTacletApp.createNoPosTacletApp(intro); SchemaVariable sk = getSV(app.uninstantiatedVars(), "sk"); - String name = VariableNameProposer.DEFAULT.getNameProposal(var.name().toString(), services, null); + String name = + VariableNameProposer.DEFAULT.getNameProposal(var.name().toString(), services, null); app = app.createSkolemConstant(name, sk, var.sort(), true, services); SchemaVariable b = getSV(app.uninstantiatedVars(), "b"); app = app.addCheckedInstantiation(b, formula.sub(0), services, true); SchemaVariable u = getSV(app.uninstantiatedVars(), "u"); - app = app.addCheckedInstantiation(u, services.getTermBuilder().var((LogicVariable) formula.boundVars().get(0)), services, true); - app = app.setPosInOccurrence(new PosInOccurrence(sequentFormula, PosInTerm.getTopLevel(), false), services); + app = app.addCheckedInstantiation(u, + services.getTermBuilder().var((LogicVariable) formula.boundVars().get(0)), services, + true); + app = app.setPosInOccurrence( + new PosInOccurrence(sequentFormula, PosInTerm.getTopLevel(), false), services); goal.apply(app); return app.instantiations().getInstantiation(sk); } private SequentFormula identifySequentFormula(Node node) { - SemisequentChangeInfo changes = node.getNodeInfo().getSequentChangeInfo().getSemisequentChangeInfo(false); + SemisequentChangeInfo changes = + node.getNodeInfo().getSequentChangeInfo().getSemisequentChangeInfo(false); ImmutableList added = changes.addedFormulas(); - if(!added.isEmpty()) { - if(added.size() == 1) { + if (!added.isEmpty()) { + if (added.size() == 1) { return added.get(0); } } else { ImmutableList modified = changes.modifiedFormulas(); - if(modified.size() == 1) { + if (modified.size() == 1) { return modified.get(0).newFormula(); } } - throw new IllegalStateException("Multiple or no formulas modified or added in last step, cannot identify sequent formula to skolemize."); + throw new IllegalStateException( + "Multiple or no formulas modified or added in last step, cannot identify sequent formula to skolemize."); } private JTerm executeSuchThat(LocationVariable var, @Nullable JTerm suchThat) { throw new UnsupportedOperationException("such_that not yet supported in obtain."); } - private JTerm executeEquals(LocationVariable var, @Nullable JTerm equals) throws ScriptException { + private JTerm executeEquals(LocationVariable var, @Nullable JTerm equals) + throws ScriptException { Services services = state().getProof().getServices(); Taclet intro = state.getProof().getEnv().getInitConfigForEnvironment() .lookupActiveTaclet(INTRO_TACLET_NAME); TacletApp app = NoPosTacletApp.createNoPosTacletApp(intro); SchemaVariable sk = getSV(app.uninstantiatedVars(), "sk"); SchemaVariable t = getSV(app.uninstantiatedVars(), "t"); - String name = VariableNameProposer.DEFAULT.getNameProposal(var.name().toString(), services, null); + String name = + VariableNameProposer.DEFAULT.getNameProposal(var.name().toString(), services, null); app = app.createSkolemConstant(name, sk, var.sort(), true, services); app = app.addCheckedInstantiation(t, equals, services, true); state.getFirstOpenAutomaticGoal().apply(app); @@ -148,7 +162,7 @@ private JTerm executeEquals(LocationVariable var, @Nullable JTerm equals) throws private static SchemaVariable getSV(ImmutableSet schemaVars, String name) { for (SchemaVariable schemaVar : schemaVars) { - if(schemaVar.name().toString().equals(name)) { + if (schemaVar.name().toString().equals(name)) { return schemaVar; } } @@ -158,7 +172,7 @@ private static SchemaVariable getSV(ImmutableSet schemaVars, Str @Documentation(category = "JML", value = """ Command that introduces a fresh variable with a given name and sort. Exactly one of `such_that`, `equals`, or `from_goal` must be given. - + The command should not be called directly, but is used internally by the JML script support within KeY. """) @@ -182,11 +196,15 @@ public static class Parameters implements ValueInjector.VerifyableParameters { @Override public void verifyParameters() throws IllegalArgumentException, InjectionException { int cnt = 0; - if(suchThat != null) cnt++; - if(equals != null) cnt++; - if(fromGoal) cnt++; - if(cnt != 1) { - throw new InjectionException("Exactly one of 'such_that', 'equals', or 'from_goal' must be given."); + if (suchThat != null) + cnt++; + if (equals != null) + cnt++; + if (fromGoal) + cnt++; + if (cnt != 1) { + throw new InjectionException( + "Exactly one of 'such_that', 'equals', or 'from_goal' must be given."); } } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java index ac2ff7b63f4..d824366c039 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/OneStepSimplifierCommand.java @@ -34,7 +34,7 @@ public void execute(ScriptCommandAst command) throws ScriptException, Interrupte final Goal goal = state.getFirstOpenAutomaticGoal(); - if(Boolean.TRUE.equals(arguments.recentOnly)) { + if (Boolean.TRUE.equals(arguments.recentOnly)) { SequentChangeInfo sci = goal.node().getNodeInfo().getSequentChangeInfo(); var ante = sci.addedFormulas(true) .prepend(sci.modifiedFormulas(true).map(FormulaChangeInfo::newFormula)); @@ -56,38 +56,42 @@ public void execute(ScriptCommandAst command) throws ScriptException, Interrupte } - private static void applyOSS(Iterable antecedent, Goal goal, boolean inAntec) { - for (SequentFormula sf : antecedent) { - ImmutableList builtins = goal.ruleAppIndex().getBuiltInRules(goal, - new PosInOccurrence(sf, PosInTerm.getTopLevel(), inAntec)); - for (IBuiltInRuleApp builtin : builtins) { - if (builtin instanceof OneStepSimplifierRuleApp) { - goal.apply(builtin); - } + private static void applyOSS(Iterable antecedent, Goal goal, boolean inAntec) { + for (SequentFormula sf : antecedent) { + ImmutableList builtins = goal.ruleAppIndex().getBuiltInRules(goal, + new PosInOccurrence(sf, PosInTerm.getTopLevel(), inAntec)); + for (IBuiltInRuleApp builtin : builtins) { + if (builtin instanceof OneStepSimplifierRuleApp) { + goal.apply(builtin); } } } + } - @Documentation(category = "Fundamental", value = """ - The oss command applies the *one step simplifier* on the current proof goal. - This simplifier applies a set of built-in simplification rules to the formulas in the sequent. - It can be configured to apply the one step simplifier only on the antecedent or succedent. - By default, it is applied on both sides of the sequent. - """) + @Documentation(category = "Fundamental", + value = """ + The oss command applies the *one step simplifier* on the current proof goal. + This simplifier applies a set of built-in simplification rules to the formulas in the sequent. + It can be configured to apply the one step simplifier only on the antecedent or succedent. + By default, it is applied on both sides of the sequent. + """) public static class Parameters { - @Documentation("Application of the one step simplifier can be forbidden on the antecedent side by setting " + - "this option to false. Default is true.") + @Documentation("Application of the one step simplifier can be forbidden on the antecedent side by setting " + + + "this option to false. Default is true.") @Option(value = "antecedent") public @Nullable Boolean antecedent = Boolean.TRUE; - @Documentation("Application of the one step simplifier can be forbidden on the succedent side by setting " + - "this option to false. Default is true.") + @Documentation("Application of the one step simplifier can be forbidden on the succedent side by setting " + + + "this option to false. Default is true.") @Option(value = "succedent") public @Nullable Boolean succedent = Boolean.TRUE; - @Documentation("Limit the application to the recently added or changed formulas. Deactivates the " + - "antecedent and succedent options.") + @Documentation("Limit the application to the recently added or changed formulas. Deactivates the " + + + "antecedent and succedent options.") @Flag("recentOnly") public @Nullable Boolean recentOnly = Boolean.FALSE; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java index 1fd2bceb78a..753d4ee56de 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java @@ -13,8 +13,6 @@ import java.util.stream.Collectors; import de.uka.ilkd.key.control.AbstractUserInterfaceControl; -import de.uka.ilkd.key.control.DefaultUserInterfaceControl; -import de.uka.ilkd.key.nparser.KeYParser; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.nparser.ParsingFacade; import de.uka.ilkd.key.parser.Location; @@ -22,7 +20,6 @@ import de.uka.ilkd.key.proof.Node; import de.uka.ilkd.key.proof.Proof; -import org.antlr.v4.runtime.RuleContext; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -94,7 +91,7 @@ public void execute(AbstractUserInterfaceControl uiControl, List 1) { // todo make a nice string here! throw new ScriptException("More than one applicable occurrence:\n" + - matchingApps.stream().map( - ap -> ap.posInOccurrence().subTerm() + " " + ap.matchConditions()) - .collect(Collectors.joining("\n") )); + matchingApps.stream().map( + ap -> ap.posInOccurrence().subTerm() + " " + ap.matchConditions()) + .collect(Collectors.joining("\n"))); } return matchingApps.get(0); } else { @@ -390,7 +390,7 @@ private List filterList(Services services, Parameters p, || userInst.equalsModProperty(ptaInst, IRRELEVANT_TERM_LABELS_PROPERTY); } - if(tacletApp.assumesFormulaInstantiations() != null) { + if (tacletApp.assumesFormulaInstantiations() != null) { add &= checkAssumes(p, tacletApp.assumesFormulaInstantiations(), services); } @@ -402,8 +402,9 @@ private List filterList(Services services, Parameters p, return matchingApps; } - private boolean checkAssumes(Parameters p, ImmutableList ifFormulaInstantiations, Services services) { - if(p.assumes == null) { + private boolean checkAssumes(Parameters p, + ImmutableList ifFormulaInstantiations, Services services) { + if (p.assumes == null) { // no "assumes" restrictions specified. return true; } @@ -412,24 +413,26 @@ private boolean checkAssumes(Parameters p, ImmutableList 0)` applies the cut rule on the formula `a > 0` like the cut command. - - `rule and_right on=(__ & __)` applies the rule `and_right` to the second occurrence - of a conjunction in the succedent. - - `rule my_rule on=(f(x)) formula="f\\(.*search.*\\)"` applies the rule `my_rule` to the term - `f(x)` in a formula matching the regular expression. - """) + @Documentation(category = "Fundamental", + value = """ + This command can be used to apply a calculus rule to the currently active open goal. + + #### Examples: + - `rule cut inst_cutFormula: (a > 0)` applies the cut rule on the formula `a > 0` like the cut command. + - `rule and_right on=(__ & __)` applies the rule `and_right` to the second occurrence + of a conjunction in the succedent. + - `rule my_rule on=(f(x)) formula="f\\(.*search.*\\)"` applies the rule `my_rule` to the term + `f(x)` in a formula matching the regular expression. + """) public static class Parameters { @Argument @Documentation("Name of the rule to be applied.") public @MonotonicNonNull String rulename; @Option(value = "on") - @Documentation("Term on which the rule should be applied to (matching the 'find' clause of the rule). " + - "This may contain placeholders.") + @Documentation("Term on which the rule should be applied to (matching the 'find' clause of the rule). " + + + "This may contain placeholders.") public @Nullable TermWithHoles on; @Option(value = "formula") @@ -437,23 +440,25 @@ public static class Parameters { public @Nullable JTerm formula; @Option(value = "occ") - @Documentation("Occurrence number if more than one occurrence matches. The first occurrence is 1. " + - "If ommitted, there must be exactly one occurrence.") + @Documentation("Occurrence number if more than one occurrence matches. The first occurrence is 1. " + + + "If ommitted, there must be exactly one occurrence.") public @Nullable Integer occ = -1; /** * Represents a part of a formula (may use Java regular expressions as long as supported by * proof script parser). Rule is applied to the sequent formula which matches that string. */ - @Documentation("Instead of giving the toplevl formula completely, a regular expression can be " + - "specified to match the toplevel formula.") + @Documentation("Instead of giving the toplevl formula completely, a regular expression can be " + + + "specified to match the toplevel formula.") @Option(value = "matches") public @Nullable String matches = null; - + @Option(value = "assumes") @Documentation(""" - If the rule has an `\\assumes` clause, this can be used to restrict the instantiations - """) + If the rule has an `\\assumes` clause, this can be used to restrict the instantiations + """) public @Nullable SequentWithHoles assumes; @OptionalVarargs(as = JTerm.class, prefix = "inst_") diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SMTCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SMTCommand.java index 3a2bd65efa9..1e6bebdbc48 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SMTCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SMTCommand.java @@ -115,13 +115,13 @@ private SolverTypeCollection computeSolvers(String value) throws ScriptException } @Documentation(category = "Fundamental", value = """ - The smt command invokes an SMT solver on the current goal(s). - By default, it uses the Z3 solver on the first open automatic goal. - If the option 'all' is given, it runs on all open goals. - If the option 'solver' is given, it uses the specified solver(s) instead of Z3. - Multiple solvers can be specified by separating their names with commas. - The available solvers depend on your system: KeY supports at least z3, cvc5. - """) + The smt command invokes an SMT solver on the current goal(s). + By default, it uses the Z3 solver on the first open automatic goal. + If the option 'all' is given, it runs on all open goals. + If the option 'solver' is given, it uses the specified solver(s) instead of Z3. + Multiple solvers can be specified by separating their names with commas. + The available solvers depend on your system: KeY supports at least z3, cvc5. + """) public static class SMTCommandArguments { @Option("solver") public String solver = "Z3"; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveInstCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveInstCommand.java index 2b39adbfaea..72cf316bd6f 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveInstCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveInstCommand.java @@ -8,8 +8,8 @@ import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.pp.AbbrevMap; import de.uka.ilkd.key.rule.TacletApp; - import de.uka.ilkd.key.scripts.meta.Documentation; + import org.key_project.logic.Name; import org.key_project.logic.op.Function; import org.key_project.logic.op.sv.SchemaVariable; @@ -25,11 +25,12 @@ * * @author Dominic Steinhoefel */ -@Documentation(category = "Internal", value = """ - Saves the instantiation of a schema variable by the last taclet application into an abbreviation for later use. - A nice use case is a manual loop invariant rule application, where the newly introduced anonymizing Skolem constants can be saved for later interactive instantiations. - As for the let command, it is not allowed to call this command multiple times with the same name argument (all names used for remembering instantiations are "final"). - """) +@Documentation(category = "Internal", + value = """ + Saves the instantiation of a schema variable by the last taclet application into an abbreviation for later use. + A nice use case is a manual loop invariant rule application, where the newly introduced anonymizing Skolem constants can be saved for later interactive instantiations. + As for the let command, it is not allowed to call this command multiple times with the same name argument (all names used for remembering instantiations are "final"). + """) public class SaveInstCommand extends AbstractCommand { public SaveInstCommand() { super(null); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveNewNameCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveNewNameCommand.java index 42d0c818376..a3e233e0351 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveNewNameCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SaveNewNameCommand.java @@ -89,14 +89,15 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup } } - @Documentation(category = "Internal", value = """ - Special "Let" usually to be applied immediately after a manual rule application. Saves a new name - introduced by the last rule which matches certain criteria into an abbreviation for - later use. A nice use case is a manual loop invariant rule application, where the newly - introduced anonymizing Skolem constants can be saved for later interactive instantiations. As for - the let command, it is not allowed to call this command multiple times with the same name - argument (all names used for remembering instantiations are "final"). - """) + @Documentation(category = "Internal", + value = """ + Special "Let" usually to be applied immediately after a manual rule application. Saves a new name + introduced by the last rule which matches certain criteria into an abbreviation for + later use. A nice use case is a manual loop invariant rule application, where the newly + introduced anonymizing Skolem constants can be saved for later interactive instantiations. As for + the let command, it is not allowed to call this command multiple times with the same name + argument (all names used for remembering instantiations are "final"). + """) public static class Parameters { @Documentation("The abbreviation to store the new name under, must start with @") @Argument diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SchemaVarCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SchemaVarCommand.java index 06ac00c5099..a953ff806f7 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SchemaVarCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SchemaVarCommand.java @@ -9,8 +9,8 @@ import de.uka.ilkd.key.logic.op.SchemaVariableFactory; import de.uka.ilkd.key.pp.AbbrevMap; import de.uka.ilkd.key.scripts.meta.Argument; - import de.uka.ilkd.key.scripts.meta.Documentation; + import org.key_project.logic.Name; import org.key_project.logic.sort.Sort; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommand.java index 978edf073a2..a8fa2ba7093 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommand.java @@ -8,8 +8,8 @@ import java.nio.file.Path; import de.uka.ilkd.key.scripts.meta.Argument; - import de.uka.ilkd.key.scripts.meta.Documentation; + import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommandAst.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommandAst.java index 4fe7bfc0f01..8a2de058e0e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommandAst.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommandAst.java @@ -10,8 +10,8 @@ import de.uka.ilkd.key.nparser.KeYParser; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.parser.Location; - import de.uka.ilkd.key.util.ANTLRUtil; + import org.antlr.v4.runtime.ParserRuleContext; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SelectCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SelectCommand.java index 71bb5a35d40..2949f3f2e7d 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SelectCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SelectCommand.java @@ -15,8 +15,8 @@ import de.uka.ilkd.key.scripts.meta.Documentation; import de.uka.ilkd.key.scripts.meta.InjectionException; import de.uka.ilkd.key.scripts.meta.Option; - import de.uka.ilkd.key.scripts.meta.ValueInjector; + import org.key_project.logic.Term; import org.key_project.prover.sequent.Semisequent; import org.key_project.prover.sequent.Sequent; @@ -165,8 +165,9 @@ public String getName() { """) public static class Parameters implements ValueInjector.VerifyableParameters { /** A formula defining the goal to select */ - @Documentation("A formula defining the goal to select. May contain placeholder symbols. If there is a formula " + - "matching the given formula in multiple goals, the first one is selected.") + @Documentation("A formula defining the goal to select. May contain placeholder symbols. If there is a formula " + + + "matching the given formula in multiple goals, the first one is selected.") @Option(value = "formula") public @Nullable JTerm formula; @@ -174,23 +175,28 @@ public static class Parameters implements ValueInjector.VerifyableParameters { * The number of the goal to select, starts with 0. Negative indices are also allowed: -1 is * the last goal, -2 the second-to-last, etc. */ - @Documentation("The number of the goal to select, starts with 0. Negative indices are also allowed: -1 is " + - "the last goal, -2 the second-to-last, etc.") + @Documentation("The number of the goal to select, starts with 0. Negative indices are also allowed: -1 is " + + + "the last goal, -2 the second-to-last, etc.") @Option(value = "number") public @Nullable Integer number; /** The name of the branch to select */ - @Documentation("The name of the branch to select. If there are multiple branches with the same name, " + - "the first one is selected.") + @Documentation("The name of the branch to select. If there are multiple branches with the same name, " + + + "the first one is selected.") @Option(value = "branch") public @Nullable String branch; @Override public void verifyParameters() throws IllegalArgumentException, InjectionException { int cnt = 0; - if (formula != null) cnt++; - if (number != null) cnt++; - if (branch != null) cnt++; + if (formula != null) + cnt++; + if (number != null) + cnt++; + if (branch != null) + cnt++; if (cnt != 1) { throw new InjectionException( "Exactly one of 'formula', 'branch' or 'number' are required"); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SequentWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SequentWithHoles.java index 6b67ab25ab2..07dfe1c6113 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SequentWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SequentWithHoles.java @@ -3,36 +3,22 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.scripts; -import de.uka.ilkd.key.java.Services; -import de.uka.ilkd.key.ldt.JavaDLTheory; -import de.uka.ilkd.key.logic.JTerm; -import de.uka.ilkd.key.logic.NamespaceSet; -import de.uka.ilkd.key.logic.op.JFunction; -import de.uka.ilkd.key.logic.op.SortDependingFunction; -import de.uka.ilkd.key.logic.sort.GenericSort; +import java.util.List; + import de.uka.ilkd.key.nparser.KeYParser; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.nparser.ParsingFacade; -import de.uka.ilkd.key.nparser.builder.ExpressionBuilder; -import de.uka.ilkd.key.util.parsing.BuildingIssue; -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.ParserRuleContext; -import org.jspecify.annotations.NullMarked; -import org.key_project.logic.Name; -import org.key_project.logic.PosInTerm; -import org.key_project.logic.sort.AbstractSort; -import org.key_project.logic.sort.Sort; + import org.key_project.prover.rules.instantiation.AssumesFormulaInstantiation; -import org.key_project.prover.sequent.PosInOccurrence; -import org.key_project.prover.sequent.Sequent; import org.key_project.prover.sequent.SequentFormula; import org.key_project.util.collection.ImmutableList; -import org.key_project.util.collection.ImmutableSet; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.ParserRuleContext; +import org.jspecify.annotations.NullMarked; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.List; - @NullMarked public class SequentWithHoles { @@ -54,21 +40,21 @@ public static SequentWithHoles fromString(EngineState engineState, String str) { public static SequentWithHoles fromParserContext(EngineState state, ParserRuleContext ctx) { - if(ctx instanceof KeYParser.ProofScriptExpressionContext psctx) { + if (ctx instanceof KeYParser.ProofScriptExpressionContext psctx) { ctx = psctx.seq(); } - if(ctx instanceof KeYParser.SeqContext seqCtx) { + if (ctx instanceof KeYParser.SeqContext seqCtx) { List antecedent = new java.util.ArrayList<>(); KeYParser.SemisequentContext semseq = seqCtx.ant; - while(semseq != null && semseq.term() != null) { + while (semseq != null && semseq.term() != null) { antecedent.add(TermWithHoles.fromParserContext(state, semseq.term())); semseq = semseq.semisequent(); } List succedent = new java.util.ArrayList<>(); semseq = seqCtx.suc; - while(semseq != null && semseq.term() != null) { + while (semseq != null && semseq.term() != null) { succedent.add(TermWithHoles.fromParserContext(state, semseq.term())); semseq = semseq.semisequent(); } @@ -99,4 +85,4 @@ public boolean containsAntecendent(SequentFormula seqFormula) { public boolean containsSuccedent(SequentFormula seqFormula) { return succedent.stream().anyMatch(f -> f.matchesToplevel(seqFormula)); } -} \ No newline at end of file +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetEchoCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetEchoCommand.java index ff84be60a72..bd71fef7408 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetEchoCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetEchoCommand.java @@ -5,8 +5,8 @@ import de.uka.ilkd.key.scripts.meta.Argument; - import de.uka.ilkd.key.scripts.meta.Documentation; + import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetFailOnClosedCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetFailOnClosedCommand.java index 3ca852901ea..54c7a48d969 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetFailOnClosedCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetFailOnClosedCommand.java @@ -5,8 +5,8 @@ import de.uka.ilkd.key.scripts.meta.Argument; - import de.uka.ilkd.key.scripts.meta.Documentation; + import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java index 984c44bf9f1..8f96dfa50b1 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermComparisonWithHoles.java @@ -1,16 +1,19 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.scripts; +import java.util.*; + import de.uka.ilkd.key.java.NameAbstractionTable; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.equality.RenamingTermProperty; import de.uka.ilkd.key.logic.op.*; -import org.jspecify.annotations.NullMarked; -import org.jspecify.annotations.Nullable; + import org.key_project.logic.PosInTerm; import org.key_project.logic.Term; import org.key_project.logic.op.Operator; import org.key_project.logic.op.QuantifiableVariable; -import org.key_project.prover.rules.instantiation.AssumesFormulaInstantiation; import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.prover.sequent.Sequent; import org.key_project.prover.sequent.SequentFormula; @@ -18,14 +21,16 @@ import org.key_project.util.collection.ImmutableSLList; import org.key_project.util.collection.Pair; -import java.util.*; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import static de.uka.ilkd.key.scripts.TermWithHoles.*; /** * A property that can be used for comparisons for terms. * All term labels are ignored in this equality check. Additionally, holes (represented by the - * SortDependingFunction with name "_" and the Predicate with name "__") are treated as wildcards that + * SortDependingFunction with name "_" and the Predicate with name "__") are treated as wildcards + * that * match any subterm. * * @author Mattias Ulbrich @@ -46,21 +51,21 @@ public final boolean matches(PosInOccurrence pio) { } PosInTerm focus = findFocus(referenceTerm); - if(focus == null) { + if (focus == null) { focus = PosInTerm.getTopLevel(); } List focusPaths = new ArrayList<>(); expandFocusPaths(focus, pio.posInTerm(), PosInTerm.getTopLevel(), focusPaths); - for(PosInTerm fpath : focusPaths) { + for (PosInTerm fpath : focusPaths) { PosInTerm pit = pio.posInTerm().firstN(pio.depth() - fpath.depth()); JTerm startTerm = (JTerm) pit.getSubTerm(pio.sequentFormula().formula()); boolean result = unifyHelp(referenceTerm, startTerm, - ImmutableSLList.nil(), - ImmutableSLList.nil(), - fpath); + ImmutableSLList.nil(), + ImmutableSLList.nil(), + fpath); if (result) { return true; } @@ -69,25 +74,27 @@ public final boolean matches(PosInOccurrence pio) { return false; } - private void expandFocusPaths(PosInTerm focus, PosInTerm input, PosInTerm base, List collected) { - if(focus.isTopLevel()) { + private void expandFocusPaths(PosInTerm focus, PosInTerm input, PosInTerm base, + List collected) { + if (focus.isTopLevel()) { // fully matched: collected.add(base); return; } - if(input.isTopLevel()) { + if (input.isTopLevel()) { // input is too shallow return; } - if(focus.getIndex() == (char)-1) { + if (focus.getIndex() == (char) -1) { // ellipsis found, we need to expand expandFocusPaths(focus.up(), input, base, collected); expandFocusPaths(focus, input.up(), base.prepend((char) input.getIndex()), collected); } else { - if(focus.getIndex() == input.getIndex()) { - expandFocusPaths(focus.up(), input.up(), base.prepend((char) input.getIndex()), collected); + if (focus.getIndex() == input.getIndex()) { + expandFocusPaths(focus.up(), input.up(), base.prepend((char) input.getIndex()), + collected); } else { // mismatch return; @@ -103,13 +110,13 @@ public final boolean matchesToplevel(SequentFormula sf) { private static @Nullable PosInTerm findFocus(Term pattern) { var op = pattern.op(); if (op instanceof JFunction) { - if(op.name().equals(TermWithHoles.FOCUS_NAME)) { + if (op.name().equals(TermWithHoles.FOCUS_NAME)) { return PosInTerm.getTopLevel(); } - if(op.name().equals(TermWithHoles.ELLIPSIS_NAME)) { + if (op.name().equals(TermWithHoles.ELLIPSIS_NAME)) { PosInTerm subFocus = findFocus(pattern.sub(0)); - if(subFocus != null) { - return subFocus.prepend((char)-1); + if (subFocus != null) { + return subFocus.prepend((char) -1); } else { return null; } @@ -118,8 +125,8 @@ public final boolean matchesToplevel(SequentFormula sf) { for (int i = 0; i < pattern.arity(); i++) { Term sub = pattern.sub(i); PosInTerm subFocus = findFocus(sub); - if(subFocus != null) { - return subFocus.prepend((char)i); + if (subFocus != null) { + return subFocus.prepend((char) i); } } return null; @@ -129,41 +136,42 @@ public final boolean matchesToplevel(SequentFormula sf) { /** * Compares two terms modulo bound renaming * - * @param t0 the first term -- potentially containing holes - * @param t1 the second term + * @param t0 the first term -- potentially containing holes + * @param t1 the second term * @param ownBoundVars variables bound above the current position * @param cmpBoundVars variables bound above the current position * @return true is returned iff the terms are equal modulo - * bound renaming + * bound renaming */ private static boolean unifyHelp(JTerm t0, JTerm t1, - ImmutableList ownBoundVars, - ImmutableList cmpBoundVars, - @Nullable PosInTerm expectedFocus) { + ImmutableList ownBoundVars, + ImmutableList cmpBoundVars, + @Nullable PosInTerm expectedFocus) { if (t0 == t1 && ownBoundVars.equals(cmpBoundVars)) { return true; } Operator op = t0.op(); - if(op instanceof SortDependingFunction sdop) { - if(sdop.getKind().equals(HOLE_SORT_DEP_NAME)) { + if (op instanceof SortDependingFunction sdop) { + if (sdop.getKind().equals(HOLE_SORT_DEP_NAME)) { return true; } - } else if(op.name().equals(HOLE_PREDICATE_NAME) || op.name().equals(HOLE_NAME)) { + } else if (op.name().equals(HOLE_PREDICATE_NAME) || op.name().equals(HOLE_NAME)) { return true; - } else if(op.name().equals(FOCUS_NAME)) { - if(expectedFocus == null || !expectedFocus.isTopLevel()) { + } else if (op.name().equals(FOCUS_NAME)) { + if (expectedFocus == null || !expectedFocus.isTopLevel()) { // focus annotation not at expected position return false; } return unifyHelp(t0.sub(0), t1, ownBoundVars, cmpBoundVars, null); - } else if(op.name().equals(ELLIPSIS_NAME)) { + } else if (op.name().equals(ELLIPSIS_NAME)) { // return true if it hits one subterm ... Set> deepAllSubs = new HashSet<>(); computeSubterms(t1, expectedFocus, deepAllSubs); var lookfor = t0.sub(0); - return deepAllSubs.stream().anyMatch(t -> unifyHelp(lookfor, t.first, ownBoundVars, cmpBoundVars, t.second)); + return deepAllSubs.stream().anyMatch( + t -> unifyHelp(lookfor, t.first, ownBoundVars, cmpBoundVars, t.second)); } @@ -171,7 +179,7 @@ private static boolean unifyHelp(JTerm t0, JTerm t1, if (op0 instanceof QuantifiableVariable) { return handleQuantifiableVariable(t0, t1, ownBoundVars, - cmpBoundVars); + cmpBoundVars); } final Operator op1 = t1.op(); @@ -180,27 +188,28 @@ private static boolean unifyHelp(JTerm t0, JTerm t1, return false; } -// nat = handleJava(t0, t1, nat); -// if (nat == FAILED) { -// return false; -// } + // nat = handleJava(t0, t1, nat); + // if (nat == FAILED) { + // return false; + // } return descendRecursively(t0, t1, ownBoundVars, cmpBoundVars, expectedFocus); } - private static void computeSubterms(JTerm t, @Nullable PosInTerm expectedPos, Set> deepAllSubs) { + private static void computeSubterms(JTerm t, @Nullable PosInTerm expectedPos, + Set> deepAllSubs) { deepAllSubs.add(new Pair<>(t, expectedPos)); - for(int i = 0; i < t.arity(); i++) { + for (int i = 0; i < t.arity(); i++) { computeSubterms(t.sub(i), nextFocusPos(expectedPos, i), deepAllSubs); } } private static boolean handleQuantifiableVariable(JTerm t0, JTerm t1, - ImmutableList ownBoundVars, - ImmutableList cmpBoundVars) { + ImmutableList ownBoundVars, + ImmutableList cmpBoundVars) { if (!((t1.op() instanceof QuantifiableVariable) && compareBoundVariables( - (QuantifiableVariable) t0.op(), (QuantifiableVariable) t1.op(), - ownBoundVars, cmpBoundVars))) { + (QuantifiableVariable) t0.op(), (QuantifiableVariable) t1.op(), + ownBoundVars, cmpBoundVars))) { return false; } return true; @@ -209,15 +218,15 @@ private static boolean handleQuantifiableVariable(JTerm t0, JTerm t1, /** * compare two quantifiable variables if they are equal modulo renaming * - * @param ownVar first QuantifiableVariable to be compared - * @param cmpVar second QuantifiableVariable to be compared + * @param ownVar first QuantifiableVariable to be compared + * @param cmpVar second QuantifiableVariable to be compared * @param ownBoundVars variables bound above the current position * @param cmpBoundVars variables bound above the current position */ private static boolean compareBoundVariables(QuantifiableVariable ownVar, - QuantifiableVariable cmpVar, - ImmutableList ownBoundVars, - ImmutableList cmpBoundVars) { + QuantifiableVariable cmpVar, + ImmutableList ownBoundVars, + ImmutableList cmpBoundVars) { final int ownNum = indexOf(ownVar, ownBoundVars); final int cmpNum = indexOf(cmpVar, cmpBoundVars); @@ -234,7 +243,7 @@ private static boolean compareBoundVariables(QuantifiableVariable ownVar, } private static int indexOf(QuantifiableVariable var, - ImmutableList list) { + ImmutableList list) { int res = 0; while (!list.isEmpty()) { if (list.head() == var) { @@ -255,9 +264,9 @@ private static NameAbstractionTable checkNat(NameAbstractionTable nat) { } private static boolean descendRecursively(JTerm t0, JTerm t1, - ImmutableList ownBoundVars, - ImmutableList cmpBoundVars, - PosInTerm expectedFocus) { + ImmutableList ownBoundVars, + ImmutableList cmpBoundVars, + PosInTerm expectedFocus) { for (int i = 0; i < t0.arity(); i++) { ImmutableList subOwnBoundVars = ownBoundVars; @@ -280,7 +289,7 @@ private static boolean descendRecursively(JTerm t0, JTerm t1, PosInTerm nextFocus = nextFocusPos(expectedFocus, i); boolean newConstraint = unifyHelp(t0.sub(i), t1.sub(i), - subOwnBoundVars, subCmpBoundVars, nextFocus); + subOwnBoundVars, subCmpBoundVars, nextFocus); if (!newConstraint) { return false; @@ -291,7 +300,8 @@ private static boolean descendRecursively(JTerm t0, JTerm t1, } private static @Nullable PosInTerm nextFocusPos(PosInTerm expectedFocus, int i) { - if(expectedFocus != null && !expectedFocus.isTopLevel() && expectedFocus.getIndexAt(0) == i) { + if (expectedFocus != null && !expectedFocus.isTopLevel() + && expectedFocus.getIndexAt(0) == i) { // we are on the path to the focus return expectedFocus.lastN(expectedFocus.depth() - 1); } else { @@ -315,7 +325,8 @@ public List> findTopLevelMatchesInSequent(Sequent } - public @Nullable Pair findUniqueToplevelMatchInSequent(Sequent sequent) { + public @Nullable Pair findUniqueToplevelMatchInSequent( + Sequent sequent) { List> matches = findTopLevelMatchesInSequent(sequent); if (matches.size() != 1) { return null; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java index c52e1115c6d..2222ebebb84 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java @@ -3,7 +3,9 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.scripts; -import de.uka.ilkd.key.control.AbstractProofControl; +import java.util.List; +import java.util.Objects; + import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.ldt.JavaDLTheory; import de.uka.ilkd.key.logic.JTerm; @@ -12,46 +14,31 @@ import de.uka.ilkd.key.logic.op.SortDependingFunction; import de.uka.ilkd.key.logic.sort.GenericSort; import de.uka.ilkd.key.nparser.KeYParser; -import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.nparser.ParsingFacade; import de.uka.ilkd.key.nparser.builder.ExpressionBuilder; -import de.uka.ilkd.key.proof.Goal; -import de.uka.ilkd.key.proof.Proof; -import de.uka.ilkd.key.proof.init.Profile; -import de.uka.ilkd.key.prover.impl.ApplyStrategy; import de.uka.ilkd.key.scripts.meta.*; -import de.uka.ilkd.key.strategy.FocussedBreakpointRuleApplicationManager; -import de.uka.ilkd.key.strategy.Strategy; -import de.uka.ilkd.key.strategy.StrategyProperties; import de.uka.ilkd.key.util.parsing.BuildingIssue; -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.tree.ParseTree; -import org.jspecify.annotations.NonNull; -import org.jspecify.annotations.NullMarked; -import org.jspecify.annotations.Nullable; + import org.key_project.logic.Name; import org.key_project.logic.sort.AbstractSort; import org.key_project.logic.sort.Sort; -import org.key_project.prover.engine.ProverCore; import org.key_project.prover.sequent.PosInOccurrence; import org.key_project.prover.sequent.SequentFormula; -import org.key_project.prover.strategy.RuleApplicationManager; -import org.key_project.util.collection.ImmutableArray; -import org.key_project.util.collection.ImmutableList; -import org.key_project.util.collection.ImmutableSLList; import org.key_project.util.collection.ImmutableSet; import org.key_project.util.java.StringUtil; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ParseTree; +import org.jspecify.annotations.NullMarked; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.List; -import java.util.Objects; - @NullMarked public class TermWithHoles { private final JTerm term; + public TermWithHoles(JTerm term) { this.term = Objects.requireNonNull(term); } @@ -103,13 +90,14 @@ public static TermWithHoles fromString(EngineState engineState, String str) { return fromParserContext(engineState, term); } - public static TermWithHoles fromProofScriptExpression(EngineState engineState, KeYParser.ProofScriptExpressionContext ctx) throws ConversionException { - if(ctx.string_literal() != null) { + public static TermWithHoles fromProofScriptExpression(EngineState engineState, + KeYParser.ProofScriptExpressionContext ctx) throws ConversionException { + if (ctx.string_literal() != null) { String text = StringUtil.stripQuotes(ctx.string_literal().getText()); return fromString(engineState, text); - } else if(ctx.proofScriptCodeBlock() != null) { + } else if (ctx.proofScriptCodeBlock() != null) { throw new ConversionException("A block cannot be used as a term"); - } else if(ctx.seq() != null) { + } else if (ctx.seq() != null) { throw new ConversionException("A sequent cannot be used as a term"); } else { return fromParserContext(engineState, ctx.getRuleContext(ParserRuleContext.class, 0)); @@ -118,7 +106,7 @@ public static TermWithHoles fromProofScriptExpression(EngineState engineState, K public static TermWithHoles fromParserContext(EngineState state, ParseTree ctx) { var expressionBuilder = - new ExpressionBuilder(state.getProof().getServices(), enrichState(state)); + new ExpressionBuilder(state.getProof().getServices(), enrichState(state)); expressionBuilder.setAbbrevMap(state.getAbbreviations()); JTerm t = (JTerm) ctx.accept(expressionBuilder); List warnings = expressionBuilder.getBuildingIssues(); @@ -140,7 +128,7 @@ private static NamespaceSet enrichState(EngineState state) { ns.functions().addSafely(new JFunction(ELLIPSIS_NAME, nothing, JavaDLTheory.ANY)); GenericSort g = new GenericSort(new Name("G")); ns.functions().addSafely(SortDependingFunction.createFirstInstance(g, HOLE_SORT_DEP_NAME, g, - new Sort[0], false)); + new Sort[0], false)); return ns; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/UnhideCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/UnhideCommand.java index c4d902aa13a..a8a7eb07aa0 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/UnhideCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/UnhideCommand.java @@ -10,8 +10,8 @@ import de.uka.ilkd.key.proof.RuleAppIndex; import de.uka.ilkd.key.rule.NoPosTacletApp; import de.uka.ilkd.key.scripts.meta.Argument; - import de.uka.ilkd.key.scripts.meta.Documentation; + import org.key_project.logic.Term; import org.key_project.logic.op.sv.SchemaVariable; import org.key_project.prover.proof.rulefilter.TacletFilter; @@ -84,10 +84,11 @@ public String getName() { return "unhide"; } - @Documentation(category = "Control", value = """ - The unhide command re-inserts formulas that have been hidden earlier in the proof using the hide command. - It takes a sequent as parameter and re-inserts all formulas in this sequent that have been hidden earlier. - """) + @Documentation(category = "Control", + value = """ + The unhide command re-inserts formulas that have been hidden earlier in the proof using the hide command. + It takes a sequent as parameter and re-inserts all formulas in this sequent that have been hidden earlier. + """) public static class Parameters { @Documentation("The sequent containing the formulas to be re-inserted. Placeholders are allowed.") @Argument diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java index c5d7f94721e..119d40e49f2 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/WitnessCommand.java @@ -1,9 +1,12 @@ /* This file is part of KeY - https://key-project.org * KeY is licensed under the GNU General Public License Version 2 * SPDX-License-Identifier: GPL-2.0-only */ - package de.uka.ilkd.key.scripts; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.regex.Pattern; + import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.*; import de.uka.ilkd.key.logic.op.Quantifier; @@ -12,7 +15,7 @@ import de.uka.ilkd.key.scripts.meta.Argument; import de.uka.ilkd.key.scripts.meta.Documentation; import de.uka.ilkd.key.scripts.meta.Option; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; + import org.key_project.logic.Name; import org.key_project.logic.PosInTerm; import org.key_project.logic.op.Operator; @@ -21,9 +24,7 @@ import org.key_project.prover.sequent.SequentFormula; import org.key_project.util.collection.Pair; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.regex.Pattern; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * witness "\exists int x; phi(x)" as="x_12" @@ -62,7 +63,8 @@ public void execute(ScriptCommandAst ast) throws ScriptException, InterruptedExc TermComparisonWithHoles comp = params.formula.getMatcher(); // First component: true for antecedent, false for succedent - Pair match = comp.findUniqueToplevelMatchInSequent(goal.node().sequent()); + Pair match = + comp.findUniqueToplevelMatchInSequent(goal.node().sequent()); if (match == null) { throw new ScriptException("Cannot unique match the formula argument"); } @@ -73,31 +75,34 @@ public void execute(ScriptCommandAst ast) throws ScriptException, InterruptedExc throw new ScriptException("Expected quantifier " + expected + ", but got " + op); } - if(!GOOD_NAME.matcher(params.as).matches()) { + if (!GOOD_NAME.matcher(params.as).matches()) { throw new ScriptException("Invalid name: " + params.as); } NamespaceSet nss = services.getNamespaces(); Name asName = new Name(params.as); - if(nss.functions().lookup(asName) != null) { + if (nss.functions().lookup(asName) != null) { throw new ScriptException("Name already used as function or predicate: " + params.as); } - if(nss.programVariables().lookup(asName) != null) { + if (nss.programVariables().lookup(asName) != null) { throw new ScriptException("Name already used as program variable: " + params.as); } Name tacletName = match.first ? ANTEC_TACLET : SUCC_TACLET; FindTaclet taclet = (FindTaclet) state.getProof().getEnv().getInitConfigForEnvironment() .lookupActiveTaclet(tacletName); - PosInOccurrence pio = new PosInOccurrence(match.second, PosInTerm.getTopLevel(), match.first); + PosInOccurrence pio = + new PosInOccurrence(match.second, PosInTerm.getTopLevel(), match.first); MatchConditions mc = new MatchConditions(); TacletApp app = PosTacletApp.createPosTacletApp(taclet, mc, pio, services); Set schemaVars = taclet.collectSchemaVars(); app = app.addInstantiation(getSV(schemaVars, "u"), - services.getTermBuilder().tf().createTerm(match.second.formula().boundVars().get(0)), - true, services); - app = app.addInstantiation(getSV(schemaVars, "b"), match.second.formula().sub(0), true, services); - app = app.createSkolemConstant(params.as, getSV(schemaVars, "sk"), match.second.formula().boundVars().get(0).sort(), true, services); + services.getTermBuilder().tf().createTerm(match.second.formula().boundVars().get(0)), + true, services); + app = app.addInstantiation(getSV(schemaVars, "b"), match.second.formula().sub(0), true, + services); + app = app.createSkolemConstant(params.as, getSV(schemaVars, "sk"), + match.second.formula().boundVars().get(0).sort(), true, services); Goal g = state.getFirstOpenAutomaticGoal(); g.apply(app); @@ -105,26 +110,27 @@ public void execute(ScriptCommandAst ast) throws ScriptException, InterruptedExc private static SchemaVariable getSV(Set schemaVars, String name) { for (SchemaVariable schemaVar : schemaVars) { - if(schemaVar.name().toString().equals(name)) { + if (schemaVar.name().toString().equals(name)) { return schemaVar; } } throw new NoSuchElementException("No schema variable with name " + name); } - @Documentation(category = "Fundamental", value = """ - Provides a witness symbol for an existential or universal quantifier. - The given formula must be present on the sequent. Placeholders are allowed. - The command fails if the formula cannot be uniquely matched on the sequent. - The witness symbol `as` must be a valid identifier and not already used as function, predicate, or - program variable name. The new function symbol is created as a Skolem constant. - - #### Example: - - If the sequent contains the formula `\\exists int x; x > 0` in the antecedent then the command - `witness "\\exists int x; x > 0" as="x_12"` will introduce the witness symbol `x_12` for which "x_12 > 0` - holds and is added to the antecedent. - """) + @Documentation(category = "Fundamental", + value = """ + Provides a witness symbol for an existential or universal quantifier. + The given formula must be present on the sequent. Placeholders are allowed. + The command fails if the formula cannot be uniquely matched on the sequent. + The witness symbol `as` must be a valid identifier and not already used as function, predicate, or + program variable name. The new function symbol is created as a Skolem constant. + + #### Example: + + If the sequent contains the formula `\\exists int x; x > 0` in the antecedent then the command + `witness "\\exists int x; x > 0" as="x_12"` will introduce the witness symbol `x_12` for which "x_12 > 0` + holds and is added to the antecedent. + """) public static class Parameters { @Documentation("The name of the witness symbol to be created.") @Option(value = "as") diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ArgumentsLifter.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ArgumentsLifter.java index e37627457ff..c3fbddf4505 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ArgumentsLifter.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ArgumentsLifter.java @@ -7,8 +7,8 @@ import java.lang.reflect.Modifier; import java.util.*; -import de.uka.ilkd.key.scripts.AbstractCommand; import de.uka.ilkd.key.scripts.ProofScriptCommand; + import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; @@ -43,11 +43,12 @@ public static String generateCommandUsage(String commandName, Class parameter var sb = new StringBuilder(commandName); for (var meta : args) { sb.append(' '); - if(!meta.isRequired() || meta.isFlag()) + if (!meta.isRequired() || meta.isFlag()) sb.append("["); if (meta.isPositional()) { - sb.append(OPEN_BRACKET + meta.getType().getSimpleName() + " (" + meta.getName() + ")" + CLOSE_BRACKET); + sb.append(OPEN_BRACKET + meta.getType().getSimpleName() + " (" + meta.getName() + + ")" + CLOSE_BRACKET); } if (meta.isOption()) { @@ -67,8 +68,8 @@ public static String generateCommandUsage(String commandName, Class parameter if (meta.isOptionalVarArgs()) { sb.append("%s...".formatted(meta.getName())); } - - if(!meta.isRequired() || meta.isFlag()) + + if (!meta.isRequired() || meta.isFlag()) sb.append("]"); } @@ -81,8 +82,9 @@ public static String extractDocumentation(String command, Class commandClazz, StringBuilder sb = new StringBuilder(); Deprecated dep = commandClazz.getAnnotation(Deprecated.class); - if(dep != null) { - sb.append("**Caution! This proof script command is deprecated, and may be removed soon!**\n\n"); + if (dep != null) { + sb.append( + "**Caution! This proof script command is deprecated, and may be removed soon!**\n\n"); } Documentation docCommand = commandClazz.getAnnotation(Documentation.class); @@ -91,7 +93,7 @@ public static String extractDocumentation(String command, Class commandClazz, sb.append("\n\n"); } - if(parameterClazz == null) { + if (parameterClazz == null) { return sb.toString(); } @@ -164,13 +166,14 @@ private static String ordinalStr(int post) { }; } - public static String extractCategory(Class commandClazz, @Nullable Class parameterClazz) { + public static String extractCategory(Class commandClazz, + @Nullable Class parameterClazz) { Documentation docCommand = commandClazz.getAnnotation(Documentation.class); if (docCommand != null && !docCommand.category().isBlank()) { return docCommand.category(); } - if(parameterClazz != null) { + if (parameterClazz != null) { Documentation docAn = parameterClazz.getAnnotation(Documentation.class); if (docAn != null && !docAn.category().isBlank()) { return docAn.category(); @@ -183,36 +186,38 @@ public static String extractCategory(Class command private static @NonNull List getSortedProofScriptArguments( Class parameterClazz) { -// Comparator optional = -// Comparator.comparing(ProofScriptArgument::isOption); -// Comparator positional = -// Comparator.comparing(ProofScriptArgument::isPositional); -// Comparator flagal = Comparator.comparing(ProofScriptArgument::isFlag); -// Comparator allargsal = -// Comparator.comparing(ProofScriptArgument::isPositionalVarArgs); -// Comparator byRequired = -// Comparator.comparing(ProofScriptArgument::isRequired); -// Comparator byName = Comparator.comparing(ProofScriptArgument::getName); -// -// Comparator byPos = Comparator.comparing(it -> { -// if (it.isPositionalVarArgs()) { -// it.getPositionalVarargs().startIndex(); -// } -// if (it.isPositional()) { -// it.getArgument().value(); -// } -// -// return -1; -// }); -// -// -// var comp = optional -// .thenComparing(flagal) -// .thenComparing(positional) -// .thenComparing(allargsal) -// .thenComparing(byRequired) -// .thenComparing(byPos) -// .thenComparing(byName); + // Comparator optional = + // Comparator.comparing(ProofScriptArgument::isOption); + // Comparator positional = + // Comparator.comparing(ProofScriptArgument::isPositional); + // Comparator flagal = + // Comparator.comparing(ProofScriptArgument::isFlag); + // Comparator allargsal = + // Comparator.comparing(ProofScriptArgument::isPositionalVarArgs); + // Comparator byRequired = + // Comparator.comparing(ProofScriptArgument::isRequired); + // Comparator byName = + // Comparator.comparing(ProofScriptArgument::getName); + // + // Comparator byPos = Comparator.comparing(it -> { + // if (it.isPositionalVarArgs()) { + // it.getPositionalVarargs().startIndex(); + // } + // if (it.isPositional()) { + // it.getArgument().value(); + // } + // + // return -1; + // }); + // + // + // var comp = optional + // .thenComparing(flagal) + // .thenComparing(positional) + // .thenComparing(allargsal) + // .thenComparing(byRequired) + // .thenComparing(byPos) + // .thenComparing(byName); var args = Arrays.stream(parameterClazz.getDeclaredFields()) .map(ProofScriptArgument::new) diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Documentation.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Documentation.java index e3c7a1f62f4..34175e6e723 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Documentation.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/Documentation.java @@ -17,5 +17,6 @@ public @interface Documentation { /// @return a non-null string String value(); + String category() default ""; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ProofScriptArgument.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ProofScriptArgument.java index 02b11bf41e9..d425df5d34e 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ProofScriptArgument.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ProofScriptArgument.java @@ -142,12 +142,13 @@ public Class getType() { /** * This is used for ordering arguments in the usage string and documention. * - * A twodigit number is used for positional arguments. 'M' for mandatory, 'O' for optional flags and options. + * A twodigit number is used for positional arguments. 'M' for mandatory, 'O' for optional flags + * and options. */ public String orderString() { - if(isPositional()) + if (isPositional()) return "%02d".formatted(getArgumentPosition()); - if(isRequired()) + if (isRequired()) return "M " + getName(); return "O " + getName(); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java index ea9088b1023..eddb19623b8 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/meta/ValueInjector.java @@ -6,7 +6,6 @@ import java.util.*; import java.util.stream.Collectors; -import de.uka.ilkd.key.nparser.KeYParser; import de.uka.ilkd.key.scripts.ProofScriptCommand; import de.uka.ilkd.key.scripts.ScriptCommandAst; @@ -14,7 +13,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.jspecify.annotations.NonNull; -import org.jspecify.annotations.Nullable; /** * @author Alexander Weigl @@ -342,14 +340,15 @@ public void addConverter(Converter conv) { * converter is known. */ @SuppressWarnings("unchecked") - public @NonNull Converter getConverter(Class ret, Class arg) throws NoSpecifiedConverterException { + public @NonNull Converter getConverter(Class ret, Class arg) + throws NoSpecifiedConverterException { if (ret.isAssignableFrom(arg)) { return (T it) -> (R) it; } Converter result = (Converter) converters.get(new ConverterKey<>(ret, arg)); if (result == null) { throw new NoSpecifiedConverterException( - "No converter registered for class: " + ret.getName() + " from " + arg.getName()); + "No converter registered for class: " + ret.getName() + " from " + arg.getName()); } return result; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlIO.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlIO.java index b4dcc1d4d6d..f4843a3a37b 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlIO.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlIO.java @@ -212,7 +212,7 @@ public JTerm translateTerm(ParserRuleContext expr) { case KeYJavaType kjt -> kjt; case Type type -> services.getJavaInfo().getKeYJavaType(type); default -> throw new IllegalArgumentException("Cannot translate to KeYJavaType: " + - interpreted + " of class " + interpreted.getClass()); + interpreted + " of class " + interpreted.getClass()); }; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/util/ANTLRUtil.java b/key.core/src/main/java/de/uka/ilkd/key/util/ANTLRUtil.java index c4be36280ae..20e594c92bf 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/util/ANTLRUtil.java +++ b/key.core/src/main/java/de/uka/ilkd/key/util/ANTLRUtil.java @@ -1,3 +1,6 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.util; import java.util.ArrayList; @@ -17,7 +20,7 @@ */ @NullMarked public final class ANTLRUtil { - private ANTLRUtil() { } + private ANTLRUtil() {} /** * Reconstructs the original source text from a given ANTLR4 parse tree. @@ -32,10 +35,12 @@ public static String reconstructOriginal(ParseTree tree) { for (TerminalNode tn : terminals) { Token t = tn.getSymbol(); - if (t == null) continue; - if (t.getType() == Token.EOF) continue; + if (t == null) + continue; + if (t.getType() == Token.EOF) + continue; - if(curLine == -1) { + if (curLine == -1) { // use first token to initialize line and column curCol = t.getCharPositionInLine(); curLine = t.getLine(); @@ -59,7 +64,8 @@ public static String reconstructOriginal(ParseTree tree) { } String text = t.getText(); - if (text == null) text = ""; + if (text == null) + text = ""; sb.append(text); @@ -78,7 +84,8 @@ public static String reconstructOriginal(ParseTree tree) { } private static void collectTerminals(ParseTree node, List out) { - if (node == null) return; + if (node == null) + return; if (node instanceof TerminalNode) { out.add((TerminalNode) node); return; @@ -90,7 +97,9 @@ private static void collectTerminals(ParseTree node, List out) { private static int countOccurrences(String s, char c) { int cnt = 0; - for (int i = 0; i < s.length(); i++) if (s.charAt(i) == c) cnt++; + for (int i = 0; i < s.length(); i++) + if (s.charAt(i) == c) + cnt++; return cnt; } -} \ No newline at end of file +} diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java index a70ab5de2ed..ff60686b5f8 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/DocumentationGenerator.java @@ -1,10 +1,13 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.scripts; -import de.uka.ilkd.key.util.KeYResourceManager; - import java.io.FileNotFoundException; import java.util.*; +import de.uka.ilkd.key.util.KeYResourceManager; + public class DocumentationGenerator { private static String branch; @@ -12,15 +15,17 @@ public class DocumentationGenerator { public static void main(String[] args) throws FileNotFoundException { - if(args.length > 0) { + if (args.length > 0) { System.err.println("Redirecting output to " + args[0]); System.setOut(new java.io.PrintStream(args[0])); } printHeader(); - Set> commands = ProofScriptEngine.loadCommands().entrySet(); - Map>> commandsByCategory = new TreeMap<>(); + Set> commands = + ProofScriptEngine.loadCommands().entrySet(); + Map>> commandsByCategory = + new TreeMap<>(); for (Map.Entry entry : commands) { String category = entry.getValue().getCategory(); @@ -48,50 +53,60 @@ private static void printHeader() { sha1 = KeYResourceManager.getManager().getSHA1(); // This gets too technical. But this is for the key-docs repository. ... - System.out.printf(""" - - # Proof Script Commands - - This document lists all proof script commands available in the KeY system. - The general ideas of scripts, their syntax, and control flow are described - in the general documentation files on proof scripts. - - Field | Value - ----- | ----- - Generated on: | %s - Branch: | %s - Version: | %s - Commit: | %s - - The commands are organised into categories. Each command may have multiple aliases - under which it can be invoked. The first alias listed is the primary name of the command. - There *named* and *positional* arguments. Named arguments need to be prefixed by their name - and a colon. Positional arguments are given in the order defined by the command. - Optional arguments are enclosed in square brackets. - """, new Date(), branch, version, sha1); + System.out.printf( + """ + + # Proof Script Commands + + This document lists all proof script commands available in the KeY system. + The general ideas of scripts, their syntax, and control flow are described + in the general documentation files on proof scripts. + + Field | Value + ----- | ----- + Generated on: | %s + Branch: | %s + Version: | %s + Commit: | %s + + The commands are organised into categories. Each command may have multiple aliases + under which it can be invoked. The first alias listed is the primary name of the command. + There *named* and *positional* arguments. Named arguments need to be prefixed by their name + and a colon. Positional arguments are given in the order defined by the command. + Optional arguments are enclosed in square brackets. + """, + new Date(), branch, version, sha1); } - private static void listCategory(String category, List> proofScriptCommands) { + private static void listCategory(String category, + List> proofScriptCommands) { proofScriptCommands.sort(Map.Entry.comparingByKey()); System.out.println("\n## Category *" + category + "*\n"); for (Map.Entry entry : proofScriptCommands) { System.out.println("


    \n"); - if(entry.getKey().equals(entry.getValue().getName())) { + if (entry.getKey().equals(entry.getValue().getName())) { ProofScriptCommand command = entry.getValue(); String link = "main".equals(branch) ? "main" : sha1; - System.out.println("### Command `" + command.getName() + "`\n\n"); - System.out.printf("[Source](https://github.com/KeYProject/key/blob/%s/key.core/src/main/java/%s.java)\n\n", - link, - command.getClass().getName().replace('.', '/') ); + System.out + .println("### Command `" + + command.getName() + "`\n\n"); + System.out.printf( + "[Source](https://github.com/KeYProject/key/blob/%s/key.core/src/main/java/%s.java)\n\n", + link, + command.getClass().getName().replace('.', '/')); System.out.println(command.getDocumentation() + "\n"); if (command.getAliases().size() > 1) { - System.out.println("#### Aliases:\n" + String.join(", ", command.getAliases()) + "\n"); + System.out.println( + "#### Aliases:\n" + String.join(", ", command.getAliases()) + "\n"); } } else { - System.out.println("### Command `" + entry.getKey() + "`\n"); - System.out.println("Alias for command [\u2192 `" + entry.getValue().getName() + "`](#command-" + entry.getValue().getName() + ")\n"); + System.out + .println("### Command `" + + entry.getKey() + "`\n"); + System.out.println("Alias for command [\u2192 `" + entry.getValue().getName() + + "`](#command-" + entry.getValue().getName() + ")\n"); } } } diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java index fa53950a474..503ff1cd2eb 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/JmlScriptTest.java @@ -1,23 +1,8 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.scripts; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import de.uka.ilkd.key.control.DefaultUserInterfaceControl; -import de.uka.ilkd.key.control.KeYEnvironment; -import de.uka.ilkd.key.control.UserInterfaceControl; -import de.uka.ilkd.key.nparser.KeyAst; -import de.uka.ilkd.key.proof.io.ProblemLoaderControl; -import de.uka.ilkd.key.proof.io.ProofSaver; -import de.uka.ilkd.key.util.KeYConstants; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import recoder.util.Debug; - import java.io.File; import java.io.IOException; import java.net.URISyntaxException; @@ -27,10 +12,24 @@ import java.nio.file.Paths; import java.util.Comparator; import java.util.Map; -import java.util.logging.LogManager; import java.util.stream.Collectors; import java.util.stream.Stream; +import de.uka.ilkd.key.control.DefaultUserInterfaceControl; +import de.uka.ilkd.key.control.KeYEnvironment; +import de.uka.ilkd.key.nparser.KeyAst; +import de.uka.ilkd.key.proof.io.ProofSaver; +import de.uka.ilkd.key.util.KeYConstants; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class JmlScriptTest { private static final Path KEY_FILE; @@ -62,9 +61,10 @@ public void testJmlScript(Path path, String identifier) throws Exception { Path projectFile = tmpDir.resolve("project.key"); Files.copy(KEY_FILE, projectFile); KeYEnvironment env = KeYEnvironment.load(projectFile); - if(params.settings != null && !params.settings.isEmpty()) { + if (params.settings != null && !params.settings.isEmpty()) { for (Map.Entry entry : params.settings.entrySet()) { - env.getLoadedProof().getSettings().getStrategySettings().getActiveStrategyProperties() + env.getLoadedProof().getSettings().getStrategySettings() + .getActiveStrategyProperties() .setProperty(entry.getKey(), entry.getValue()); } } @@ -74,23 +74,25 @@ public void testJmlScript(Path path, String identifier) throws Exception { pse.execute(env.getUi(), script); } - if(SAVE_PROOF) { + if (SAVE_PROOF) { String filename = tmpDir.resolve("saved.proof").toString(); - ProofSaver saver = new ProofSaver(env.getLoadedProof(), filename, KeYConstants.INTERNAL_VERSION); + ProofSaver saver = + new ProofSaver(env.getLoadedProof(), filename, KeYConstants.INTERNAL_VERSION); saver.save(); LOGGER.info("Saved proof to {}", filename); } - if(params.shouldClose) { + if (params.shouldClose) { Assertions.assertTrue(env.getLoadedProof().closed(), "Proof did not close."); } else { Assertions.assertFalse(env.getLoadedProof().closed(), "Proof closes unexpectedly."); } } finally { // Uncomment the following line to delete the temporary directory after the test - if(params.deleteTmpDir && !SAVE_PROOF) { + if (params.deleteTmpDir && !SAVE_PROOF) { LOGGER.info("Deleting temporary directory: {}", tmpDir); - Files.walk(tmpDir).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); + Files.walk(tmpDir).sorted(Comparator.reverseOrder()).map(Path::toFile) + .forEach(File::delete); } else { LOGGER.info("Temporary directory retained for inspection: {}", tmpDir); } @@ -100,8 +102,8 @@ public void testJmlScript(Path path, String identifier) throws Exception { private static Parameters readParams(Path path) throws IOException { String input = Files.lines(path).filter(l -> l.startsWith("//!")).map(l -> l.substring(3)) - .collect(Collectors.joining("\n")).trim(); - if(input.isEmpty()) { + .collect(Collectors.joining("\n")).trim(); + if (input.isEmpty()) { return new Parameters(); } var objectMapper = new ObjectMapper(new YAMLFactory()); @@ -112,7 +114,8 @@ private static Parameters readParams(Path path) throws IOException { public static Stream filesProvider() throws URISyntaxException, IOException { URL jmlUrl = JmlScriptTest.class.getResource("jml"); if (ONLY_CASE != null) { - return Stream.of(Arguments.of(Paths.get(jmlUrl.toURI()).resolve(ONLY_CASE), "single specified case: " + ONLY_CASE)); + return Stream.of(Arguments.of(Paths.get(jmlUrl.toURI()).resolve(ONLY_CASE), + "single specified case: " + ONLY_CASE)); } else { return Files.list(Paths.get(jmlUrl.toURI())) .filter(p -> p.toString().endsWith(".java")) diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java index 4eb88209a5c..4bf11721adc 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java @@ -12,19 +12,15 @@ import java.util.List; import java.util.Set; import java.util.function.Predicate; -import java.util.stream.Stream; import de.uka.ilkd.key.control.DefaultUserInterfaceControl; import de.uka.ilkd.key.control.KeYEnvironment; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.nparser.ParsingFacade; -import de.uka.ilkd.key.pp.LogicPrinter; -import de.uka.ilkd.key.pp.NotationInfo; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.smt.newsmt2.MasterHandlerTest; -import org.jspecify.annotations.NonNull; import org.key_project.util.collection.ImmutableList; import com.fasterxml.jackson.databind.ObjectMapper; @@ -62,10 +58,11 @@ public static List data() throws IOException, URISyntaxException { .toAbsolutePath(); Predicate filter; - if(ONLY_CASES != null && !ONLY_CASES.isEmpty()) { + if (ONLY_CASES != null && !ONLY_CASES.isEmpty()) { // if ONLY_CASES is set, only run those cases (comma separated) Set only = Set.of(ONLY_CASES.split(" *, *")); - filter = p -> only.contains(p.getFileName().toString().substring(0, p.getFileName().toString().length() - 4)); + filter = p -> only.contains( + p.getFileName().toString().substring(0, p.getFileName().toString().length() - 4)); } else { filter = p -> true; } @@ -78,15 +75,14 @@ public static List data() throws IOException, URISyntaxException { List args = new ArrayList<>(files.size()); for (Path path : files) { - if(!filter.test(path)) { + if (!filter.test(path)) { continue; } try { TestInstance instance = objectMapper.readValue(path.toFile(), TestInstance.class); - var name = instance.name == null ? - path.getFileName().toString().substring(0, path.getFileName().toString().length() - 4) : - instance.name; + var name = instance.name == null ? path.getFileName().toString().substring(0, + path.getFileName().toString().length() - 4) : instance.name; args.add(Arguments.of(instance, name)); } catch (Exception e) { System.out.println(path); @@ -143,7 +139,8 @@ void testProofScript(TestInstance data, String name) throws Exception { if (data.selectedGoal() != null) { Goal goal = pse.getStateMap().getFirstOpenAutomaticGoal(); - assertThat(normaliseSpace(goal.toString())).isEqualTo(data.goals()[data.selectedGoal()]); + assertThat(normaliseSpace(goal.toString())) + .isEqualTo(data.goals()[data.selectedGoal()]); } } } diff --git a/key.ui/src/main/java/de/uka/ilkd/key/gui/ProofScriptWorker.java b/key.ui/src/main/java/de/uka/ilkd/key/gui/ProofScriptWorker.java index e218efc1623..2f6df0d8b59 100644 --- a/key.ui/src/main/java/de/uka/ilkd/key/gui/ProofScriptWorker.java +++ b/key.ui/src/main/java/de/uka/ilkd/key/gui/ProofScriptWorker.java @@ -5,7 +5,6 @@ import java.awt.*; import java.awt.Dialog.ModalityType; -import java.io.IOException; import java.net.URI; import java.util.List; import java.util.concurrent.CancellationException; From 0d62fa7597dca03a0bce297b68a6828975c7888a Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Mon, 23 Feb 2026 13:05:20 +0100 Subject: [PATCH 83/90] fix regression fixtures taclets.old.txt --- .../src/test/resources/de/uka/ilkd/key/nparser/taclets.old.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/key.core/src/test/resources/de/uka/ilkd/key/nparser/taclets.old.txt b/key.core/src/test/resources/de/uka/ilkd/key/nparser/taclets.old.txt index eaba7dd834f..505c99527b5 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/nparser/taclets.old.txt +++ b/key.core/src/test/resources/de/uka/ilkd/key/nparser/taclets.old.txt @@ -17991,6 +17991,7 @@ tryCatchThrow { #slist1 } ... }}| (post)) +\sameUpdateLevel \add []==>[equals(#se,null)] \replacewith(#allmodal ((modal operator))|{{ .. if (#se instanceof #t) { #t #v0; From 95db885bf8648303843a81c24aa9e80f8ffaf8b1 Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Thu, 9 Apr 2026 16:02:33 +0200 Subject: [PATCH 84/90] fix rebasing --- .../key/java/ast/statement/JmlAssert.java | 10 + .../ilkd/key/java/loader/JP2KeYConverter.java | 555 +++++++++--------- .../MarkerStatementHelper.java | 5 +- .../pipeline/JMLTransformer.java | 4 +- .../TransformationPipelineServices.java | 7 +- .../ilkd/key/macros/ApplyScriptsMacro.java | 6 +- .../java/de/uka/ilkd/key/nparser/KeyAst.java | 2 +- .../java/de/uka/ilkd/key/parser/Location.java | 2 +- .../uka/ilkd/key/proof/init/JavaProfile.java | 1 - .../uka/ilkd/key/scripts/ScriptException.java | 2 + .../uka/ilkd/key/scripts/TermWithHoles.java | 14 +- .../de/uka/ilkd/key/speclang/njml/JmlIO.java | 1 + .../key/speclang/njml/TextualTranslator.java | 1 - 13 files changed, 322 insertions(+), 288 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java index 94888f8c258..9d587005baf 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/ast/statement/JmlAssert.java @@ -86,6 +86,16 @@ public JmlAssert(JmlAssert other) { other.getPositionInfo()); } + public JmlAssert(TextualJMLAssertStatement.Kind kind, TextualJMLAssertStatement construct, + PositionInfo pi) { + super(pi); + this.kind = kind; + this.optLabel = construct.getOptLabel(); + this.condition = construct.getContext(); + // script may be null + this.assertionProof = construct.getAssertionProof(); + } + public TextualJMLAssertStatement.Kind getKind() { return kind; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/loader/JP2KeYConverter.java b/key.core/src/main/java/de/uka/ilkd/key/java/loader/JP2KeYConverter.java index b2985ea025e..841f26a0771 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/loader/JP2KeYConverter.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/loader/JP2KeYConverter.java @@ -36,6 +36,7 @@ import de.uka.ilkd.key.logic.VariableNamer; import de.uka.ilkd.key.logic.op.*; import de.uka.ilkd.key.logic.sort.ProgramSVSort; +import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.parser.Location; import de.uka.ilkd.key.parser.ParserException; import de.uka.ilkd.key.rule.metaconstruct.*; @@ -52,6 +53,7 @@ import org.key_project.util.collection.ImmutableArray; import org.key_project.util.collection.ImmutableList; +import com.github.javaparser.Range; import com.github.javaparser.ast.*; import com.github.javaparser.ast.Modifier; import com.github.javaparser.ast.body.*; @@ -69,6 +71,7 @@ import com.github.javaparser.ast.visitor.GenericVisitorAdapter; import com.github.javaparser.ast.visitor.Visitable; import com.github.javaparser.resolution.UnsolvedSymbolException; +import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration; import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; import com.github.javaparser.resolution.model.typesystem.ReferenceTypeImpl; import com.github.javaparser.resolution.types.ResolvedType; @@ -82,6 +85,7 @@ import org.slf4j.LoggerFactory; import static com.github.javaparser.ast.Modifier.DefaultKeyword.*; +import static de.uka.ilkd.key.speclang.jml.pretranslation.TextualJMLAssertStatement.*; import static java.lang.String.format; /** @@ -100,8 +104,10 @@ public Object process(Node block) { // Install the symbolSolver if the node has none. block.getSymbolResolver(); } catch (IllegalStateException ignore) { - var symbolSolver = services.getJavaService().getProgramFactory().getSymbolSolver(); - var compUnit = block.findCompilationUnit(); + JavaSymbolSolver symbolSolver = + services.getJavaService().getProgramFactory().getSymbolSolver(); + Optional compUnit = + block.findCompilationUnit(); compUnit.ifPresent(it -> it.setData(Node.SYMBOL_RESOLVER_KEY, symbolSolver)); } return block.accept(new JP2KeYVisitor(services, schemaVariables), null); @@ -160,14 +166,14 @@ private static ProgramElementName createProgramElementName(SimpleName n) { throw new IllegalArgumentException( "Creating a program element name from a string that identifies a schema variable"); } - var c = createComments(n); + List c = createComments(n); return new ProgramElementName(n.asString(), c.toArray(Comment[]::new)); } @Override public Object visit(ArrayAccessExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); Expression expr = accept(n.getIndex()); Expression prefix = accept(n.getName()); // TODO weigl how to express (new int[0])[0] in Java-KeY-AST? @@ -176,8 +182,8 @@ public Object visit(ArrayAccessExpr n, Void arg) { @Override public Object visit(ArrayCreationExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); TypeReference type = accept(n.getElementType()); // TODO javaparser how should int[5][4][][] be encoded in the key ast? ArrayInitializer ai; @@ -191,18 +197,19 @@ public Object visit(ArrayCreationExpr n, Void arg) { } int dimensions = n.getLevels().size(); - var rtype = n.calculateResolvedType(); + ResolvedType rtype = n.calculateResolvedType(); return new NewArray(pi, c, children, type, getKeYJavaType(rtype), dimensions, ai); } private ArrayInitializer visitArrayInitializerExpr(ArrayInitializerExpr n, KeYJavaType type) { - var pi = createPositionInfo(n); - var c = createComments(n); - var list = new ArrayList(n.getValues().size()); - for (var node : n.getValues()) { + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); + ArrayList list = new ArrayList(n.getValues().size()); + for (com.github.javaparser.ast.expr.Expression node : n.getValues()) { Expression expr; if (node instanceof ArrayInitializerExpr) { - var array = ((de.uka.ilkd.key.java.ast.abstraction.ArrayType) type.getJavaType()); + de.uka.ilkd.key.java.ast.abstraction.ArrayType array = + ((de.uka.ilkd.key.java.ast.abstraction.ArrayType) type.getJavaType()); expr = visitArrayInitializerExpr((ArrayInitializerExpr) node, array.getBaseType().getKeYJavaType()); } else { @@ -210,7 +217,7 @@ private ArrayInitializer visitArrayInitializerExpr(ArrayInitializerExpr n, KeYJa } list.add(expr); } - var children = new ImmutableArray<>(list); + ImmutableArray children = new ImmutableArray<>(list); return new ArrayInitializer(pi, c, children, type); } @@ -225,8 +232,8 @@ public Object visit(AssertStmt n, Void arg) { public Object visit(AssignExpr n, Void arg) { Expression target = accept(n.getTarget()); Expression expr = accept(n.getValue()); - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); return switch (n.getOperator()) { case ASSIGN -> new CopyAssignment(pi, c, target, expr); case PLUS -> new PlusAssignment(pi, c, target, expr); @@ -245,10 +252,10 @@ public Object visit(AssignExpr n, Void arg) { @Override public Object visit(BinaryExpr n, Void arg) { - var lhs = (Expression) n.getLeft().accept(this, arg); - var rhs = (Expression) n.getRight().accept(this, arg); - var pi = createPositionInfo(n); - var c = createComments(n); + Expression lhs = (Expression) n.getLeft().accept(this, arg); + Expression rhs = (Expression) n.getRight().accept(this, arg); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); return switch (n.getOperator()) { case OR -> new LogicalOr(pi, c, lhs, rhs); case AND -> new LogicalAnd(pi, c, lhs, rhs); @@ -275,40 +282,40 @@ public Object visit(BinaryExpr n, Void arg) { @Override public Object visit(BlockStmt n, Void arg) { ImmutableArray body = map(n.getStatements()); - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); return new StatementBlock(pi, c, body, getSpec(n)); } @Override public Object visit(BooleanLiteralExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); return new BooleanLiteral(pi, c, n.getValue()); } @Override public Object visit(BreakStmt n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); - var name = simpleNameToLabel(n.getLabel()); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); + Label name = simpleNameToLabel(n.getLabel()); return new Break(pi, c, name); } @Override public Object visit(CastExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); TypeReference type = requireTypeReference(n.getType()); return new TypeCast(pi, c, accept(n.getExpression()), type); } @Override public Object visit(CatchClause n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); if (n instanceof KeyCatchClauseSV sv) { - var v = lookupSchemaVariable(new Name(sv.getSchemaVar())); + SchemaVariable v = lookupSchemaVariable(new Name(sv.getSchemaVar())); if (!(v instanceof ProgramSV)) { reportError(n, "Catch"); } @@ -321,18 +328,18 @@ public Object visit(CatchClause n, Void arg) { @Override public Object visit(CharLiteralExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); return new CharLiteral(pi, c, n.getValue().charAt(0)); } @Override public Object visit(ClassOrInterfaceDeclaration n, Void arg) { - final var ref = new ReferenceTypeImpl(n.resolve()); - var kjt = createOrCachedKeyJavaType(ref); + final ReferenceTypeImpl ref = new ReferenceTypeImpl(n.resolve()); + KeYJavaType kjt = createOrCachedKeyJavaType(ref); - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); ProgramElementName name = createProgramElementName(n.getName()); ProgramElementName fullName = new ProgramElementName(n.getFullyQualifiedName().get()); boolean isLibrary = mapping.isParsingLibraries(); @@ -363,7 +370,7 @@ public Object visit(ClassOrInterfaceDeclaration n, Void arg) { } private KeYJavaType createOrCachedKeyJavaType(ReferenceTypeImpl ref) { - var kjt = getCachedKeYJavaType(ref); + KeYJavaType kjt = getCachedKeYJavaType(ref); if (kjt != null) { return kjt; } @@ -382,7 +389,7 @@ private T acceptn(@Nullable Node check) { private boolean parentIsInterface(@NonNull Node n) { if (n.getParentNode().isPresent()) { - var parent = n.getParentNode().get(); + Node parent = n.getParentNode().get(); if (parent instanceof ClassOrInterfaceDeclaration) { return ((ClassOrInterfaceDeclaration) parent).isInterface(); } @@ -395,7 +402,7 @@ private static PositionInfo createPositionInfo(Node node) { if (node.getRange().isEmpty()) { return PositionInfo.UNDEFINED; } - var r = node.getRange().get(); + Range r = node.getRange().get(); URI uri = node.findCompilationUnit() .flatMap(com.github.javaparser.ast.CompilationUnit::getStorage) @@ -414,7 +421,7 @@ public Object visit(ClassOrInterfaceType n, Void arg) { final var name = n.getNameAsString(); if (name.startsWith("\\")) { JavaInfo ji = services.getJavaInfo(); - var type = ji.getPrimitiveKeYJavaType(name); + KeYJavaType type = ji.getPrimitiveKeYJavaType(name); if (type == null) { return reportError(n, "Unresolved KeY type"); } @@ -433,7 +440,7 @@ public Object visit(com.github.javaparser.ast.CompilationUnit n, Void arg) { } private static List createComments(Node n) { - var comments = new ArrayList(); + ArrayList comments = new ArrayList(); // if (n.containsData(JMLCommentTransformer.BEFORE_COMMENTS)) { // comments.addAll(n.getData(JMLCommentTransformer.BEFORE_COMMENTS).stream() // .map(c -> new Comment(c.asString(), createPositionInfo(c))).toList()); @@ -447,9 +454,9 @@ private static List createComments(Node n) { @SuppressWarnings("unchecked") private ImmutableArray map(NodeList nodes) { - var list = new ArrayList(nodes.size()); + ArrayList list = new ArrayList(nodes.size()); for (Node node : nodes) { - var res = node.accept(this, null); + Object res = node.accept(this, null); list.add((T) Objects.requireNonNull(res)); } return new ImmutableArray<>(list); @@ -457,7 +464,7 @@ private ImmutableArray map(NodeList nodes) { @SuppressWarnings("unchecked") private ImmutableArray flatMap(NodeList nodes) { - var seq = nodes.stream() + List seq = nodes.stream() .flatMap(it -> ((List) Objects.requireNonNull(it.accept(this, null))).stream()) .collect(Collectors.toList()); return new ImmutableArray<>(seq); @@ -470,8 +477,8 @@ private R accepto(Optional node) { @Override public Object visit(ConditionalExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); return new Conditional(pi, c, accept(n.getCondition()), accept(n.getThenExpr()), @@ -480,25 +487,26 @@ public Object visit(ConditionalExpr n, Void arg) { @Override public Object visit(ConstructorDeclaration n, Void arg) { - var isInInterface = parentIsInterface(n); - var pi = createPositionInfo(n); - var c = createComments(n); + boolean isInInterface = parentIsInterface(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); ImmutableArray exc = map(n.getThrownExceptions()); Throws thr = exc.isEmpty() ? null : new Throws(null, null, exc); - final var body = n.body(); - var cd = new de.uka.ilkd.key.java.ast.declaration.ConstructorDeclaration(pi, c, - map(n.getModifiers()), - null, - null, - createProgramElementName(n.getName()), - map(n.getParameters()), - thr, - body != null ? accept(body) : new StatementBlock(), isInInterface, - getSpec(n)); - - var clazz = getContainingClass(n); + final BlockStmt body = n.body(); + de.uka.ilkd.key.java.ast.declaration.ConstructorDeclaration cd = + new de.uka.ilkd.key.java.ast.declaration.ConstructorDeclaration(pi, c, + map(n.getModifiers()), + null, + null, + createProgramElementName(n.getName()), + map(n.getParameters()), + thr, + body != null ? accept(body) : new StatementBlock(), isInInterface, + getSpec(n)); + + ClassOrInterfaceDeclaration clazz = getContainingClass(n); try { - var containing = clazz.resolve(); + ResolvedReferenceTypeDeclaration containing = clazz.resolve(); final HeapLDT heapLDT = typeConverter.getTypeConverter().getHeapLDT(); Sort heapSort = heapLDT == null ? JavaDLTheory.ANY : heapLDT.targetSort(); @@ -506,7 +514,7 @@ public Object visit(ConstructorDeclaration n, Void arg) { // store container type as member when visiting type declaration. final KeYJavaType containerKJT = getCachedKeYJavaType(new ReferenceTypeImpl(containing)); - var method = + ProgramMethod method = new ProgramMethod(cd, containerKJT, KeYJavaType.VOID_TYPE, PositionInfo.UNDEFINED, heapSort, heapLDT == null ? 1 : heapLDT.getAllHeaps().size() - 1); return addToMapping(n, method); @@ -518,8 +526,8 @@ public Object visit(ConstructorDeclaration n, Void arg) { @Override public Object visit(ContinueStmt n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); Label name = simpleNameToLabel(n.getLabel()); return new Continue(pi, c, name); } @@ -529,7 +537,7 @@ private Label nameToLabel(@Nullable Name name) { return null; } - var str = name.asString(); + String str = name.asString(); if (str.startsWith("#")) { return (Label) lookupSchemaVariable(str, name); } @@ -552,10 +560,10 @@ private Label simpleNameToLabel(Optional label) { @Override public Object visit(DoStmt n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); - var guard = accept(n.getCondition()); - var body = accept(n.getBody()); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); + Object guard = accept(n.getCondition()); + Object body = accept(n.getBody()); List loopSpecs = new ArrayList<>(); loopSpecs.addAll(getLoopSpec(n)); // loop invariants loopSpecs.addAll(getSpec(n)); // loop contracts @@ -565,15 +573,15 @@ public Object visit(DoStmt n, Void arg) { @Override public Object visit(DoubleLiteralExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); return new DoubleLiteral(pi, c, n.getValue()); } @Override public Object visit(EmptyStmt n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); /* * if (n.containsData(JMLTransformer.KEY_CONSTRUCT)) { * var construct = n.getData(JMLTransformer.KEY_CONSTRUCT); @@ -594,17 +602,17 @@ public Object visit(EmptyStmt n, Void arg) { @Override public Object visit(EnclosedExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); - var expr = accept(n.getInner()); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); + Object expr = accept(n.getInner()); return new ParenthesizedExpression(pi, c, (Expression) expr); } @Override public Object visit(ExplicitConstructorInvocationStmt n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); ImmutableArray args = map(n.getArguments()); return n.isThis() ? new ThisConstructorReference(args, pi, c) : new SuperConstructorReference(args, pi, c); @@ -631,30 +639,30 @@ private static FieldSpecification findArrayLength(ArrayDeclaration type) { @Override public Object visit(FieldAccessExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); if (n.getNameAsString().startsWith("#")) { - var scope = (ReferencePrefix) n.getScope().accept(this, arg); - var name = lookupSchemaVariable(n.getName()); + ReferencePrefix scope = (ReferencePrefix) n.getScope().accept(this, arg); + SchemaVariable name = lookupSchemaVariable(n.getName()); return new SchematicFieldReference(pi, c, name, scope); } if ("length".equals(n.getName().getId())) { // Handle .length access of sequences // TODO: Move this to KeYJavaParser project once its aware of logical types - var ty = n.scope().calculateResolvedType(); + ResolvedType ty = n.scope().calculateResolvedType(); if (ty instanceof ReferenceTypeImpl rti && rti.getTypeDeclaration().isPresent() && rti.getTypeDeclaration().get() instanceof ResolvedLogicalType rlt && rlt.getKeYJavaType().getSort() == services.getTypeConverter().getSeqLDT() .targetSort()) { - var child = (Expression) n.scope().accept(this, null); + Expression child = (Expression) n.scope().accept(this, null); return new SeqLength(pi, c, child); } } try { ResolvedValueDeclaration target = n.resolve(); - var rtype = n.calculateResolvedType(); + ResolvedType rtype = n.calculateResolvedType(); - var descriptor = "L%s/%s;".formatted( + String descriptor = "L%s/%s;".formatted( n.getScope().toString().replace(".", "/"), n.getNameAsString()); @@ -690,8 +698,8 @@ public Object visit(FieldAccessExpr n, Void arg) { final VariableDeclarator varDecl = variableCandidates.getFirst(); - var isModel = fldDecl.hasModifier(JML_MODEL); - var isGhost = fldDecl.hasModifier(JML_GHOST); + boolean isModel = fldDecl.hasModifier(JML_MODEL); + boolean isGhost = fldDecl.hasModifier(JML_GHOST); final FullVariableDeclarator decl = new FullVariableDeclarator(varDecl, varDecl.findAncestor(ClassOrInterfaceDeclaration.class).orElse(null), @@ -708,7 +716,7 @@ public Object visit(FieldAccessExpr n, Void arg) { } catch (UnsolvedSymbolException e) { try { ResolvedType type = n.calculateResolvedType(); - var keyType = getKeYJavaType(type); + KeYJavaType keyType = getKeYJavaType(type); return new TypeRef(keyType); } catch (UnsolvedSymbolException e1) { throw new ParserException("Name could not be resolved '" + n + "'", @@ -719,8 +727,8 @@ public Object visit(FieldAccessExpr n, Void arg) { @Override public Object visit(TypeExpr n, Void arg) { - var rt = n.calculateResolvedType(); - var kjt = getKeYJavaType(rt); + ResolvedType rt = n.calculateResolvedType(); + KeYJavaType kjt = getKeYJavaType(rt); return new TypeRef(kjt); } @@ -748,18 +756,18 @@ private ClassOrInterfaceDeclaration getContainingClass(Node node) { @Override public Object visit(KeYMarkerStatement n, Void arg) { - var pi = createPositionInfo(n); + PositionInfo pi = createPositionInfo(n); return switch (n.getKind()) { case MarkerStatementHelper.KIND_ASSERT -> { - var construct = n.getData(MarkerStatementHelper.KEY_EXPR); - yield new JmlAssert(TextualJMLAssertStatement.Kind.ASSERT, construct, pi); + TextualJMLAssertStatement construct = n.getData(MarkerStatementHelper.KEY_ASSERT); + yield new JmlAssert(Kind.ASSERT, construct, pi); } case MarkerStatementHelper.KIND_ASSUME -> { - var construct = n.getData(MarkerStatementHelper.KEY_EXPR); - yield new JmlAssert(TextualJMLAssertStatement.Kind.ASSUME, construct, pi); + TextualJMLAssertStatement construct = n.getData(MarkerStatementHelper.KEY_ASSERT); + yield new JmlAssert(Kind.ASSUME, construct, pi); } case MarkerStatementHelper.KIND_SET -> { - var context = n.getData(MarkerStatementHelper.KEY_ASSIGN); + KeyAst.SetStatementContext context = n.getData(MarkerStatementHelper.KEY_ASSIGN); yield new SetStatement(context, pi); } @@ -779,32 +787,33 @@ public Object visit(KeYMarkerStatement n, Void arg) { @Override public Object visit(FieldDeclaration n, Void arg) { - var existing = mapping.nodeToKeY(n); + ProgramElement existing = mapping.nodeToKeY(n); if (existing != null) { return existing; } - var pi = createPositionInfo(n); - var c = createComments(n); - var isInInterface = parentIsInterface(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); + boolean isInInterface = parentIsInterface(n); ImmutableArray modArray = map(n.getModifiers()); TypeReference type = requireTypeReference(n.getVariables().get(0).getType()); - var varsList = new ArrayList(n.getVariables().size()); + ArrayList varsList = + new ArrayList(n.getVariables().size()); for (VariableDeclarator v : n.getVariables()) { boolean isInstance = n.hasModifier(JML_INSTANCE); boolean isStatic = n.hasModifier(STATIC) || (isInInterface && !isInstance); boolean isFinal = n.hasModifier(FINAL) || (isInInterface && !isInstance); boolean isModel = n.hasModifier(JML_MODEL); boolean isGhost = n.hasModifier(JML_GHOST); - var decl = new FullVariableDeclarator(v, + FullVariableDeclarator decl = new FullVariableDeclarator(v, v.findAncestor(ClassOrInterfaceDeclaration.class).orElse(null), isFinal, isStatic, isModel, isGhost); - final var fs = visitFieldSpecification(decl); + final FieldSpecification fs = visitFieldSpecification(decl); varsList.add(fs); mapping.put(v, fs); } - var fieldSpecs = new ImmutableArray<>(varsList); - final var decl = + ImmutableArray fieldSpecs = new ImmutableArray<>(varsList); + final de.uka.ilkd.key.java.ast.declaration.FieldDeclaration decl = new de.uka.ilkd.key.java.ast.declaration.FieldDeclaration(pi, c, modArray, type, isInInterface, fieldSpecs); return addToMapping(n, decl); @@ -812,8 +821,8 @@ public Object visit(FieldDeclaration n, Void arg) { @Override public Object visit(ForEachStmt n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); LocalVariableDeclaration decl = accept(n.getVariable()); ILoopInit init = new LoopInit(new LoopInitializer[] { decl }); Guard guard = new Guard(null, null, accept(n.getIterable())); @@ -825,8 +834,8 @@ public Object visit(ForEachStmt n, Void arg) { @Override public Object visit(ForStmt n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); ImmutableArray inits = map(n.getInitialization()); ImmutableArray updates = map(n.getUpdate()); Object guard = accepto(n.getCompare()); @@ -883,8 +892,8 @@ public static List getClassSpec(Node n) { @Override public Object visit(IfStmt n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); Statement t = accept(n.getThenStmt()); Statement e = accepto(n.getElseStmt()); return new If(pi, c, accept(n.getCondition()), @@ -894,10 +903,10 @@ public Object visit(IfStmt n, Void arg) { @Override public Object visit(InitializerDeclaration n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); StatementBlock body = accept(n.getBody()); - var mods = + de.uka.ilkd.key.java.ast.declaration.Modifier[] mods = n.isStatic() ? new de.uka.ilkd.key.java.ast.declaration.Modifier[] { new Static() } : new de.uka.ilkd.key.java.ast.declaration.Modifier[0]; return new ClassInitializer(mods, body, pi, c); @@ -905,17 +914,17 @@ public Object visit(InitializerDeclaration n, Void arg) { @Override public Object visit(InstanceOfExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); Expression lhs = accept(n.getExpression()); - var type = requireTypeReference(n.getType()); + TypeReference type = requireTypeReference(n.getType()); return new Instanceof(pi, c, lhs, type); } @Override public Object visit(IntegerLiteralExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); return new IntLiteral(pi, c, n.getValue()); } @@ -927,22 +936,22 @@ public Object visit(LabeledStmt n, Void arg) { } else { id = createProgramElementName(n.getLabel()); } - var stmt = accept(n.getStatement()); + Object stmt = accept(n.getStatement()); return new LabeledStatement(id, (Statement) stmt, createPositionInfo(n)); } @Override public Object visit(LongLiteralExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); return new LongLiteral(pi, c, n.getValue()); } @Override public Object visit(MethodCallExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); if (n.getNameAsString().startsWith("\\")) { return handleSpecialFunctionInvocation(n, n.getNameAsString(), n.getArguments()); } @@ -960,30 +969,31 @@ public Object visit(MethodCallExpr n, Void arg) { @Override public Object visit(MethodDeclaration n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); ImmutableArray t = map(n.getThrownExceptions()); - var thr = t.isEmpty() ? null : new Throws(null, null, t); - var isInInterface = parentIsInterface(n); + Throws thr = t.isEmpty() ? null : new Throws(null, null, t); + boolean isInInterface = parentIsInterface(n); TypeReference returnType = requireTypeReference(n.getType()); - var md = new de.uka.ilkd.key.java.ast.declaration.MethodDeclaration( - pi, c, map(n.getModifiers()), - returnType, - null, - createProgramElementName(n.getName()), - map(n.getParameters()), - thr, - accepto(n.getBody()), - isInInterface, ImmutableList.fromList(getSpec(n))); - - var containing = getContainingClass(n).resolve(); + de.uka.ilkd.key.java.ast.declaration.MethodDeclaration md = + new de.uka.ilkd.key.java.ast.declaration.MethodDeclaration( + pi, c, map(n.getModifiers()), + returnType, + null, + createProgramElementName(n.getName()), + map(n.getParameters()), + thr, + accepto(n.getBody()), + isInInterface, ImmutableList.fromList(getSpec(n))); + + ResolvedReferenceTypeDeclaration containing = getContainingClass(n).resolve(); final HeapLDT heapLDT = typeConverter.getTypeConverter().getHeapLDT(); Sort heapSort = heapLDT == null ? JavaDLTheory.ANY : heapLDT.targetSort(); final KeYJavaType containerType = getKeYJavaType(new ReferenceTypeImpl(containing)); // may be null for a void method - var method = new ProgramMethod(md, containerType, returnType.getKeYJavaType(), pi, + ProgramMethod method = new ProgramMethod(md, containerType, returnType.getKeYJavaType(), pi, heapSort, heapLDT == null ? 1 : heapLDT.getAllHeaps().size() - 1); return addToMapping(n, method); } @@ -1009,7 +1019,7 @@ public Object visit(NameExpr n, Void arg) { } catch (UnsolvedSymbolException e) { try { ResolvedType type = n.calculateResolvedType(); - var keyType = getKeYJavaType(type); + KeYJavaType keyType = getKeYJavaType(type); return new TypeRef(keyType); } catch (UnsolvedSymbolException e1) { throw new ParserException("Name could not be resolved '" + n + "'", @@ -1021,17 +1031,18 @@ public Object visit(NameExpr n, Void arg) { return reportUnsupportedElement(n); } - var ast = target.toAst().get(); + Node ast = target.toAst().get(); // Make sure the field is already converted - var tmp = (VariableDeclaration) mapping.nodeToKeY(ast); + VariableDeclaration tmp = (VariableDeclaration) mapping.nodeToKeY(ast); VariableDeclaration other = tmp == null ? accept(ast) : tmp; - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); if (target instanceof JavaParserFieldDeclaration) { // Field declarations can have multiple variables - var decl = ((JavaParserFieldDeclaration) target).getVariableDeclarator(); - var keyDecl = (VariableSpecification) Objects.requireNonNull(mapping.nodeToKeY(decl)); - var pv = (ProgramVariable) keyDecl.getProgramVariable(); + VariableDeclarator decl = ((JavaParserFieldDeclaration) target).getVariableDeclarator(); + VariableSpecification keyDecl = + (VariableSpecification) Objects.requireNonNull(mapping.nodeToKeY(decl)); + ProgramVariable pv = (ProgramVariable) keyDecl.getProgramVariable(); if (pv.isMember()) { // TODO javaparser prefix null? should we add default this? return new FieldReference(pi, c, pv, null); @@ -1041,8 +1052,10 @@ public Object visit(NameExpr n, Void arg) { } if (target instanceof JavaParserVariableDeclaration) { // Variable declarations can have multiple variables - var decl = ((JavaParserVariableDeclaration) target).getVariableDeclarator(); - var keyDecl = (VariableSpecification) Objects.requireNonNull(mapping.nodeToKeY(decl)); + VariableDeclarator decl = + ((JavaParserVariableDeclaration) target).getVariableDeclarator(); + VariableSpecification keyDecl = + (VariableSpecification) Objects.requireNonNull(mapping.nodeToKeY(decl)); return keyDecl.getProgramVariable(); } if (other.getVariables().size() == 1) { @@ -1068,15 +1081,15 @@ public Object visit(NormalAnnotationExpr n, Void arg) { @Override public Object visit(NullLiteralExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); return new NullLiteral(pi, c); } @Override public Object visit(ObjectCreationExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); ImmutableArray args = map(n.getArguments()); TypeReference type = requireTypeReference(n.getType()); @@ -1098,20 +1111,20 @@ public Object visit(PackageDeclaration n, Void arg) { } mapping.registerPackageName(n.getName().asString()); - var ref = translatePackageReference(n.getName()); + PackageReference ref = translatePackageReference(n.getName()); return new PackageSpecification(ref); } private ReferencePrefix translatePackageReference( com.github.javaparser.ast.expr.Expression name) { if (name.isFieldAccessExpr()) { - var fa = name.asFieldAccessExpr(); - var pen = createProgramElementName(fa.getName()); - var inner = translatePackageReference(fa.getScope()); + FieldAccessExpr fa = name.asFieldAccessExpr(); + ProgramElementName pen = createProgramElementName(fa.getName()); + ReferencePrefix inner = translatePackageReference(fa.getScope()); return new PackageReference(pen, inner); } else if (name.isNameExpr()) { - var n = name.asNameExpr(); - var pen = createProgramElementName(n.getName()); + NameExpr n = name.asNameExpr(); + ProgramElementName pen = createProgramElementName(n.getName()); return new PackageReference(pen, null); } throw new IllegalArgumentException("Unexpected expression type: " + name.getClass()); @@ -1120,15 +1133,17 @@ private ReferencePrefix translatePackageReference( @NonNull private PackageReference translatePackageReference(Name name) { // Translate recursively since PackageReference and Name are ordered differently - var pen = new ProgramElementName(name.getIdentifier(), + ProgramElementName pen = new ProgramElementName(name.getIdentifier(), createComments(name).toArray(Comment[]::new)); - var inner = name.getQualifier().map(this::translatePackageReference).orElse(null); + PackageReference inner = + name.getQualifier().map(this::translatePackageReference).orElse(null); return new PackageReference(pen, inner); } private static ReferencePrefix convertScopeToReferencePrefix(ClassOrInterfaceType scope) { - var name = scope.getName(); - var inner = scope.getScope().map(JP2KeYVisitor::convertScopeToReferencePrefix).orElse(null); + SimpleName name = scope.getName(); + ReferencePrefix inner = + scope.getScope().map(JP2KeYVisitor::convertScopeToReferencePrefix).orElse(null); return new PackageReference(createProgramElementName(name), inner); } @@ -1138,42 +1153,43 @@ private static ReferencePrefix convertScopeToReferencePrefix(ClassOrInterfaceTyp } ReferencePrefix prefix = type.getScope().map(JP2KeYVisitor::convertScopeToReferencePrefix).orElse(null); - var name = createProgramElementName(type.getName()); - var resolvedType = getKeYJavaType(type.resolve()); + ProgramElementName name = createProgramElementName(type.getName()); + KeYJavaType resolvedType = getKeYJavaType(type.resolve()); return new TypeRef(name, 0, prefix, resolvedType); } private ParameterDeclaration visitNoMap(Parameter n) { ImmutableArray modifiers = map(n.getModifiers()); - var va = n.isVarArgs(); + boolean va = n.isVarArgs(); // Var arg expects an array type later on but JP gives us "normal" type TypeReference type; if (va) { - var at = new ArrayType(n.getType()); + ArrayType at = new ArrayType(n.getType()); at.setParentNode(n); type = accept(at); } else { type = accept(n.getType()); } - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); IProgramVariable pv; if (n.getName().toString().startsWith("#")) { pv = (IProgramVariable) lookupSchemaVariable(n.getName()); } else { - var name = VariableNamer.parseName(n.getName().asString()); + ProgramElementName name = VariableNamer.parseName(n.getName().asString()); pv = new LocationVariable(name, type.getKeYJavaType(), n.isFinal()); } - var spec = new VariableSpecification(pi, c, null, pv, 0, type.getKeYJavaType()); - var isInInterface = parentIsInterface(n); + VariableSpecification spec = + new VariableSpecification(pi, c, null, pv, 0, type.getKeYJavaType()); + boolean isInInterface = parentIsInterface(n); return new ParameterDeclaration(new ImmutableArray<>(spec), pi, c, modifiers, type, isInInterface, va); } @Override public Object visit(Parameter n, Void arg) { - var param = visitNoMap(n); + ParameterDeclaration param = visitNoMap(n); return addToMapping(n, param); } @@ -1224,8 +1240,8 @@ public Object visit(UnionType n, Void arg) { @Override public Object visit(ReturnStmt n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); Expression expr = accepto(n.getExpression()); return new Return(expr, pi, c); } @@ -1237,15 +1253,15 @@ public Object visit(SingleMemberAnnotationExpr n, Void arg) { @Override public Object visit(StringLiteralExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); return new StringLiteral(pi, c, '"' + n.getValue() + '"'); } @Override public Object visit(SuperExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); // TODO ReferencePrefix path = null; return new SuperReference(path, pi, c); @@ -1253,16 +1269,16 @@ public Object visit(SuperExpr n, Void arg) { @Override public Object visit(SwitchEntry n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); ImmutableArray body = map(n.getStatements()); if (n.getLabels().isEmpty()) { // Default branch return List.of(new Default(body, pi, c)); } else { // TODO javaparser we currently multiply the branches - var result = new ArrayList(n.getLabels().size()); - for (var label : n.getLabels()) { + ArrayList result = new ArrayList(n.getLabels().size()); + for (com.github.javaparser.ast.expr.Expression label : n.getLabels()) { Expression expr = accept(label); result.add(new Case(expr, body, pi, c)); } @@ -1272,8 +1288,8 @@ public Object visit(SwitchEntry n, Void arg) { @Override public Object visit(SwitchStmt n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); Expression expr = accept(n.getSelector()); ImmutableArray branches = flatMap(n.getEntries()); return new Switch(pi, c, expr, branches); @@ -1281,8 +1297,8 @@ public Object visit(SwitchStmt n, Void arg) { @Override public Object visit(SynchronizedStmt n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); return new SynchronizedBlock(pi, c, accept(n.getExpression()), accept(n.getBody()), null, 0); } @@ -1291,7 +1307,7 @@ public Object visit(SynchronizedStmt n, Void arg) { public Object visit(ThisExpr n, Void arg) { ReferencePrefix prefix = null; if (n.typeName() != null) { - var ty = typeConverter.getKeYJavaType(n.typeName().asString()); + KeYJavaType ty = typeConverter.getKeYJavaType(n.typeName().asString()); prefix = new TypeRef(ty); } return new ThisReference(createPositionInfo(n), createComments(n), prefix); @@ -1299,22 +1315,22 @@ public Object visit(ThisExpr n, Void arg) { @Override public Object visit(ThrowStmt n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); Expression expr = accept(n.getExpression()); return new Throw(expr, pi, c); } @Override public Object visit(TryStmt n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); StatementBlock body = accept(n.getTryBlock()); ImmutableArray branches = map(n.getCatchClauses()); if (n.getFinallyBlock().isPresent()) { StatementBlock block = accept(n.getFinallyBlock().get()); - var fin = new Finally(block); - var list = branches.toList(); + Finally fin = new Finally(block); + List list = branches.toList(); list.add(fin); branches = new ImmutableArray<>(list); } @@ -1324,12 +1340,12 @@ public Object visit(TryStmt n, Void arg) { @Override public Object visit(UnaryExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); if (n.getOperator() == UnaryExpr.Operator.MINUS) { - var expr = n.getExpression(); + com.github.javaparser.ast.expr.Expression expr = n.getExpression(); if (expr instanceof IntegerLiteralExpr lit) { - var num = lit.asNumber(); + Number num = lit.asNumber(); if (num instanceof Long) { if (-num.longValue() != (long) Integer.MIN_VALUE) { return reportUnsupportedElement(n); @@ -1360,21 +1376,22 @@ public Object visit(UnknownType n, Void arg) { @Override public Object visit(VariableDeclarationExpr n, Void arg) { - var existing = mapping.nodeToKeY(n); + ProgramElement existing = mapping.nodeToKeY(n); if (existing != null) { return existing; } TypeReference type = requireTypeReference(n.getVariable(0).getType()); - var varsList = new ArrayList(n.getVariables().size()); + ArrayList varsList = + new ArrayList(n.getVariables().size()); for (VariableDeclarator v : n.getVariables()) { varsList.add(visitVariableSpecification(type, v, n)); } - var vars = new ImmutableArray<>(varsList); + ImmutableArray vars = new ImmutableArray<>(varsList); ImmutableArray modifiers = map(n.getModifiers()); - var pi = createPositionInfo(n); - var c = createComments(n); - var isInInterface = parentIsInterface(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); + boolean isInInterface = parentIsInterface(n); return addToMapping(n, new LocalVariableDeclaration(pi, c, modifiers, type, isInInterface, vars)); } @@ -1383,15 +1400,15 @@ public Object visit(VariableDeclarationExpr n, Void arg) { private VariableSpecification visitVariableSpecification(TypeReference type, VariableDeclarator v, NodeWithModifiers modifiers) { - var pi = createPositionInfo(v); - var c = createComments(v); + PositionInfo pi = createPositionInfo(v); + List c = createComments(v); Expression init = accepto(v.getInitializer()); IProgramVariable pv; KeYJavaType kjt = type.getKeYJavaType(); if (v.getNameAsString().startsWith("#")) { pv = (IProgramVariable) lookupSchemaVariable(v.getNameAsString(), v); } else { - var name = VariableNamer.parseName(v.getNameAsString()); + ProgramElementName name = VariableNamer.parseName(v.getNameAsString()); pv = new LocationVariable(name, kjt, modifiers.hasModifier(JML_GHOST), modifiers.hasModifier(FINAL)); } @@ -1412,7 +1429,7 @@ private Literal getLiteralFor(LiteralExpr lit) { return new DoubleLiteral(lit.asDoubleLiteralExpr().getValue()); } else if (lit.isIntegerLiteralExpr()) { // TODO javaparser there are only int literals in jp, not byte short int - var value = lit.asIntegerLiteralExpr().getValue(); + String value = lit.asIntegerLiteralExpr().getValue(); // TODO weigl 1L is a javaparser int literal? if (value.endsWith("L") || value.endsWith("l")) { return new LongLiteral(value); @@ -1444,11 +1461,11 @@ private Literal getCompileTimeConstantInitializer(FullVariableDeclarator spec) { return null; } - var init = spec.decl.getInitializer(); + Optional init = spec.decl.getInitializer(); if (init.isPresent()) { try { - var expr = evaluator.evaluate(init.get()); + com.github.javaparser.ast.expr.Expression expr = evaluator.evaluate(init.get()); if (expr.isLiteralExpr()) { return getLiteralFor(expr.asLiteralExpr()); } @@ -1468,12 +1485,12 @@ private ProgramVariable getProgramVariableForFieldSpecification(FullVariableDecl if (pv != null) { return pv; } - var spec = decl.decl; - var varSpec = mapping.nodeToKeY(spec); + VariableDeclarator spec = decl.decl; + ProgramElement varSpec = mapping.nodeToKeY(spec); if (varSpec == null) { - var t = spec.getType().resolve(); - var classNode = findContainingClass(spec).orElseThrow(); - var classType = new ReferenceTypeImpl(classNode.resolve()); + ResolvedType t = spec.getType().resolve(); + ClassOrInterfaceDeclaration classNode = findContainingClass(spec).orElseThrow(); + ReferenceTypeImpl classType = new ReferenceTypeImpl(classNode.resolve()); final ProgramElementName pen = new ProgramElementName(spec.getName().asString(), classNode.getFullyQualifiedName().orElseThrow()); @@ -1512,11 +1529,11 @@ private static Optional findParent(Node node, Predicate filter) { } private FieldSpecification visitFieldSpecification(FullVariableDeclarator v) { - var pi = createPositionInfo(v.decl); - var c = createComments(v.decl); + PositionInfo pi = createPositionInfo(v.decl); + List c = createComments(v.decl); Expression init = accepto(v.decl.getInitializer()); - var type = getKeYJavaType(v.decl.getType().resolve()); - var pv = getProgramVariableForFieldSpecification(v); + KeYJavaType type = getKeYJavaType(v.decl.getType().resolve()); + ProgramVariable pv = getProgramVariableForFieldSpecification(v); return new FieldSpecification(pi, c, init, pv, 0, type); } @@ -1533,8 +1550,8 @@ public Object visit(VoidType n, Void arg) { @Override public Object visit(WhileStmt n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); Guard guard = new Guard((Expression) accept(n.getCondition())); Statement body = accept(n.getBody()); List loopSpecs = new ArrayList<>(); @@ -1545,8 +1562,8 @@ public Object visit(WhileStmt n, Void arg) { @Override public Object visit(ImportDeclaration n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); if (n.isStatic()) { return reportUnsupportedElement(n); @@ -1554,7 +1571,7 @@ public Object visit(ImportDeclaration n, Void arg) { if (n.isAsterisk()) { // TODO javaparser Class.* works as well - var ref = translatePackageReference(n.getName()); + PackageReference ref = translatePackageReference(n.getName()); return new Import(ref, pi, c); } else { // TODO: is the lookup correct? Seems to work in small examples ... @@ -1567,9 +1584,9 @@ public Object visit(ImportDeclaration n, Void arg) { @Override public Object visit(Modifier n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); - var k = n.getKeyword(); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); + Modifier.Keyword k = n.getKeyword(); if (!(k instanceof Modifier.DefaultKeyword)) { reportUnsupportedElement(n); @@ -1632,14 +1649,14 @@ public Object visit(Modifier n, Void arg) { // region ccatch @Override public Object visit(KeyCcatchBreak n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); CcatchNonstandardParameterDeclaration param; if (n.getLabel().isPresent()) { if (n.getLabel().get().asString().equals("*")) { param = new CcatchBreakWildcardParameterDeclaration(pi, c); } else { - var label = nameToLabel(n.getLabel()); + Label label = nameToLabel(n.getLabel()); param = new CcatchBreakLabelParameterDeclaration(pi, c, label); } } else { @@ -1651,14 +1668,14 @@ public Object visit(KeyCcatchBreak n, Void arg) { @Override public Object visit(KeyCcatchContinue n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); CcatchNonstandardParameterDeclaration param; if (n.getLabel().isPresent()) { if (n.getLabel().get().asString().equals("*")) { param = new CcatchContinueWildcardParameterDeclaration(pi, c); } else { - var label = nameToLabel(n.getLabel()); + Label label = nameToLabel(n.getLabel()); param = new CcatchContinueLabelParameterDeclaration(pi, c, label); } } else { @@ -1670,8 +1687,8 @@ public Object visit(KeyCcatchContinue n, Void arg) { @Override public Object visit(KeyCcatchParameter n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); // reportUnsupportedElement(n); TypeReference typeRef = (TypeReference) n.getParameter().get().getType().accept(this, arg); @@ -1694,7 +1711,7 @@ public Object visit(KeyCcatchParameter n, Void arg) { @Override public Object visit(KeyCcatchReturn n, Void arg) { - var pi = createPositionInfo(n); + PositionInfo pi = createPositionInfo(n); var c = createComments(n); CcatchNonstandardParameterDeclaration param; if (n.getParameter().isPresent()) { @@ -1722,11 +1739,11 @@ public Object visit(KeyEscapeExpression n, Void arg) { public Object handleSpecialFunctionInvocation(Node n, String name, NodeList arguments) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); final var PREFIX = "\\dl_DEFAULT_VALUE_"; - final var DEFVALUE = "@defaultValue("; + final String DEFVALUE = "@defaultValue("; if (name.startsWith(PREFIX)) { // handle default value resolution String sortName = name.substring(PREFIX.length()).trim(); @@ -1736,7 +1753,7 @@ public Object handleSpecialFunctionInvocation(Node n, String name, "Requested to find the default value of an unknown sort '%s'.", sortName)); } - var doc = sort.getDocumentation(); + String doc = sort.getDocumentation(); if (doc == null) { return reportError(n, @@ -1808,7 +1825,7 @@ private ImmutableArray map( @Override public Object visit(KeyExecStatement n, Void arg) { - var pi = createPositionInfo(n); + PositionInfo pi = createPositionInfo(n); var c = createComments(n); StatementBlock body = accept(n.getExecBlock()); ImmutableArray branches = map(n.getBranches()); @@ -1817,8 +1834,8 @@ public Object visit(KeyExecStatement n, Void arg) { @Override public Object visit(KeyExecutionContext n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); TypeReference classContext = requireTypeReference(n.getContext()); ReferencePrefix runtimeInstance = accepto(n.getInstance()); IProgramMethod methodContext = @@ -1832,8 +1849,8 @@ public Object visit(KeyExecutionContext n, Void arg) { @Override public Object visit(KeyLoopScopeBlock n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); StatementBlock body = accept(n.getBlock()); IProgramVariable indexPV = accept(n.getIndexPV()); return new LoopScopeBlock(pi, c, indexPV, body, null, 0); @@ -1842,22 +1859,22 @@ public Object visit(KeyLoopScopeBlock n, Void arg) { @Override public Object visit(KeyMergePointStatement n, Void arg) { var pi = createPositionInfo(n); - var c = createComments(n); + List c = createComments(n); IProgramVariable expr = accept(n.getExpr()); return new MergePointStatement(pi, c, null, expr); } @Override public Object visit(KeyMethodBodyStatement n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); MethodReference methodReference = accept(n.getExpr()); TypeReference bodySource = requireTypeReference(n.getSource()); IProgramVariable resultVar = n.getName().map(it -> { // Get a PV where possible, then try SV - var pv = services.getNamespaces().programVariables() + IProgramVariable pv = services.getNamespaces().programVariables() .lookup(new org.key_project.logic.Name(it.getIdentifier())); if (pv != null) return pv; @@ -1868,8 +1885,8 @@ public Object visit(KeyMethodBodyStatement n, Void arg) { @Override public Object visit(KeyMethodCallStatement n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); IProgramVariable resultVar = accepto(n.getName()); StatementBlock body = accept(n.getBlock()); IExecutionContext execContext = accept(n.getContext()); @@ -1886,21 +1903,21 @@ private IProgramMethod resolveMethodSignature(KeYJavaType type, KeyMethodSignatu KeYJavaType context) { final String name = sig.getName().asString(); final ImmutableArray params = map(sig.getParamTypes()); - var paramTypes = params.stream().map(TypeReference::getKeYJavaType).toList(); + List paramTypes = params.stream().map(TypeReference::getKeYJavaType).toList(); return services.getJavaInfo().getProgramMethod(type, name, paramTypes, context); } @Override public Object visit(KeyTransactionStatement n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); return new TransactionStatement(pi, c, n.getType()); } @Override public Object visit(KeyContextStatementBlock n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); IExecutionContext execContext; if (n.getContext().isPresent()) { execContext = accepto(n.getContext()); @@ -1909,9 +1926,9 @@ public Object visit(KeyContextStatementBlock n, Void arg) { if (n.getTr().isEmpty() || n.getSignature().isEmpty()) { return reportError(n, "No context type or method signature given"); } - var signature = n.getSignature().get(); - var execPi = createPositionInfo(signature); - var execC = createComments(signature); + KeyMethodSignatureSV signature = n.getSignature().get(); + PositionInfo execPi = createPositionInfo(signature); + List execC = createComments(signature); TypeReference classContext = requireTypeReference(n.getTr().get()); ReferencePrefix runtimeInstance = accepto(n.getExpression()); IProgramMethod methodContext = accept(signature); @@ -1992,7 +2009,7 @@ yield reportError(n, ProgramSV execSV = null; ProgramSV returnSV = null; for (int i = 0; i < labels.size(); i++) { - final var sv = (OperatorSV) labels.get(i); + final OperatorSV sv = (OperatorSV) labels.get(i); if (sv.sort() == ProgramSVSort.VARIABLE) { returnSV = (ProgramSV) sv; } @@ -2031,8 +2048,8 @@ public Object visit(KeyMetaConstructType n, Void arg) { @Override public Object visit(KeyPassiveExpression n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); // TODO javaparser weigl remove after fix of // https://github.com/wadoon/key-javaparser/issues/2 n.getExpr().setParentNode(n); @@ -2067,8 +2084,8 @@ public Object visit(AnnotationMemberDeclaration n, Void arg) { @Override public Object visit(ClassExpr n, Void arg) { - var pi = createPositionInfo(n); - var c = createComments(n); + PositionInfo pi = createPositionInfo(n); + List c = createComments(n); TypeReference rt = accept(n.getType()); return new MetaClassReference(pi, c, rt); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/transformations/MarkerStatementHelper.java b/key.core/src/main/java/de/uka/ilkd/key/java/transformations/MarkerStatementHelper.java index 187cc4c4b29..6e390ca18b5 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/transformations/MarkerStatementHelper.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/transformations/MarkerStatementHelper.java @@ -4,6 +4,7 @@ package de.uka.ilkd.key.java.transformations; import de.uka.ilkd.key.nparser.KeyAst; +import de.uka.ilkd.key.speclang.jml.pretranslation.TextualJMLAssertStatement; import de.uka.ilkd.key.speclang.jml.pretranslation.TextualJMLMergePointDecl; import com.github.javaparser.ast.DataKey; @@ -20,10 +21,10 @@ public class MarkerStatementHelper { public static final int KIND_SET = 3; public static final int KIND_MERGE_POINT = 4; - public static final DataKey KEY_EXPR = new DataKey<>() { - }; public static final DataKey KEY_ASSIGN = new DataKey<>() { }; public static final DataKey KEY_MERGE_POINT = new DataKey<>() { }; + public static final DataKey KEY_ASSERT = new DataKey<>() { + }; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/transformations/pipeline/JMLTransformer.java b/key.core/src/main/java/de/uka/ilkd/key/java/transformations/pipeline/JMLTransformer.java index 1f0703571e9..53951126680 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/transformations/pipeline/JMLTransformer.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/transformations/pipeline/JMLTransformer.java @@ -229,14 +229,12 @@ public JMLTransformer(TransformationPipelineServices services) { private Statement transformAssertStatement(TextualJMLAssertStatement stat) { KeyAst.Expression ctx = stat.getContext(); - de.uka.ilkd.key.java.Position pos = ctx.getStartLocation().getPosition(); int kind = switch (stat.getKind()) { case ASSERT -> KIND_ASSERT; case ASSUME -> KIND_ASSUME; }; KeYMarkerStatement stmt = new KeYMarkerStatement(kind); - // TODO simulate/ copy token range. - stmt.setData(KEY_EXPR, ctx); + stmt.setData(KEY_ASSERT, stat); return stmt; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/transformations/pipeline/TransformationPipelineServices.java b/key.core/src/main/java/de/uka/ilkd/key/java/transformations/pipeline/TransformationPipelineServices.java index 23bc2e23d1a..623261989a6 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/transformations/pipeline/TransformationPipelineServices.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/transformations/pipeline/TransformationPipelineServices.java @@ -39,7 +39,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static de.uka.ilkd.key.java.transformations.MarkerStatementHelper.KEY_EXPR; +import static de.uka.ilkd.key.java.transformations.MarkerStatementHelper.KEY_ASSERT; + /** * @author Alexander Weigl @@ -364,14 +365,14 @@ public void attachSpec(Node node, @Nullable String spec) { LOGGER.info("Generated specification {} for {}", spec, node); } - public void attachExpr(Node node, @Nullable String spec) { + public void attachAssertion(Node node, @Nullable String spec) { if (spec == null) return; PreParser pp = getPreParser(); ImmutableList specification = pp.parseMethodLevel(spec, null, Position.UNDEFINED); TextualJMLAssertStatement expr = (TextualJMLAssertStatement) specification.get(0); - node.setData(KEY_EXPR, expr.getContext()); + node.setData(KEY_ASSERT, expr); LOGGER.info("Generated specification {} for {}", spec, node); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 8404754fda9..6f76140c864 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -11,9 +11,9 @@ import de.uka.ilkd.key.control.UserInterfaceControl; import de.uka.ilkd.key.java.JavaTools; import de.uka.ilkd.key.java.Services; -import de.uka.ilkd.key.java.SourceElement; -import de.uka.ilkd.key.java.statement.JmlAssert; -import de.uka.ilkd.key.java.statement.MethodFrame; +import de.uka.ilkd.key.java.ast.SourceElement; +import de.uka.ilkd.key.java.ast.statement.JmlAssert; +import de.uka.ilkd.key.java.ast.statement.MethodFrame; import de.uka.ilkd.key.logic.DefaultVisitor; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.JavaBlock; diff --git a/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java b/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java index 85d4ad82455..7d8ec6c1eb1 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java +++ b/key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java @@ -12,7 +12,7 @@ import java.util.List; import de.uka.ilkd.key.java.Position; -import de.uka.ilkd.key.java.abstraction.KeYJavaType; +import de.uka.ilkd.key.java.ast.abstraction.KeYJavaType; import de.uka.ilkd.key.logic.ProgramElementName; import de.uka.ilkd.key.logic.op.LocationVariable; import de.uka.ilkd.key.nparser.builder.BuilderHelpers; diff --git a/key.core/src/main/java/de/uka/ilkd/key/parser/Location.java b/key.core/src/main/java/de/uka/ilkd/key/parser/Location.java index 8eed306f16b..6fa17b9046b 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/parser/Location.java +++ b/key.core/src/main/java/de/uka/ilkd/key/parser/Location.java @@ -11,7 +11,7 @@ import java.util.Optional; import de.uka.ilkd.key.java.Position; -import de.uka.ilkd.key.java.PositionInfo; +import de.uka.ilkd.key.java.ast.PositionInfo; import de.uka.ilkd.key.util.MiscTools; import com.github.javaparser.ast.CompilationUnit; diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/init/JavaProfile.java b/key.core/src/main/java/de/uka/ilkd/key/proof/init/JavaProfile.java index 0e76f8a6ef9..397d0e8d483 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/init/JavaProfile.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/init/JavaProfile.java @@ -20,7 +20,6 @@ import de.uka.ilkd.key.rule.label.TermLabelRefactoring; import de.uka.ilkd.key.rule.merge.MergeRule; import de.uka.ilkd.key.smt.newsmt2.DefinedSymbolsHandler; -import de.uka.ilkd.key.strategy.JavaCardDLStrategyFactory; import de.uka.ilkd.key.strategy.ModularJavaDLStrategyFactory; import de.uka.ilkd.key.strategy.StrategyFactory; diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptException.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptException.java index 1093db3e436..0826d4f1fc3 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptException.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptException.java @@ -6,6 +6,8 @@ import de.uka.ilkd.key.parser.Location; import de.uka.ilkd.key.util.parsing.HasLocation; +import org.jspecify.annotations.Nullable; + public class ScriptException extends Exception implements HasLocation { diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java index 2222ebebb84..3b878df6dab 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/TermWithHoles.java @@ -80,6 +80,11 @@ public ImmutableSet extendsSorts() { public boolean extendsTrans(Sort s) { return true; } + + @Override + public boolean containsGenericSort() { + return false; + } } public static TermWithHoles fromString(EngineState engineState, String str) { @@ -116,10 +121,11 @@ public static TermWithHoles fromParserContext(EngineState state, ParseTree ctx) } private static NamespaceSet enrichState(EngineState state) { - NamespaceSet ns = state.getProof().getServices().getNamespaces().copy(); + final var services = state.getProof().getServices(); + NamespaceSet ns = services.getNamespaces().copy(); // Sort Nothing as bottom sort - NothingSort nothing = new NothingSort(state.getProof().getServices()); + NothingSort nothing = new NothingSort(services); ns.sorts().add(nothing); ns.functions().addSafely(new JFunction(HOLE_NAME, nothing)); @@ -127,8 +133,8 @@ private static NamespaceSet enrichState(EngineState state) { ns.functions().addSafely(new JFunction(FOCUS_NAME, nothing, JavaDLTheory.ANY)); ns.functions().addSafely(new JFunction(ELLIPSIS_NAME, nothing, JavaDLTheory.ANY)); GenericSort g = new GenericSort(new Name("G")); - ns.functions().addSafely(SortDependingFunction.createFirstInstance(g, HOLE_SORT_DEP_NAME, g, - new Sort[0], false)); + ns.functions().addSafely(SortDependingFunction.createFirstInstance( + g, HOLE_SORT_DEP_NAME, g, new Sort[0], false, services)); return ns; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlIO.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlIO.java index f4843a3a37b..3d4b1fca170 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlIO.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlIO.java @@ -8,6 +8,7 @@ import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.java.ast.Label; import de.uka.ilkd.key.java.ast.abstraction.KeYJavaType; +import de.uka.ilkd.key.java.ast.abstraction.Type; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.label.OriginTermLabel; import de.uka.ilkd.key.logic.op.IObserverFunction; diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java index 42238f7b713..0a9255b2773 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/TextualTranslator.java @@ -3,7 +3,6 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.speclang.njml; -import de.uka.ilkd.key.java.transformations.pipeline.JMLTransformer; import de.uka.ilkd.key.java.transformations.pipeline.JMLTransformer; import de.uka.ilkd.key.ldt.HeapLDT; import de.uka.ilkd.key.logic.label.OriginTermLabel; From 2fa2276e3494d5ddc94862e3b4cadb1c95cf098b Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Thu, 9 Apr 2026 22:04:13 +0200 Subject: [PATCH 85/90] fix tests --- .../de/uka/ilkd/key/nparser/taclets.old.txt | 11 ++++++++--- key.ui/examples/Java/Records/project.key | 3 +++ .../ilkd/key/testfixtures/GithubTestPrinter.java | 16 ++++++++-------- .../org/key_project/slicing/EndToEndTests.java | 4 ++-- 4 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 key.ui/examples/Java/Records/project.key diff --git a/key.core/src/test/resources/de/uka/ilkd/key/nparser/taclets.old.txt b/key.core/src/test/resources/de/uka/ilkd/key/nparser/taclets.old.txt index 505c99527b5..c2d4d51dbaa 100644 --- a/key.core/src/test/resources/de/uka/ilkd/key/nparser/taclets.old.txt +++ b/key.core/src/test/resources/de/uka/ilkd/key/nparser/taclets.old.txt @@ -1,5 +1,5 @@ # This files contains representation of taclets, which are accepted and revised. -# Date: Wed Nov 19 11:46:34 CET 2025 +# Date: Thu Apr 09 21:47:14 CEST 2026 == abortJavaCardTransactionAPI (abortJavaCardTransactionAPI) ========================================= abortJavaCardTransactionAPI { @@ -11641,6 +11641,12 @@ intersectionSetMinusItself_2 { \heuristics(concrete) Choices: programRules:Java} ----------------------------------------------------- +== intro (intro) ========================================= +intro { +\add [equals(t,sk)]==>[] + +Choices: true} +----------------------------------------------------- == irrflConcrete1 (irrflConcrete1) ========================================= irrflConcrete1 { \find(lt(i,i)==>) @@ -17991,8 +17997,7 @@ tryCatchThrow { #slist1 } ... }}| (post)) -\sameUpdateLevel -\add []==>[equals(#se,null)] \replacewith(#allmodal ((modal operator))|{{ .. +\sameUpdateLevel\add []==>[equals(#se,null)] \replacewith(#allmodal ((modal operator))|{{ .. if (#se instanceof #t) { #t #v0; #v0 = (#t) #se; diff --git a/key.ui/examples/Java/Records/project.key b/key.ui/examples/Java/Records/project.key new file mode 100644 index 00000000000..def675c6c38 --- /dev/null +++ b/key.ui/examples/Java/Records/project.key @@ -0,0 +1,3 @@ +\javaSource "src"; + +\chooseContract \ No newline at end of file diff --git a/key.util/src/testFixtures/java/de/uka/ilkd/key/testfixtures/GithubTestPrinter.java b/key.util/src/testFixtures/java/de/uka/ilkd/key/testfixtures/GithubTestPrinter.java index 58afda17f72..f4535ca0ab6 100644 --- a/key.util/src/testFixtures/java/de/uka/ilkd/key/testfixtures/GithubTestPrinter.java +++ b/key.util/src/testFixtures/java/de/uka/ilkd/key/testfixtures/GithubTestPrinter.java @@ -18,11 +18,11 @@ /// @author Alexander Weigl /// @version 1 (2026-02-23) public class GithubTestPrinter implements TestWatcher, BeforeAllCallback, AfterAllCallback { - private static final boolean ENABLED = "true".equals(System.getenv("CI")); + private static final boolean DISABLED = !"true".equals(System.getenv("CI")); @Override public void testAborted(ExtensionContext context, Throwable cause) { - if (ENABLED) { + if (DISABLED) { return; } System.out.format("::error title=Test aborted %s::Test %s#%s aborted due to \"%s: %s\"%n", @@ -34,7 +34,7 @@ public void testAborted(ExtensionContext context, Throwable cause) { @Override public void testDisabled(ExtensionContext context, Optional reason) { - if (ENABLED) { + if (DISABLED) { return; } System.out.format("::notice title=Disabled test %s::Test %s#%s disabled due to %s%n", @@ -46,7 +46,7 @@ public void testDisabled(ExtensionContext context, Optional reason) { @Override public void testFailed(ExtensionContext context, Throwable cause) { - if (ENABLED) { + if (DISABLED) { return; } System.out.format("::error title=Test failed %s::Test %s#%s aborted due to \"%s: %s\"%n", @@ -58,7 +58,7 @@ public void testFailed(ExtensionContext context, Throwable cause) { @Override public void testSuccessful(ExtensionContext context) { - if (ENABLED) { + if (DISABLED) { return; } System.out.format("::debug::SUCCESS:%s%n", context.getDisplayName()); @@ -67,7 +67,7 @@ public void testSuccessful(ExtensionContext context) { @Override public void beforeAll(ExtensionContext context) { - if (ENABLED) { + if (DISABLED) { return; } System.out.format("::group::%s%n", context.getDisplayName()); @@ -75,9 +75,9 @@ public void beforeAll(ExtensionContext context) { @Override public void afterAll(ExtensionContext context) { - if (ENABLED) { + if (DISABLED) { return; } - System.out.format("::endgroup::"); + System.out.format("::endgroup::%n"); } } diff --git a/keyext.slicing/src/test/java/org/key_project/slicing/EndToEndTests.java b/keyext.slicing/src/test/java/org/key_project/slicing/EndToEndTests.java index d0888752061..f17c4ca4cd8 100644 --- a/keyext.slicing/src/test/java/org/key_project/slicing/EndToEndTests.java +++ b/keyext.slicing/src/test/java/org/key_project/slicing/EndToEndTests.java @@ -116,10 +116,10 @@ void sliceMultipleIterations() throws Exception { void sliceJavaProof() throws Exception { sliceProof( "../../../../../key.ui/examples/heap/verifyThis15_2_ParallelGcd/parallelGcd.proof", - 3379, 1305, true, false).dispose(); + 2638, 976, true, false).dispose(); sliceProofOffline( "../../../../../key.ui/examples/heap/verifyThis15_2_ParallelGcd/parallelGcd.proof", - 3379, 1305, true, false).dispose(); + 2638, 976, true, false).dispose(); } /** From 678b53e0f9554b3b9205348ce35e587288bb39d1 Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Thu, 9 Apr 2026 22:35:00 +0200 Subject: [PATCH 86/90] fixes --- ...erentVarsWithSameName.MPS.cut.closed.proof | 344 ++++++++---------- key.ui/src/main/resources/logback.xml | 56 +++ 2 files changed, 206 insertions(+), 194 deletions(-) create mode 100644 key.ui/src/main/resources/logback.xml diff --git a/key.core/src/test/resources/testcase/merge/A.differentVarsWithSameName.MPS.cut.closed.proof b/key.core/src/test/resources/testcase/merge/A.differentVarsWithSameName.MPS.cut.closed.proof index f50f4424e32..5ed3c36e6c1 100644 --- a/key.core/src/test/resources/testcase/merge/A.differentVarsWithSameName.MPS.cut.closed.proof +++ b/key.core/src/test/resources/testcase/merge/A.differentVarsWithSameName.MPS.cut.closed.proof @@ -1,60 +1,97 @@ \profile "Java Profile"; -\settings { -"#Proof-Settings-Config-File -#Tue May 02 14:36:15 CEST 2017 -[StrategyProperty]VBT_PHASE=VBT_SYM_EX -[SMTSettings]useUninterpretedMultiplication=true -[SMTSettings]SelectedTaclets=\\#begboolean_equal_2\\#end,\\#begboolean_not_equal_1\\#end,\\#begboolean_not_equal_2\\#end,\\#begtrue_to_not_false\\#end,\\#begfalse_to_not_true\\#end,\\#begboolean_true_commute\\#end,\\#begboolean_false_commute\\#end,\\#begapply_eq_boolean\\#end,\\#begapply_eq_boolean_2\\#end,\\#begapply_eq_boolean_rigid\\#end,\\#begapply_eq_boolean_rigid_2\\#end,\\#begexpandInByte\\#end,\\#begexpandInChar\\#end,\\#begexpandInShort\\#end,\\#begexpandInInt\\#end,\\#begexpandInLong\\#end,\\#begreplace_byte_MAX\\#end,\\#begreplace_byte_MIN\\#end,\\#begreplace_char_MAX\\#end,\\#begreplace_char_MIN\\#end,\\#begreplace_short_MAX\\#end,\\#begreplace_short_MIN\\#end,\\#begreplace_int_MAX\\#end,\\#begreplace_int_MIN\\#end,\\#begreplace_long_MAX\\#end,\\#begreplace_long_MIN\\#end,\\#begreplace_byte_RANGE\\#end,\\#begreplace_byte_HALFRANGE\\#end,\\#begreplace_short_RANGE\\#end,\\#begreplace_short_HALFRANGE\\#end,\\#begreplace_char_RANGE\\#end,\\#begreplace_int_RANGE\\#end,\\#begreplace_int_HALFRANGE\\#end,\\#begreplace_long_RANGE\\#end,\\#begreplace_long_HALFRANGE\\#end,\\#begtranslateJavaUnaryMinusInt\\#end,\\#begtranslateJavaUnaryMinusLong\\#end,\\#begtranslateJavaBitwiseNegation\\#end,\\#begtranslateJavaAddInt\\#end,\\#begtranslateJavaAddLong\\#end,\\#begtranslateJavaSubInt\\#end,\\#begtranslateJavaSubLong\\#end,\\#begtranslateJavaMulInt\\#end,\\#begtranslateJavaMulLong\\#end,\\#begtranslateJavaMod\\#end,\\#begtranslateJavaDivInt\\#end,\\#begtranslateJavaDivLong\\#end,\\#begtranslateJavaCastByte\\#end,\\#begtranslateJavaCastShort\\#end,\\#begtranslateJavaCastInt\\#end,\\#begtranslateJavaCastLong\\#end,\\#begtranslateJavaCastChar\\#end,\\#begtranslateJavaShiftRightInt\\#end,\\#begtranslateJavaShiftRightLong\\#end,\\#begtranslateJavaShiftLeftInt\\#end,\\#begtranslateJavaShiftLeftLong\\#end,\\#begtranslateJavaUnsignedShiftRightInt\\#end,\\#begtranslateJavaUnsignedShiftRightLong\\#end,\\#begtranslateJavaBitwiseOrInt\\#end,\\#begtranslateJavaBitwiseOrLong\\#end,\\#begtranslateJavaBitwiseAndInt\\#end,\\#begtranslateJavaBitwiseAndLong\\#end,\\#begtranslateJavaBitwiseXOrInt\\#end,\\#begtranslateJavaBitwiseXOrLong\\#end,\\#begcastDel\\#end,\\#begtypeEq\\#end,\\#begtypeEqDerived\\#end,\\#begtypeEqDerived2\\#end,\\#begtypeStatic\\#end,\\#begcloseType\\#end,\\#begcloseTypeSwitched\\#end,\\#begexact_instance_definition_int\\#end,\\#begexact_instance_definition_boolean\\#end,\\#begexact_instance_definition_null\\#end,\\#begexact_instance_for_interfaces_or_abstract_classes\\#end,\\#begclass_being_initialized_is_prepared\\#end,\\#beginitialized_class_is_prepared\\#end,\\#beginitialized_class_is_not_erroneous\\#end,\\#begclass_initialized_excludes_class_init_in_progress\\#end,\\#begclass_erroneous_excludes_class_in_init\\#end,\\#begerroneous_class_has_no_initialized_sub_class\\#end,\\#begsuperclasses_of_initialized_classes_are_prepared\\#end,\\#begelementOfEmpty\\#end,\\#begelementOfAllLocs\\#end,\\#begelementOfSingleton\\#end,\\#begelementOfUnion\\#end,\\#begelementOfIntersect\\#end,\\#begelementOfSetMinus\\#end,\\#begelementOfAllFields\\#end,\\#begelementOfAllObjects\\#end,\\#begelementOfArrayRange\\#end,\\#begelementOfFreshLocs\\#end,\\#begequalityToElementOf\\#end,\\#begsubsetToElementOf\\#end,\\#begdisjointToElementOf\\#end,\\#begcreatedInHeapToElementOf\\#end,\\#begelementOfEmptyEQ\\#end,\\#begelementOfAllLocsEQ\\#end,\\#begelementOfSingletonEQ\\#end,\\#begelementOfUnionEQ\\#end,\\#begelementOfIntersectEQ\\#end,\\#begelementOfSetMinusEQ\\#end,\\#begelementOfAllFieldsEQ\\#end,\\#begelementOfAllObjectsEQ\\#end,\\#begelementOfArrayRangeEQ\\#end,\\#begelementOfFreshLocsEQ\\#end,\\#begunionWithEmpty1\\#end,\\#begunionWithEmpty2\\#end,\\#begunionWithAllLocs1\\#end,\\#begunionWithAllLocs2\\#end,\\#begintersectWithEmpty1\\#end,\\#begintersectWithEmpty2\\#end,\\#begintersectWithAllLocs1\\#end,\\#begintersectWithAllLocs2\\#end,\\#begsetMinusWithEmpty1\\#end,\\#begsetMinusWithEmpty2\\#end,\\#begsetMinusWithAllLocs\\#end,\\#begsubsetWithEmpty\\#end,\\#begsubsetWithAllLocs\\#end,\\#begdisjointWithEmpty1\\#end,\\#begdisjointWithEmpty2\\#end,\\#begcreatedInHeapWithEmpty\\#end,\\#begcreatedInHeapWithSingleton\\#end,\\#begcreatedInHeapWithUnion\\#end,\\#begcreatedInHeapWithSetMinusFreshLocs\\#end,\\#begcreatedInHeapWithAllFields\\#end,\\#begcreatedInHeapWithArrayRange\\#end,\\#begreferencedObjectIsCreatedRight\\#end,\\#begreferencedObjectIsCreatedRightEQ\\#end,\\#begunionWithItself\\#end,\\#begintersectWithItself\\#end,\\#begsetMinusItself\\#end,\\#begsubsetOfItself\\#end,\\#begselectOfStore\\#end,\\#begselectOfCreate\\#end,\\#begselectOfAnon\\#end,\\#begselectOfMemset\\#end,\\#begonlyCreatedObjectsAreReferenced\\#end,\\#begonlyCreatedObjectsAreInLocSets\\#end,\\#begonlyCreatedObjectsAreInLocSetsEQ\\#end,\\#begarrayLengthNotNegative\\#end,\\#begwellFormedStoreObject\\#end,\\#begwellFormedStoreLocSet\\#end,\\#begwellFormedStorePrimitive\\#end,\\#begwellFormedCreate\\#end,\\#begwellFormedAnon\\#end,\\#begwellFormedMemsetObject\\#end,\\#begwellFormedMemsetLocSet\\#end,\\#begwellFormedMemsetPrimitive\\#end,\\#begselectOfStoreEQ\\#end,\\#begselectOfCreateEQ\\#end,\\#begselectOfAnonEQ\\#end,\\#begselectOfMemsetEQ\\#end,\\#begmemsetEmpty\\#end,\\#begselectCreatedOfAnonEQ\\#end,\\#begwellFormedStoreObjectEQ\\#end,\\#begwellFormedStoreLocSetEQ\\#end,\\#begwellFormedStorePrimitiveEQ\\#end,\\#begwellFormedAnonEQ\\#end,\\#begwellFormedMemsetObjectEQ\\#end,\\#begwellFormedMemsetPrimitiveEQ\\#end,\\#begaccDefinition\\#end,\\#begreachDefinition\\#end,\\#begreachZero\\#end,\\#begreachOne\\#end,\\#begreachNull\\#end,\\#begreachNull2\\#end,\\#begreachAddOne\\#end,\\#begreachAddOne2\\#end,\\#begreachUniquePathSameObject\\#end,\\#begreachDependenciesStoreSimple\\#end,\\#begreachDoesNotDependOnCreatedness\\#end,\\#begreachDependenciesStore\\#end,\\#begreachDependenciesAnon\\#end,\\#begreachDependenciesAnonCoarse\\#end,\\#begonly_created_objects_are_reachable\\#end,\\#begreach_does_not_depend_on_fresh_locs\\#end,\\#begreach_does_not_depend_on_fresh_locs_EQ\\#end -[StrategyProperty]METHOD_OPTIONS_KEY=METHOD_CONTRACT -[StrategyProperty]USER_TACLETS_OPTIONS_KEY3=USER_TACLETS_OFF -[StrategyProperty]SYMBOLIC_EXECUTION_ALIAS_CHECK_OPTIONS_KEY=SYMBOLIC_EXECUTION_ALIAS_CHECK_NEVER -[StrategyProperty]LOOP_OPTIONS_KEY=LOOP_NONE -[StrategyProperty]USER_TACLETS_OPTIONS_KEY2=USER_TACLETS_OFF -[StrategyProperty]USER_TACLETS_OPTIONS_KEY1=USER_TACLETS_OFF -[StrategyProperty]QUANTIFIERS_OPTIONS_KEY=QUANTIFIERS_NON_SPLITTING_WITH_PROGS -[StrategyProperty]NON_LIN_ARITH_OPTIONS_KEY=NON_LIN_ARITH_DEF_OPS -[SMTSettings]instantiateHierarchyAssumptions=true -[StrategyProperty]AUTO_INDUCTION_OPTIONS_KEY=AUTO_INDUCTION_OFF -[StrategyProperty]DEP_OPTIONS_KEY=DEP_ON -[StrategyProperty]BLOCK_OPTIONS_KEY=BLOCK_EXPAND -[StrategyProperty]CLASS_AXIOM_OPTIONS_KEY=CLASS_AXIOM_FREE -[StrategyProperty]SYMBOLIC_EXECUTION_NON_EXECUTION_BRANCH_HIDING_OPTIONS_KEY=SYMBOLIC_EXECUTION_NON_EXECUTION_BRANCH_HIDING_OFF -[StrategyProperty]QUERY_NEW_OPTIONS_KEY=QUERY_OFF -[Strategy]Timeout=-1 -[Strategy]MaximumNumberOfAutomaticApplications=7000 -[SMTSettings]integersMaximum=2147483645 -[Choice]DefaultChoices=initialisation-initialisation\\:disableStaticInitialisation , wdChecks-wdChecks\\:off , reach-reach\\:on , moreSeqRules-moreSeqRules\\:off , sequences-sequences\\:on , Strings-Strings\\:on , mergeGenerateIsWeakeningGoal-mergeGenerateIsWeakeningGoal\\:on , runtimeExceptions-runtimeExceptions\\:ban , wdOperator-wdOperator\\:L , JavaCard-JavaCard\\:on , integerSimplificationRules-integerSimplificationRules\\:full , permissions-permissions\\:off , modelFields-modelFields\\:treatAsAxiom , assertions-assertions\\:safe , intRules-intRules\\:arithmeticSemanticsIgnoringOF , bigint-bigint\\:on , programRules-programRules\\:Java -[SMTSettings]useConstantsForBigOrSmallIntegers=true -[StrategyProperty]STOPMODE_OPTIONS_KEY=STOPMODE_DEFAULT -[StrategyProperty]QUERYAXIOM_OPTIONS_KEY=QUERYAXIOM_ON -[StrategyProperty]INF_FLOW_CHECK_PROPERTY=INF_FLOW_CHECK_FALSE -[SMTSettings]maxGenericSorts=2 -[SMTSettings]integersMinimum=-2147483645 -[SMTSettings]invariantForall=false -[SMTSettings]UseBuiltUniqueness=false -[SMTSettings]explicitTypeHierarchy=false -[Strategy]ActiveStrategy=JavaCardDLStrategy -[StrategyProperty]SPLITTING_OPTIONS_KEY=SPLITTING_DELAYED -" +\settings // Proof-Settings-Config-File +{ + "Choice" : { + "JavaCard" : "JavaCard:on", + "Strings" : "Strings:on", + "assertions" : "assertions:safe", + "bigint" : "bigint:on", + "finalFields" : "finalFields:immutable", + "floatRules" : "floatRules:strictfpOnly", + "initialisation" : "initialisation:disableStaticInitialisation", + "intRules" : "intRules:arithmeticSemanticsIgnoringOF", + "integerSimplificationRules" : "integerSimplificationRules:full", + "javaLoopTreatment" : "javaLoopTreatment:efficient", + "mergeGenerateIsWeakeningGoal" : "mergeGenerateIsWeakeningGoal:on", + "methodExpansion" : "methodExpansion:modularOnly", + "modelFields" : "modelFields:treatAsAxiom", + "moreSeqRules" : "moreSeqRules:off", + "permissions" : "permissions:off", + "programRules" : "programRules:Java", + "reach" : "reach:on", + "runtimeExceptions" : "runtimeExceptions:ban", + "sequences" : "sequences:on", + "soundDefaultContracts" : "soundDefaultContracts:on", + "wdChecks" : "wdChecks:off", + "wdOperator" : "wdOperator:L" + }, + "Labels" : { + "UseOriginLabels" : true + }, + "NewSMT" : { + + }, + "SMTSettings" : { + "SelectedTaclets" : [ + + ], + "UseBuiltUniqueness" : false, + "explicitTypeHierarchy" : false, + "instantiateHierarchyAssumptions" : true, + "integersMaximum" : 2147483645, + "integersMinimum" : -2147483645, + "invariantForall" : false, + "maxGenericSorts" : 2, + "useConstantsForBigOrSmallIntegers" : true, + "useUninterpretedMultiplication" : true + }, + "Strategy" : { + "ActiveStrategy" : "JavaCardDLStrategy", + "MaximumNumberOfAutomaticApplications" : 7000, + "Timeout" : -1, + "options" : { + "AUTO_INDUCTION_OPTIONS_KEY" : "AUTO_INDUCTION_OFF", + "BLOCK_OPTIONS_KEY" : "BLOCK_EXPAND", + "CLASS_AXIOM_OPTIONS_KEY" : "CLASS_AXIOM_FREE", + "DEP_OPTIONS_KEY" : "DEP_ON", + "INF_FLOW_CHECK_PROPERTY" : "INF_FLOW_CHECK_FALSE", + "LOOP_OPTIONS_KEY" : "LOOP_NONE", + "METHOD_OPTIONS_KEY" : "METHOD_CONTRACT", + "MPS_OPTIONS_KEY" : "MPS_MERGE", + "NON_LIN_ARITH_OPTIONS_KEY" : "NON_LIN_ARITH_DEF_OPS", + "OSS_OPTIONS_KEY" : "OSS_ON", + "QUANTIFIERS_OPTIONS_KEY" : "QUANTIFIERS_NON_SPLITTING_WITH_PROGS", + "QUERYAXIOM_OPTIONS_KEY" : "QUERYAXIOM_ON", + "QUERY_NEW_OPTIONS_KEY" : "QUERY_OFF", + "SPLITTING_OPTIONS_KEY" : "SPLITTING_DELAYED", + "STOPMODE_OPTIONS_KEY" : "STOPMODE_DEFAULT", + "SYMBOLIC_EXECUTION_ALIAS_CHECK_OPTIONS_KEY" : "SYMBOLIC_EXECUTION_ALIAS_CHECK_NEVER", + "SYMBOLIC_EXECUTION_NON_EXECUTION_BRANCH_HIDING_OPTIONS_KEY" : "SYMBOLIC_EXECUTION_NON_EXECUTION_BRANCH_HIDING_OFF", + "USER_TACLETS_OPTIONS_KEY1" : "USER_TACLETS_OFF", + "USER_TACLETS_OPTIONS_KEY2" : "USER_TACLETS_OFF", + "USER_TACLETS_OPTIONS_KEY3" : "USER_TACLETS_OFF", + "VBT_PHASE" : "VBT_SYM_EX" + } + } } + \javaSource "."; \proofObligation { - "name": "A[A::m(boolean)].JML operation contract.0", - "contract": "A[A::m(boolean)].JML operation contract.0", - "class": "de.uka.ilkd.key.proof.init.FunctionalOperationContractPO", + "class" : "de.uka.ilkd.key.proof.init.FunctionalOperationContractPO", + "contract" : "A[A::m(boolean)].JML operation contract.0", + "name" : "A[A::m(boolean)].JML operation contract.0" } \proof { -(keyLog "0" (keyUser "dscheurer" ) (keyVersion "327015b4b7182a877de25e71099a53d78919e1bb")) -(keyLog "1" (keyUser "dscheurer" ) (keyVersion "327015b4b7182a877de25e71099a53d78919e1bb")) +(keyLog "0" (keyUser "ulbrich" ) (keyVersion "ec5929d9d07ad15825a3df906130ec25331835c0")) -(autoModeTime "1166") +(autoModeTime "2175") (branch "dummy ID" -(builtin "One Step Simplification" (formula "1") (newnames "b,self,result,exc,heapAtPre,o,f")) + (builtin "One Step Simplification" (formula "1") (newnames "heapAtPre,o,f")) (rule "impRight" (formula "1")) (rule "andLeft" (formula "1")) (rule "andLeft" (formula "1")) @@ -73,10 +110,10 @@ (branch "if _b true" (builtin "One Step Simplification" (formula "7")) (builtin "One Step Simplification" (formula "1")) - (rule "compound_greater_than_comparison_1" (formula "7") (term "1") (inst "#v0=x")) + (rule "compound_greater_than_comparison_1" (formula "7") (term "1") (inst "#v0=i")) (rule "variableDeclarationAssign" (formula "7") (term "1")) - (rule "variableDeclaration" (formula "7") (term "1") (newnames "x")) - (builtin "Use Operation Contract" (formula "7") (newnames "heapBefore_f,result_0,exc_0,heapAfter_f,anon_heap_f") (contract "A[A::f()].JML operation contract.0")) + (rule "variableDeclaration" (formula "7") (term "1") (newnames "i")) + (builtin "Use Operation Contract" (formula "7") (newnames "heapBefore_f,result_f,exc_0,heapAfter_f,anon_heap_f") (contract "A[A::f()].JML operation contract.0") (modality "diamond")) (branch "Post (f)" (builtin "One Step Simplification" (formula "7")) (builtin "One Step Simplification" (formula "9")) @@ -99,9 +136,9 @@ (builtin "One Step Simplification" (formula "11")) (rule "inEqSimp_gtToGeq" (formula "11") (term "0,0,1,0")) (rule "times_zero_1" (formula "11") (term "1,0,0,0,0,1,0")) - (rule "add_literals" (formula "11") (term "0,0,0,0,1,0")) + (rule "add_zero_right" (formula "11") (term "0,0,0,0,1,0")) (rule "blockEmpty" (formula "11") (term "1")) - (builtin "MergeRule" (formula "11") (mergeProc "MergeByIfThenElse") (nrMergePartners "1") (mergeId "196")) + (builtin "MergeRule" (formula "11") (mergeProc "MergeByIfThenElse") (nrMergePartners "1") (mergeId "199")) (rule "deleteMergePoint" (formula "7") (term "1")) (rule "notLeft" (formula "1")) (rule "inEqSimp_sepPosMonomial1" (formula "7") (term "0,1,0,0,0,0")) @@ -123,6 +160,7 @@ (rule "shift_paren_or" (formula "7")) (rule "commute_or" (formula "6") (term "0")) (rule "commute_and_2" (formula "5") (term "1")) + (rule "commute_and_2" (formula "5") (term "0,0")) (rule "cnf_rightDist" (formula "5")) (rule "andLeft" (formula "5")) (rule "cnf_rightDist" (formula "6")) @@ -142,9 +180,9 @@ (rule "commute_or" (formula "7")) (rule "cnf_rightDist" (formula "6")) (rule "andLeft" (formula "6")) + (rule "commute_or" (formula "7")) (rule "cnf_rightDist" (formula "6")) (rule "andLeft" (formula "6")) - (rule "commute_or" (formula "7")) (rule "cnf_rightDist" (formula "6")) (rule "andLeft" (formula "6")) (rule "commute_or" (formula "6")) @@ -156,9 +194,9 @@ (rule "commute_or" (formula "7")) (rule "cnf_rightDist" (formula "6")) (rule "andLeft" (formula "6")) + (rule "commute_or" (formula "7")) (rule "cnf_rightDist" (formula "6")) (rule "andLeft" (formula "6")) - (rule "commute_or" (formula "7")) (rule "cnf_rightDist" (formula "6")) (rule "andLeft" (formula "6")) (rule "commute_or" (formula "6")) @@ -199,13 +237,12 @@ (rule "closeTrue" (formula "35")) ) (branch "Exceptional Post (f)" + (builtin "One Step Simplification" (formula "9")) (builtin "One Step Simplification" (formula "7")) - (builtin "One Step Simplification" (formula "9")) (rule "andLeft" (formula "7")) - (rule "selectCreatedOfAnonAsFormulaEQ" (formula "8") (term "1,0") (ifseqformula "7")) (rule "andLeft" (formula "8")) - (rule "andLeft" (formula "9")) (rule "andLeft" (formula "8")) + (rule "andLeft" (formula "10")) (rule "notLeft" (formula "8")) (rule "replace_known_right" (formula "9") (term "0") (ifseqformula "11")) (builtin "One Step Simplification" (formula "9")) @@ -214,33 +251,34 @@ (builtin "One Step Simplification" (formula "9")) (rule "andLeft" (formula "9")) (rule "blockThrow" (formula "13") (term "1")) + (rule "pullOutSelect" (formula "8") (term "0") (inst "selectSK=java_lang_Object_created__0")) + (rule "simplifySelectOfAnonEQ" (formula "8") (ifseqformula "7")) + (builtin "One Step Simplification" (formula "8") (ifInst "" (formula "12"))) + (rule "ifthenelse_negated" (formula "8") (term "0")) + (rule "applyEqRigid" (formula "8") (term "1") (ifseqformula "9")) + (rule "ifEqualsTRUE" (formula "8")) + (builtin "One Step Simplification" (formula "8")) + (rule "hideAuxiliaryEqConcrete" (formula "9")) (rule "Class_invariant_axiom_for_A" (formula "9") (ifseqformula "4")) (rule "true_left" (formula "9")) + (rule "cnf_rightDist" (formula "8")) + (builtin "One Step Simplification" (formula "8")) + (rule "commute_or" (formula "8")) (rule "methodCallParamThrow" (formula "12") (term "1")) (rule "tryCatchThrow" (formula "12") (term "1")) - (rule "ifElseUnfold" (formula "12") (term "1") (inst "#boolv=x")) - (rule "variableDeclaration" (formula "12") (term "1") (newnames "x_1")) - (rule "equality_comparison_simple" (formula "12") (term "1")) - (builtin "One Step Simplification" (formula "12")) - (rule "replace_known_right" (formula "12") (term "0,0,1,0") (ifseqformula "10")) - (builtin "One Step Simplification" (formula "12")) - (rule "ifElseSplit" (formula "12")) - (branch "if x_1 true" - (builtin "One Step Simplification" (formula "13")) - (builtin "One Step Simplification" (formula "1")) + (branch "Null reference in throw" + (builtin "One Step Simplification" (formula "1") (ifInst "" (formula "11"))) (rule "closeFalse" (formula "1")) ) - (branch "if x_1 false" - (builtin "One Step Simplification" (formula "13")) - (builtin "One Step Simplification" (formula "1")) - (rule "true_left" (formula "1")) + (branch "Normal execution" + (builtin "One Step Simplification" (formula "12") (ifInst "" (formula "10"))) + (rule "false_right" (formula "12")) (rule "ifElseSplit" (formula "12")) (branch "if exc_0 instanceof java.lang.Throwable true" (builtin "One Step Simplification" (formula "1") (ifInst "" (formula "11"))) (rule "true_left" (formula "1")) (rule "variableDeclaration" (formula "12") (term "1") (newnames "e")) (rule "delete_unnecessary_cast" (formula "12") (term "1")) - (branch "Normal Execution (exc_0 instanceof java.lang.Throwable)" (builtin "One Step Simplification" (formula "13")) (builtin "One Step Simplification" (formula "1")) (rule "true_left" (formula "1")) @@ -251,11 +289,6 @@ (rule "Class_invariant_axiom_for_A" (formula "12") (ifseqformula "4")) (rule "closeTrue" (formula "12")) ) - (branch "ClassCastException (!(exc_0 instanceof java.lang.Throwable))" - (builtin "One Step Simplification" (formula "12")) - (rule "closeTrue" (formula "12")) - ) - ) (branch "if exc_0 instanceof java.lang.Throwable false" (builtin "One Step Simplification" (formula "1") (ifInst "" (formula "11"))) (rule "closeFalse" (formula "1")) @@ -275,117 +308,47 @@ (rule "compound_inequality_comparison_1" (formula "7") (term "1") (inst "#v0=o")) (rule "variableDeclarationAssign" (formula "7") (term "1")) (rule "variableDeclaration" (formula "7") (term "1") (newnames "o")) - (builtin "Use Operation Contract" (formula "7") (newnames "heapBefore_g,result_0,exc_0,heapAfter_g,anon_heap_g") (contract "A[A::g()].JML operation contract.0")) + (builtin "Use Operation Contract" (formula "7") (newnames "heapBefore_g,result_g,exc_0,heapAfter_g,anon_heap_g") (contract "A[A::g()].JML operation contract.0") (modality "diamond")) (branch "Post (g)" + (builtin "One Step Simplification" (formula "9")) (builtin "One Step Simplification" (formula "6")) - (builtin "One Step Simplification" (formula "9")) (rule "andLeft" (formula "6")) - (rule "selectCreatedOfAnonAsFormulaEQ" (formula "7") (term "1,1,0") (ifseqformula "6")) (rule "andLeft" (formula "7")) - (rule "andLeft" (formula "8")) (rule "andLeft" (formula "7")) - (rule "typeEqDerived" (formula "10") (term "0,0,1,1") (ifseqformula "7")) - (rule "typeEqDerived" (formula "10") (term "0,1,1,1") (ifseqformula "7")) - (builtin "One Step Simplification" (formula "10") (ifInst "" (formula "7"))) - (rule "true_left" (formula "10")) - (rule "replace_known_left" (formula "9") (term "0") (ifseqformula "7")) - (builtin "One Step Simplification" (formula "9")) + (rule "typeEqDerived" (formula "9") (term "0,0,1,1,1") (ifseqformula "7")) + (rule "typeEqDerived" (formula "9") (term "0,1,1,1,1") (ifseqformula "7")) + (builtin "One Step Simplification" (formula "9") (ifInst "" (formula "7")) (ifInst "" (formula "7"))) (rule "andLeft" (formula "9")) - (rule "andLeft" (formula "10")) (rule "notLeft" (formula "9")) + (rule "andLeft" (formula "9")) + (rule "notLeft" (formula "10")) (rule "replace_known_right" (formula "8") (term "0") (ifseqformula "10")) (builtin "One Step Simplification" (formula "8")) (rule "assignment" (formula "13") (term "1")) (builtin "One Step Simplification" (formula "13")) + (rule "pullOutSelect" (formula "8") (term "0") (inst "selectSK=java_lang_Object_created__0")) + (rule "simplifySelectOfAnonEQ" (formula "8") (ifseqformula "6")) + (builtin "One Step Simplification" (formula "8") (ifInst "" (formula "11"))) + (rule "ifthenelse_negated" (formula "8") (term "0")) + (rule "applyEq" (formula "8") (term "1") (ifseqformula "9")) + (rule "ifEqualsTRUE" (formula "8")) + (builtin "One Step Simplification" (formula "8")) + (rule "hideAuxiliaryEqConcrete" (formula "9")) (rule "Class_invariant_axiom_for_A" (formula "9") (ifseqformula "3")) (rule "true_left" (formula "9")) + (rule "cnf_rightDist" (formula "8")) + (builtin "One Step Simplification" (formula "8")) + (rule "commute_or" (formula "8")) (rule "inequality_comparison_simple" (formula "12") (term "1")) (builtin "One Step Simplification" (formula "12")) (rule "replace_known_right" (formula "12") (term "0,0,1,0") (ifseqformula "9")) (builtin "One Step Simplification" (formula "12")) (rule "blockEmpty" (formula "12") (term "1")) - (builtin "CloseAfterMerge" (formula "12") (newnames "result_0_0,result_0_1,exc_0_0,exc_0_1,P") (mergeNode "196")) + (builtin "CloseAfterMerge" (formula "12") (newnames "exc_0_0,exc_0_1,P") (mergeNode "199")) (branch "Merged node is weakening" - (rule "cut" (inst "cutFormula= !self = null - & ( !b = TRUE - & ( !result_0_1 = null - & ( wellFormed(heap) - & ( boolean::select(heap, - self, - java.lang.Object::#$created) - = TRUE - & ( A::exactInstance(self) = TRUE - & ( measuredByEmpty - & ( wellFormed(anon_heap_g<>) - & ( anon(heap, - allLocs, - anon_heap_g<>) - = heapAfter_g - & ( exc_0_1 = null - & ( boolean::select(heap, - result_0_1, - java.lang.Object::#$created) - = TRUE - | boolean::select(anon_heap_g<>, - result_0_1, - java.lang.Object::#$created) - = TRUE)))))))))) --> !self = null - & wellFormed(heap) - & boolean::select(heap, - self, - java.lang.Object::#$created) - = TRUE - & A::exactInstance(self) = TRUE - & measuredByEmpty - & ( b = TRUE - & wellFormed(anon_heap_f<>) - & anon(heap, - allLocs, - anon_heap_f<>) - = heapAfter_f - & exc_0_0 = null - & geq(result_0_0, Z(1(#))) - | !b = TRUE - & !result_0_1 = null - & wellFormed(anon_heap_g<>) - & anon(heap, - allLocs, - anon_heap_g<>) - = heapAfter_g - & exc_0_1 = null - & ( boolean::select(heap, - result_0_1, - java.lang.Object::#$created) - = TRUE - | boolean::select(anon_heap_g<>, - result_0_1, - java.lang.Object::#$created) - = TRUE))") (userinteraction)) - (branch "CUT: !self = null & ( !b = TRUE & ( !result_0_1 = null & ( wellFormed(heap) & ( self.$created = TRUE & ( A::exactInstance(self) = TRUE & ( measuredByEmpty & ( wellFormed(anon_heap_g<>) & ( heap[anon(allLocs, anon_heap_g<>)] = heapAfter_g & ( exc_0_1 = null & ( result_0_1.$created = TRUE | result_0_1.$created@anon_heap_g<> = TRUE)))))))))) -> !self = null & wellFormed(heap) & self.$created = TRUE & A::exactInstance(self) = TRUE & measuredByEmpty & ( b = TRUE & wellFormed(anon_heap_f<>) & heap[anon(allLocs, anon_heap_f<>)] = heapAfter_f & exc_0_0 = null & result_0_0 >= 1 | !b = TRUE & !result_0_1 = null & wellFormed(anon_heap_g<>) & heap[anon(allLocs, anon_heap_g<>)] = heapAfter_g & exc_0_1 = null & ( result_0_1.$created = TRUE | result_0_1.$created@anon_heap_g<> = TRUE)) TRUE" - (builtin "One Step Simplification" (formula "2")) - (rule "impRight" (formula "2")) - (rule "impRight" (formula "3")) - (rule "andLeft" (formula "1")) - (rule "andLeft" (formula "2")) - (rule "notLeft" (formula "1")) - (rule "notLeft" (formula "1")) - (rule "andLeft" (formula "1")) - (rule "andLeft" (formula "2")) - (rule "notLeft" (formula "1")) - (rule "andLeft" (formula "2")) - (rule "andLeft" (formula "3")) - (rule "andLeft" (formula "4")) - (rule "andLeft" (formula "5")) - (rule "andLeft" (formula "6")) - (rule "andLeft" (formula "7")) - (rule "replace_known_left" (formula "9") (term "1,0,0,0") (ifseqformula "3")) - (builtin "One Step Simplification" (formula "9") (ifInst "" (formula "13")) (ifInst "" (formula "1")) (ifInst "" (formula "2")) (ifInst "" (formula "4")) (ifInst "" (formula "12")) (ifInst "" (formula "12")) (ifInst "" (formula "11")) (ifInst "" (formula "5")) (ifInst "" (formula "6")) (ifInst "" (formula "7")) (ifInst "" (formula "8")) (ifInst "" (formula "12")) (ifInst "" (formula "12")) (ifInst "" (formula "14"))) - (rule "closeFalse" (formula "9")) - ) - (branch "CUT: !self = null & ( !b = TRUE & ( !result_0_1 = null & ( wellFormed(heap) & ( self.$created = TRUE & ( A::exactInstance(self) = TRUE & ( measuredByEmpty & ( wellFormed(anon_heap_g<>) & ( heap[anon(allLocs, anon_heap_g<>)] = heapAfter_g & ( exc_0_1 = null & ( result_0_1.$created = TRUE | result_0_1.$created@anon_heap_g<> = TRUE)))))))))) -> !self = null & wellFormed(heap) & self.$created = TRUE & A::exactInstance(self) = TRUE & measuredByEmpty & ( b = TRUE & wellFormed(anon_heap_f<>) & heap[anon(allLocs, anon_heap_f<>)] = heapAfter_f & exc_0_0 = null & result_0_0 >= 1 | !b = TRUE & !result_0_1 = null & wellFormed(anon_heap_g<>) & heap[anon(allLocs, anon_heap_g<>)] = heapAfter_g & exc_0_1 = null & ( result_0_1.$created = TRUE | result_0_1.$created@anon_heap_g<> = TRUE)) FALSE" - (rule "hide_right" (formula "2") (userinteraction)) + (builtin "One Step Simplification" (formula "1")) (rule "impRight" (formula "1")) + (rule "impRight" (formula "2")) (rule "andLeft" (formula "1")) (rule "notLeft" (formula "1")) (rule "andLeft" (formula "1")) @@ -399,19 +362,17 @@ (rule "andLeft" (formula "5")) (rule "andLeft" (formula "6")) (rule "andLeft" (formula "7")) - (rule "replace_known_left" (formula "12") (term "1,0") (ifseqformula "4")) - (builtin "One Step Simplification" (formula "12") (ifInst "" (formula "11")) (ifInst "" (formula "1")) (ifInst "" (formula "2")) (ifInst "" (formula "3")) (ifInst "" (formula "10")) (ifInst "" (formula "10")) (ifInst "" (formula "9")) (ifInst "" (formula "5")) (ifInst "" (formula "6")) (ifInst "" (formula "7")) (ifInst "" (formula "8"))) - (rule "closeTrue" (formula "12")) + (rule "replace_known_right" (formula "9") (term "0,6,1") (ifseqformula "11")) + (builtin "One Step Simplification" (formula "9") (ifInst "" (formula "12")) (ifInst "" (formula "1")) (ifInst "" (formula "2")) (ifInst "" (formula "3")) (ifInst "" (formula "4")) (ifInst "" (formula "11")) (ifInst "" (formula "11")) (ifInst "" (formula "10")) (ifInst "" (formula "5")) (ifInst "" (formula "6")) (ifInst "" (formula "7")) (ifInst "" (formula "8")) (ifInst "" (formula "11")) (ifInst "" (formula "13"))) + (rule "closeFalse" (formula "9")) ) - ) - (branch "Merged with node 196" + (branch "Merged with node 199" ) ) (branch "Exceptional Post (g)" + (builtin "One Step Simplification" (formula "9")) (builtin "One Step Simplification" (formula "6")) - (builtin "One Step Simplification" (formula "9")) (rule "andLeft" (formula "6")) - (rule "selectCreatedOfAnonAsFormulaEQ" (formula "7") (term "1,0") (ifseqformula "6")) (rule "andLeft" (formula "7")) (rule "andLeft" (formula "7")) (rule "andLeft" (formula "9")) @@ -423,35 +384,36 @@ (builtin "One Step Simplification" (formula "8")) (rule "andLeft" (formula "8")) (rule "blockThrow" (formula "13") (term "1")) + (rule "pullOutSelect" (formula "7") (term "0") (inst "selectSK=java_lang_Object_created__0")) + (rule "simplifySelectOfAnonEQ" (formula "7") (ifseqformula "6")) + (builtin "One Step Simplification" (formula "7") (ifInst "" (formula "11"))) + (rule "ifthenelse_negated" (formula "7") (term "0")) + (rule "applyEq" (formula "7") (term "1") (ifseqformula "8")) + (rule "ifEqualsTRUE" (formula "7")) + (builtin "One Step Simplification" (formula "7")) + (rule "hideAuxiliaryEqConcrete" (formula "8")) (rule "Class_invariant_axiom_for_A" (formula "8") (ifseqformula "3")) (rule "true_left" (formula "8")) + (rule "cnf_rightDist" (formula "7")) + (builtin "One Step Simplification" (formula "7")) + (rule "commute_or" (formula "7")) (rule "methodCallParamThrow" (formula "12") (term "1")) (rule "tryCatchThrow" (formula "12") (term "1")) - (rule "ifElseUnfold" (formula "12") (term "1") (inst "#boolv=x")) - (rule "variableDeclaration" (formula "12") (term "1") (newnames "x")) - (rule "equality_comparison_simple" (formula "12") (term "1")) - (builtin "One Step Simplification" (formula "12")) - (rule "replace_known_right" (formula "12") (term "0,0,1,0") (ifseqformula "9")) - (builtin "One Step Simplification" (formula "12")) - (rule "ifElseSplit" (formula "12")) - (branch "if x true" - (builtin "One Step Simplification" (formula "13")) - (builtin "One Step Simplification" (formula "1")) + (branch "Null reference in throw" + (builtin "One Step Simplification" (formula "1") (ifInst "" (formula "10"))) (rule "closeFalse" (formula "1")) ) - (branch "if x false" - (builtin "One Step Simplification" (formula "13")) - (builtin "One Step Simplification" (formula "1")) - (rule "true_left" (formula "1")) + (branch "Normal execution" + (builtin "One Step Simplification" (formula "12") (ifInst "" (formula "9"))) + (rule "false_right" (formula "12")) (rule "ifElseSplit" (formula "12")) (branch "if exc_0 instanceof java.lang.Throwable true" (builtin "One Step Simplification" (formula "1") (ifInst "" (formula "10"))) (rule "true_left" (formula "1")) (rule "variableDeclaration" (formula "12") (term "1") (newnames "e")) (rule "delete_unnecessary_cast" (formula "12") (term "1")) - (branch "Normal Execution (exc_0 instanceof java.lang.Throwable)" + (builtin "One Step Simplification" (formula "13")) (builtin "One Step Simplification" (formula "1")) - (builtin "One Step Simplification" (formula "13")) (rule "true_left" (formula "1")) (rule "assignment" (formula "12") (term "1")) (builtin "One Step Simplification" (formula "12")) @@ -460,11 +422,6 @@ (rule "Class_invariant_axiom_for_A" (formula "12") (ifseqformula "3")) (rule "closeTrue" (formula "12")) ) - (branch "ClassCastException (!(exc_0 instanceof java.lang.Throwable))" - (builtin "One Step Simplification" (formula "12")) - (rule "closeTrue" (formula "12")) - ) - ) (branch "if exc_0 instanceof java.lang.Throwable false" (builtin "One Step Simplification" (formula "1") (ifInst "" (formula "10"))) (rule "closeFalse" (formula "1")) @@ -478,5 +435,4 @@ ) ) ) - } diff --git a/key.ui/src/main/resources/logback.xml b/key.ui/src/main/resources/logback.xml new file mode 100644 index 00000000000..f22b587756a --- /dev/null +++ b/key.ui/src/main/resources/logback.xml @@ -0,0 +1,56 @@ + + + + + + + + ${user.home}/.key/logs/key_${timestamp}.log + false + + UTF-8 + + %date|%level|%thread|%logger|%file:%line|%replace(%msg){'[\n\r]','\\n'}|%replace(%ex){'[\n\r]','\\n'}%nopex|%n + true + + + + + + [%date{HH:mm:ss.SSS}] %highlight(%-5level) %cyan(%logger{0}) - %msg%ex%n + + + + INFO + + + + + + + + + + + + [%relative] %highlight(%-5level) %cyan(%logger{0}): %msg %n + + + TRACE + + + + + + + From ce4ba7212a44194eb0f85ea8c7075cd8e8646948 Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Thu, 9 Apr 2026 16:11:19 +0200 Subject: [PATCH 87/90] remove codecov --- .github/workflows/tests.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a720ee46739..6e4a5d92e9d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -71,9 +71,6 @@ jobs: **/build/reports/ !**/jacocoTestReport.xml - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v6 - integration-tests: env: GH_TOKEN: ${{ github.token }} From 5b8d06553f25ec0354f9db3093d14d5dc705ee1a Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Sun, 12 Apr 2026 22:43:28 +0200 Subject: [PATCH 88/90] spotless --- key.core/build.gradle | 8 + .../ilkd/key/java/loader/JP2KeYConverter.java | 5 +- .../key/nparser/builder/DefaultBuilder.java | 2 +- .../de/uka/ilkd/key/scripts/EngineState.java | 79 ++++++--- .../uka/ilkd/key/scripts/ExprEvaluator.java | 163 +++++++----------- .../de/uka/ilkd/key/scripts/FocusCommand.java | 3 +- .../key/scripts/TestProofScriptCommand.java | 2 +- key.ui/examples/heap/quicksort/sort.script | 8 +- .../key/gui/WindowUserInterfaceControl.java | 11 +- 9 files changed, 148 insertions(+), 133 deletions(-) diff --git a/key.core/build.gradle b/key.core/build.gradle index 323d8417b65..902c2a22613 100644 --- a/key.core/build.gradle +++ b/key.core/build.gradle @@ -16,6 +16,10 @@ dependencies { api project(':key.util') + // Make Javadoc accessible at runtime + annotationProcessor 'com.github.therapi:therapi-runtime-javadoc-scribe:0.13.0' + implementation 'com.github.therapi:therapi-runtime-javadoc:0.13.0' + def JP_VERSION = "3.28.0-K13.5" api "org.key-project.proofjava:javaparser-core:$JP_VERSION" api "org.key-project.proofjava:javaparser-core-serialization:$JP_VERSION" @@ -31,6 +35,10 @@ dependencies { testFixturesApi(testFixtures(project(":key.util"))) } +tasks.withType(JavaCompile) { + options.compilerArgs << "-Ajavadoc.packages=de.uka.ilkd.key.scripts" +} + // The target directory for JavaCC (parser generation) //def javaCCOutputDir = layout.buildDirectory.dir("generated-src/javacc").getOrNull() //def javaCCOutputDirMain = file("$javaCCOutputDir/main") diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/loader/JP2KeYConverter.java b/key.core/src/main/java/de/uka/ilkd/key/java/loader/JP2KeYConverter.java index 841f26a0771..2f8c0d1dee6 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/loader/JP2KeYConverter.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/loader/JP2KeYConverter.java @@ -1803,6 +1803,9 @@ public Object handleSpecialFunctionInvocation(Node n, String name, case "\\seq_get" -> new SeqGet(pi, c, args.get(0), args.get(1)); case "\\seq_upd" -> new SeqPut(pi, c, args.get(0), args.get(1), args.get(2)); default -> { + if(name.startsWith("\\dl_")) { + name = name.substring(4); + } Function named = services.getNamespaces().functions() .lookup(new org.key_project.logic.Name(name)); @@ -1811,7 +1814,7 @@ public Object handleSpecialFunctionInvocation(Node n, String name, yield reportError(n, format( "In an embedded DL expression, %s is not a known DL function name.", name)); } - yield new DLEmbeddedExpression(pi, c, (JFunction) named, new ImmutableArray<>()); + yield new DLEmbeddedExpression(pi, c, (JFunction) named, args); } }; diff --git a/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/DefaultBuilder.java b/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/DefaultBuilder.java index 2f409400c19..4ecd37159ca 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/DefaultBuilder.java +++ b/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/DefaultBuilder.java @@ -322,7 +322,7 @@ public Object visitSimple_ident_dots_comma_list( @Override public String visitSimple_ident(KeYParser.Simple_identContext ctx) { - return ctx.id.getText(); + return ctx.IDENT().getText(); } @Override diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java index f5a1c350b0d..d726652ad41 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java @@ -5,28 +5,30 @@ import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Deque; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.function.Consumer; import de.uka.ilkd.key.java.Services; import de.uka.ilkd.key.logic.JTerm; import de.uka.ilkd.key.logic.NamespaceSet; +import de.uka.ilkd.key.nparser.KeYParser.ProofScriptExpressionContext; import de.uka.ilkd.key.nparser.KeyIO; import de.uka.ilkd.key.parser.ParserException; import de.uka.ilkd.key.pp.AbbrevMap; import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Node; import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.scripts.meta.ConversionException; +import de.uka.ilkd.key.scripts.meta.Converter; +import de.uka.ilkd.key.scripts.meta.NoSpecifiedConverterException; import de.uka.ilkd.key.scripts.meta.ValueInjector; import de.uka.ilkd.key.settings.ProofSettings; import org.key_project.logic.sort.Sort; +import org.key_project.prover.sequent.Semisequent; import org.key_project.prover.sequent.Sequent; import org.key_project.util.collection.ImmutableList; +import org.key_project.util.java.StringUtil; import org.antlr.v4.runtime.CharStreams; import org.jspecify.annotations.NonNull; @@ -48,6 +50,7 @@ public class EngineState { private final AbbrevMap abbrevMap = new AbbrevMap(); private final ValueInjector valueInjector = createDefaultValueInjector(); + private final ExprEvaluator exprEvaluator = new ExprEvaluator(this); private @Nullable Consumer observer; private Path baseFileName = Paths.get("."); @@ -55,7 +58,7 @@ public class EngineState { private @Nullable Goal goal; private @Nullable Node lastSetGoalNode; - private final HashMap userData = new HashMap<>(); + private final Map userData = new HashMap<>(); /** * If set to true, outputs all commands to observers and console. Otherwise, only shows explicit @@ -77,28 +80,60 @@ public EngineState(Proof proof, ProofScriptEngine engine) { /// add converters for types used in proof scripts, /// add support for parse trees private ValueInjector createDefaultValueInjector() { - ValueInjector v = ValueInjector.createDefault(); - - // from string to ... + var v = ValueInjector.createDefault(); v.addConverter(JTerm.class, String.class, (str) -> this.toTerm(str, null)); v.addConverter(Sequent.class, String.class, this::toSequent); v.addConverter(Sort.class, String.class, this::toSort); - // to terms with holes - v.addConverter(TermWithHoles.class, String.class, - str -> TermWithHoles.fromString(this, str)); - - // to sequents with holes - v.addConverter(SequentWithHoles.class, String.class, - str -> SequentWithHoles.fromString(this, str)); - - // from KeY parse tree to everything - ExprEvaluator exprEvaluator = new ExprEvaluator(this); - exprEvaluator.addConvertersToValueInjector(v); + addContextTranslator(v, String.class); + addContextTranslator(v, JTerm.class); + addContextTranslator(v, Integer.class); + addContextTranslator(v, Byte.class); + addContextTranslator(v, Long.class); + addContextTranslator(v, Boolean.class); + addContextTranslator(v, Character.class); + addContextTranslator(v, Sequent.class); + addContextTranslator(v, Integer.TYPE); + addContextTranslator(v, Byte.TYPE); + addContextTranslator(v, Long.TYPE); + addContextTranslator(v, Boolean.TYPE); + addContextTranslator(v, Character.TYPE); + addContextTranslator(v, JTerm.class); + addContextTranslator(v, Sequent.class); + addContextTranslator(v, Semisequent.class); + addContextTranslator(v, ScriptBlock.class); return v; } + private void addContextTranslator(ValueInjector v, Class aClass) { + Converter converter = + (ProofScriptExpressionContext a) -> convertToString(v, aClass, a); + v.addConverter(aClass, ProofScriptExpressionContext.class, converter); + } + @SuppressWarnings("unchecked") + private R convertToString(ValueInjector inj, Class aClass, + ProofScriptExpressionContext ctx) + throws Exception { + try { + if (aClass == String.class && ctx.string_literal() != null) { + return inj.getConverter(aClass, String.class) + .convert(StringUtil.trim(ctx.string_literal().getText(), '"')); + } + if (aClass == String.class) { + return inj.getConverter(aClass, String.class).convert(ctx.getText()); + } + + T value = (T) ctx.accept(exprEvaluator); + Class tClass = (Class) value.getClass(); + if (aClass.isAssignableFrom(value.getClass())) { + return aClass.cast(value); + } + return inj.getConverter(aClass, tClass).convert(value); + } catch (ConversionException | NoSpecifiedConverterException e) { + return inj.getConverter(aClass, String.class).convert(ctx.getText()); + } + } protected static Goal getGoal(ImmutableList openGoals, Node node) { for (Goal goal : openGoals) { @@ -325,6 +360,10 @@ public NamespaceSet getCurrentNamespaces() { } } + ExprEvaluator getEvaluator() { + return exprEvaluator; + } + public void putUserData(String key, @Nullable Object val) { userData.put(key, val); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java index 4d380292023..4862f59dcad 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java @@ -5,27 +5,23 @@ import java.net.URI; -import de.uka.ilkd.key.logic.JTerm; +import de.uka.ilkd.key.nparser.KeYParser; import de.uka.ilkd.key.nparser.KeYParser.*; +import de.uka.ilkd.key.nparser.KeYParserBaseVisitor; import de.uka.ilkd.key.nparser.KeyAst; import de.uka.ilkd.key.nparser.builder.ExpressionBuilder; -import de.uka.ilkd.key.proof.calculus.JavaDLSequentKit; -import de.uka.ilkd.key.scripts.meta.ConversionException; -import de.uka.ilkd.key.scripts.meta.ValueInjector; -import de.uka.ilkd.key.util.ANTLRUtil; import org.key_project.prover.sequent.Sequent; -import org.key_project.prover.sequent.SequentFormula; -import org.key_project.util.collection.ImmutableList; -import org.key_project.util.java.StringUtil; import org.antlr.v4.runtime.ParserRuleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.key_project.util.java.StringUtil.trim; /// Evaluates expression inside of proof script to their appropriate type. /// +/// - [JmlParser.ExpressionContext]: [Term] /// - [SeqContext]: [Sequent] /// - [Boolean_literalContext]: [Boolean] /// - [IntegerContext]: [Integer] @@ -35,7 +31,7 @@ /// @author Alexander Weigl /// @version 1 (18.01.25) /// @see de.uka.ilkd.key.nparser.KeYParser.ProofScriptExpressionContext -class ExprEvaluator { +class ExprEvaluator extends KeYParserBaseVisitor { private static final Logger LOGGER = LoggerFactory.getLogger(ExprEvaluator.class); private final EngineState state; @@ -43,113 +39,78 @@ class ExprEvaluator { this.state = engineState; } - private Object evaluateExpression(ParserRuleContext ctx) { - var expressionBuilder = - new ExpressionBuilder(state.getProof().getServices(), state.getCurrentNamespaces()); - expressionBuilder.setAbbrevMap(state.getAbbreviations()); - var t = ctx.accept(expressionBuilder); - var warnings = expressionBuilder.getBuildingIssues(); - warnings.forEach(it -> LOGGER.warn("{}", it)); - warnings.clear(); - return t; + @Override + public Object visitProofScriptCodeBlock(ProofScriptCodeBlockContext ctx) { + URI uri = KeyAst.ProofScript.getUri(ctx.start); + return KeyAst.ProofScript.asAst(uri, ctx); } - public void addConvertersToValueInjector(ValueInjector v) { - v.addConverter(String.class, ProofScriptExpressionContext.class, this::convertToString); - v.addConverter(Boolean.class, ProofScriptExpressionContext.class, this::convertToBoolean); - v.addConverter(boolean.class, ProofScriptExpressionContext.class, this::convertToBoolean); - v.addConverter(Integer.class, ProofScriptExpressionContext.class, this::convertToInteger); - v.addConverter(int.class, ProofScriptExpressionContext.class, this::convertToInteger); - v.addConverter(JTerm.class, ProofScriptExpressionContext.class, this::convertToTerm); - v.addConverter(Sequent.class, ProofScriptExpressionContext.class, this::convertToSequent); - v.addConverter(TermWithHoles.class, ProofScriptExpressionContext.class, - ctx -> TermWithHoles.fromProofScriptExpression(state, ctx)); - v.addConverter(SequentWithHoles.class, ProofScriptExpressionContext.class, - ctx -> SequentWithHoles.fromParserContext(state, ctx)); - v.addConverter(ScriptBlock.class, ProofScriptExpressionContext.class, - this::convertToScriptBlock); + @Override + public Object visitBoolean_literal(Boolean_literalContext ctx) { + return Boolean.parseBoolean(ctx.getText()); + } + @Override + public Object visitChar_literal(KeYParser.Char_literalContext ctx) { + return ctx.getText().charAt(1); // skip "'" } - private ScriptBlock convertToScriptBlock(ProofScriptExpressionContext ctx) - throws ConversionException { - if (ctx.proofScriptCodeBlock() == null) { - throw new ConversionException( - "Need a script block here, not: " + ANTLRUtil.reconstructOriginal(ctx)); - } - URI uri = KeyAst.ProofScript.getUri(ctx.start); - return KeyAst.ProofScript.asAst(uri, ctx.proofScriptCodeBlock()); + @Override + public Object visitInteger(IntegerContext ctx) { + return Integer.parseInt(ctx.getText()); + } + + @Override + public Object visitFloatLiteral(KeYParser.FloatLiteralContext ctx) { + return Float.parseFloat(ctx.getText()); + } + + @Override + public Object visitDoubleLiteral(DoubleLiteralContext ctx) { + return Double.parseDouble(ctx.getText()); } - private JTerm convertToTerm(ProofScriptExpressionContext ctx) throws ConversionException { - try { - if (ctx.string_literal() != null) { - String text = StringUtil.trim(ctx.string_literal().getText(), '"'); - return state.toTerm(text, null); - } else if (ctx.proofScriptCodeBlock() != null) { - throw new ConversionException("A block cannot be used as a term"); - } else { - return (JTerm) evaluateExpression( - (ParserRuleContext) ctx.getChild(ParserRuleContext.class, 0)); - } - } catch (Exception e) { - throw new ConversionException( - "Cannot convert expression to term: " + ANTLRUtil.reconstructOriginal(ctx), e); - } + @Override + public String visitString_literal(String_literalContext ctx) { + return trim(ctx.getText(), '"'); } - private Sequent convertToSequent(ProofScriptExpressionContext ctx) throws ConversionException { - try { - if (ctx.string_literal() != null) { - String text = StringUtil.trim(ctx.string_literal().getText(), '"'); - return state.toSequent(text); - } else if (ctx.proofScriptCodeBlock() != null) { - throw new ConversionException("A block cannot be used as a sequent"); - } else if (ctx.seq() != null) { - return (Sequent) evaluateExpression(ctx.seq()); - } else { - JTerm term = (JTerm) evaluateExpression((ParserRuleContext) ctx.getChild(0)); - return JavaDLSequentKit - .createSuccSequent(ImmutableList.of(new SequentFormula(term))); - } - } catch (Exception e) { - throw new ConversionException( - "Cannot convert expression to sequent: " + ANTLRUtil.reconstructOriginal(ctx)); - } + @Override + public Sequent visitSeq(SeqContext ctx) { + var expressionBuilder = + new ExpressionBuilder(state.getProof().getServices(), state.getCurrentNamespaces()); + expressionBuilder.setAbbrevMap(state.getAbbreviations()); + var t = (Sequent) ctx.accept(expressionBuilder); + var warnings = expressionBuilder.getBuildingIssues(); + warnings.forEach(it -> LOGGER.warn("{}", it)); + warnings.clear(); + return t; + } - private Boolean convertToBoolean(ProofScriptExpressionContext ctx) throws ConversionException { - if (ctx.boolean_literal() != null) { - return Boolean.parseBoolean(ctx.boolean_literal().getText()); - } else if (ctx.string_literal() != null) { - String text = StringUtil.trim(ctx.string_literal().getText(), '"'); - return Boolean.parseBoolean(text); - } else { - throw new ConversionException( - "Cannot convert expression to boolean: " + ANTLRUtil.reconstructOriginal(ctx)); - } + @Override + public Object visitSimple_ident(Simple_identContext ctx) { + return evaluateExpression(ctx); } - private Integer convertToInteger(ProofScriptExpressionContext proofScriptExpressionContext) - throws ConversionException { - if (proofScriptExpressionContext.integer() != null) { - return Integer.parseInt(proofScriptExpressionContext.integer().getText()); - } else if (proofScriptExpressionContext.string_literal() != null) { - String text = - StringUtil.trim(proofScriptExpressionContext.string_literal().getText(), '"'); - return Integer.parseInt(text); - } else { - throw new ConversionException("Cannot convert expression to integer: " - + ANTLRUtil.reconstructOriginal(proofScriptExpressionContext)); - } + @Override + public Object visitTerm(KeYParser.TermContext ctx) { + return evaluateExpression(ctx); } - private String convertToString(ProofScriptExpressionContext ctx) { - if (ctx.string_literal() != null) { - return StringUtil.trim(ctx.string_literal().getText(), '"'); - } else { - return ANTLRUtil.reconstructOriginal(ctx).trim(); - } + private Object evaluateExpression(ParserRuleContext ctx) { + var expressionBuilder = + new ExpressionBuilder(state.getProof().getServices(), state.getCurrentNamespaces()); + expressionBuilder.setAbbrevMap(state.getAbbreviations()); + var t = ctx.accept(expressionBuilder); + var warnings = expressionBuilder.getBuildingIssues(); + warnings.forEach(it -> LOGGER.warn("{}", it)); + warnings.clear(); + return t; } + @Override + protected Object aggregateResult(Object aggregate, Object nextResult) { + return nextResult == null ? aggregate : nextResult; + } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java index e8fd5cc9c81..72904c411da 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java @@ -20,6 +20,7 @@ import org.key_project.logic.PosInTerm; import org.key_project.logic.op.sv.SchemaVariable; import org.key_project.prover.sequent.PosInOccurrence; +import org.key_project.prover.sequent.Sequent; import org.key_project.prover.sequent.SequentFormula; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -68,7 +69,7 @@ static class Parameters { @Override public void execute(ScriptCommandAst args) throws ScriptException, InterruptedException { - Parameters s = state().getValueInjector().inject(new Parameters(), args); + var s = state().getValueInjector().inject(new Parameters(), args); hideAll(s.toKeep); } diff --git a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java index 4bf11721adc..d685497a47d 100644 --- a/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java +++ b/key.core/src/test/java/de/uka/ilkd/key/scripts/TestProofScriptCommand.java @@ -98,7 +98,7 @@ public static List data() throws IOException, URISyntaxException { @MethodSource("data") void testProofScript(TestInstance data, String name) throws Exception { Path tmpKey = Files.createTempFile("proofscript_key_" + name, ".key"); - LOGGER.info("Testing {} using file", name, tmpKey); + LOGGER.info("Testing {} using file {}", name, tmpKey); Files.writeString(tmpKey, data.key()); KeYEnvironment env = KeYEnvironment.load(tmpKey); diff --git a/key.ui/examples/heap/quicksort/sort.script b/key.ui/examples/heap/quicksort/sort.script index c2f8153fa2d..49017900c51 100644 --- a/key.ui/examples/heap/quicksort/sort.script +++ b/key.ui/examples/heap/quicksort/sort.script @@ -1,10 +1,12 @@ macro "autopilot-prep"; +tryclose; +// assert openGoals == 1; // proof obligation for seqPerm after 3 method calls -select formula="{heapAtPre:=heap || exc:=null || heap:=heapAfter_sort_0} - seqPerm(seqDef{int u;}(0, array.length, any::select(heap, array, arr(u))), - seqDef{int u;}(0, array.length, any::select(heapAtPre, array, arr(u))))"; +// select formula="{heapAtPre:=heap || exc:=null || heap:=heapAfter_sort_0} +// seqPerm(seqDef{int u;}(0, array.length, any::select(heap, array, arr(u))), +// seqDef{int u;}(0, array.length, any::select(heapAtPre, array, arr(u))))"; macro "simp-upd"; diff --git a/key.ui/src/main/java/de/uka/ilkd/key/gui/WindowUserInterfaceControl.java b/key.ui/src/main/java/de/uka/ilkd/key/gui/WindowUserInterfaceControl.java index 3b530ba32c0..73702df7c4d 100644 --- a/key.ui/src/main/java/de/uka/ilkd/key/gui/WindowUserInterfaceControl.java +++ b/key.ui/src/main/java/de/uka/ilkd/key/gui/WindowUserInterfaceControl.java @@ -5,10 +5,7 @@ import java.io.File; import java.nio.file.Path; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.Properties; +import java.util.*; import java.util.function.Consumer; import java.util.stream.Collectors; import javax.swing.*; @@ -188,7 +185,11 @@ private void taskFinishedInternal(TaskFinishedInfo info) { && mainWindow.getMediator().getSelectedProof() == proof) { Goal g = result.nonCloseableGoal(); if (g == null) { - g = proof.openGoals().head(); + try { + g = proof.openGoals().head(); + }catch (NoSuchElementException e) { + //all closed + } } mainWindow.getMediator().goalChosen(g); if (inStopAtFirstUncloseableGoalMode(proof)) { From 331b533d6460f2b9bb7700d7fdaf441eb46f756d Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Fri, 17 Apr 2026 16:41:45 +0200 Subject: [PATCH 89/90] add converter --- .../java/de/uka/ilkd/key/java/loader/JP2KeYConverter.java | 2 +- .../java/de/uka/ilkd/key/nparser/builder/DefaultBuilder.java | 2 +- .../src/main/java/de/uka/ilkd/key/scripts/EngineState.java | 5 +++++ .../src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java | 1 - .../java/de/uka/ilkd/key/gui/WindowUserInterfaceControl.java | 4 ++-- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/loader/JP2KeYConverter.java b/key.core/src/main/java/de/uka/ilkd/key/java/loader/JP2KeYConverter.java index 2f8c0d1dee6..4b0e7945387 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/loader/JP2KeYConverter.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/loader/JP2KeYConverter.java @@ -1803,7 +1803,7 @@ public Object handleSpecialFunctionInvocation(Node n, String name, case "\\seq_get" -> new SeqGet(pi, c, args.get(0), args.get(1)); case "\\seq_upd" -> new SeqPut(pi, c, args.get(0), args.get(1), args.get(2)); default -> { - if(name.startsWith("\\dl_")) { + if (name.startsWith("\\dl_")) { name = name.substring(4); } Function named = diff --git a/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/DefaultBuilder.java b/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/DefaultBuilder.java index 4ecd37159ca..77c1420cb19 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/DefaultBuilder.java +++ b/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/DefaultBuilder.java @@ -322,7 +322,7 @@ public Object visitSimple_ident_dots_comma_list( @Override public String visitSimple_ident(KeYParser.Simple_identContext ctx) { - return ctx.IDENT().getText(); + return ctx.getText(); } @Override diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java index d726652ad41..ebc0ea9f457 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java @@ -84,6 +84,11 @@ private ValueInjector createDefaultValueInjector() { v.addConverter(JTerm.class, String.class, (str) -> this.toTerm(str, null)); v.addConverter(Sequent.class, String.class, this::toSequent); v.addConverter(Sort.class, String.class, this::toSort); + v.addConverter(TermWithHoles.class, ProofScriptExpressionContext.class, + it -> TermWithHoles.fromParserContext(this, it)); + + v.addConverter(SequentWithHoles.class, ProofScriptExpressionContext.class, + it -> SequentWithHoles.fromParserContext(this, it)); addContextTranslator(v, String.class); addContextTranslator(v, JTerm.class); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java index 72904c411da..abe4443fcb4 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/FocusCommand.java @@ -20,7 +20,6 @@ import org.key_project.logic.PosInTerm; import org.key_project.logic.op.sv.SchemaVariable; import org.key_project.prover.sequent.PosInOccurrence; -import org.key_project.prover.sequent.Sequent; import org.key_project.prover.sequent.SequentFormula; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; diff --git a/key.ui/src/main/java/de/uka/ilkd/key/gui/WindowUserInterfaceControl.java b/key.ui/src/main/java/de/uka/ilkd/key/gui/WindowUserInterfaceControl.java index 73702df7c4d..90f575ec46b 100644 --- a/key.ui/src/main/java/de/uka/ilkd/key/gui/WindowUserInterfaceControl.java +++ b/key.ui/src/main/java/de/uka/ilkd/key/gui/WindowUserInterfaceControl.java @@ -187,8 +187,8 @@ private void taskFinishedInternal(TaskFinishedInfo info) { if (g == null) { try { g = proof.openGoals().head(); - }catch (NoSuchElementException e) { - //all closed + } catch (NoSuchElementException e) { + // all closed } } mainWindow.getMediator().goalChosen(g); From 0e8c008282892fe99fb44cadae113a427771ec3e Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Sun, 19 Apr 2026 11:55:48 +0200 Subject: [PATCH 90/90] make userData type safe --- .../ilkd/key/macros/ApplyScriptsMacro.java | 7 +- .../uka/ilkd/key/scripts/BranchesCommand.java | 8 +- .../de/uka/ilkd/key/scripts/EngineState.java | 14 ++- .../uka/ilkd/key/scripts/ObtainCommand.java | 4 +- .../de/uka/ilkd/key/scripts/SetCommand.java | 26 +++--- .../org/key_project/util/lookup/PLookup.java | 87 +++++++++++++++++++ .../org/key_project/util/lookup/Property.java | 9 ++ 7 files changed, 124 insertions(+), 31 deletions(-) create mode 100644 key.util/src/main/java/org/key_project/util/lookup/PLookup.java create mode 100644 key.util/src/main/java/org/key_project/util/lookup/Property.java diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java index 6f76140c864..1ead0f9ec27 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ApplyScriptsMacro.java @@ -47,12 +47,14 @@ import org.antlr.v4.runtime.ParserRuleContext; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; +import org.key_project.util.lookup.Property; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ApplyScriptsMacro extends AbstractProofMacro { - private static final Logger LOGGER = LoggerFactory.getLogger(ApplyScriptsMacro.class); + public static final Property> USER_DATA_JML_OBTAIN_VAR_MAP + = new Property<>("jml.obtainVarMap"); private final @Nullable ProofMacro fallBackMacro; @@ -187,13 +189,12 @@ public ProofMacroFinishedInfo applyTo(UserInterfaceControl uic, Proof proof, // raise an NPE. Map obtainMap = makeObtainVarMap(jmlAssert.collectVariablesInProof(null)); - @Nullable OpReplacer updateReplacer = getUpdateReplacer(goal); List renderedProof = renderProof(proofScript, termMap, updateReplacer, proof.getServices()); ProofScriptEngine pse = new ProofScriptEngine(proof); pse.setInitiallySelectedGoal(goal); - pse.getStateMap().putUserData("jml.obtainVarMap", obtainMap); + pse.getStateMap().getUserData().set(USER_DATA_JML_OBTAIN_VAR_MAP, obtainMap); pse.getStateMap().getValueInjector().addConverter(JTerm.class, ObtainAwareTerm.class, oat -> oat.resolve(obtainMap, goal.proof().getServices())); // TODO: Perhaps have holes also in JML? diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java index 2bc0a829b88..7cae5511d85 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/BranchesCommand.java @@ -22,8 +22,10 @@ import org.key_project.util.collection.ImmutableList; import org.jspecify.annotations.Nullable; +import org.key_project.util.lookup.Property; public class BranchesCommand extends AbstractCommand { + private static final Property> USER_DATA_BRANCH_STACK = new Property<>("BRANCH_STACK"); public BranchesCommand() { super(Parameters.class); @@ -38,11 +40,7 @@ public String getName() { public void execute(ScriptCommandAst arguments) throws ScriptException, InterruptedException { var args = state().getValueInjector().inject(new BranchesCommand.Parameters(), arguments); - Stack stack = (Stack) state.getUserData("_branchStack"); - if (stack == null) { - stack = new Stack<>(); - state.putUserData("_branchStack", stack); - } + Stack stack = state.getUserData().putIfAbsent(USER_DATA_BRANCH_STACK, Stack::new); if (args.mode == null) { throw new ScriptException("For 'branches', a mode must be specified"); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java index ebc0ea9f457..65a9f18339a 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java @@ -34,6 +34,8 @@ import org.jspecify.annotations.NonNull; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; +import org.key_project.util.lookup.Lookup; +import org.key_project.util.lookup.PLookup; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,7 +44,7 @@ * @version 1 (28.03.17) */ @NullMarked -public class EngineState { +public class EngineState { public static final Logger LOGGER = LoggerFactory.getLogger(EngineState.class); private final Proof proof; @@ -58,7 +60,7 @@ public class EngineState { private @Nullable Goal goal; private @Nullable Node lastSetGoalNode; - private final Map userData = new HashMap<>(); + private final PLookup userData = new PLookup(); /** * If set to true, outputs all commands to observers and console. Otherwise, only shows explicit @@ -369,11 +371,7 @@ ExprEvaluator getEvaluator() { return exprEvaluator; } - public void putUserData(String key, @Nullable Object val) { - userData.put(key, val); - } - - public @Nullable Object getUserData(String key) { - return userData.get(key); + public PLookup getUserData() { + return userData; } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/ObtainCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/ObtainCommand.java index 7e4a0b6d3aa..d467557ddf9 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/ObtainCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/ObtainCommand.java @@ -26,6 +26,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.jspecify.annotations.Nullable; +import static de.uka.ilkd.key.macros.ApplyScriptsMacro.USER_DATA_JML_OBTAIN_VAR_MAP; + /** * Command that applies a calculus rule All parameters are passed as strings and converted by the * command. The parameters are: @@ -57,7 +59,7 @@ public void execute(ScriptCommandAst ast) throws ScriptException, InterruptedException { var args = state().getValueInjector().inject(new Parameters(), ast); - var obtainMap = (Map) state().getUserData("jml.obtainVarMap"); + var obtainMap = state().getUserData().get(USER_DATA_JML_OBTAIN_VAR_MAP); if (obtainMap == null) { throw new ScriptException( "No obtain variable map found. This command must be used within a JML proof."); diff --git a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java index addd3beead8..42f9d08d75a 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java +++ b/key.core/src/main/java/de/uka/ilkd/key/scripts/SetCommand.java @@ -4,10 +4,6 @@ package de.uka.ilkd.key.scripts; -import java.util.HashMap; -import java.util.Map; -import java.util.Stack; - import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.proof.init.Profile; @@ -19,10 +15,17 @@ import de.uka.ilkd.key.strategy.Strategy; import de.uka.ilkd.key.strategy.StrategyFactory; import de.uka.ilkd.key.strategy.StrategyProperties; - import org.jspecify.annotations.Nullable; +import org.key_project.util.lookup.Property; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; public class SetCommand extends AbstractCommand { + public static final Property> USER_DATA_SETTINGS_STACK + = new Property<>("settingsStack"); + public SetCommand() { super(Parameters.class); } @@ -58,19 +61,14 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup } if (args.stackAction != null) { - Stack stack = - (Stack) state.getUserData("settingsStack"); - if (stack == null) { - stack = new Stack<>(); - state.putUserData("settingsStack", stack); - } + ArrayList stack = state.getUserData().putIfAbsent(USER_DATA_SETTINGS_STACK, ArrayList::new); switch (args.stackAction) { case "push": - stack.push(newProps.clone()); + stack.addLast(newProps.clone()); break; case "pop": // TODO sensible error if empty - var resetProps = stack.pop(); + var resetProps = stack.removeLast(); updateStrategySettings(state, resetProps); break; default: @@ -82,7 +80,7 @@ public void execute(ScriptCommandAst arguments) throws ScriptException, Interrup throw new IllegalArgumentException( "userData must be of the form key:value. Use userData:\"myKey:myValue\"."); } - state.putUserData("user." + kv[0], kv[1]); + state.getUserData().set(new Property<>("user." + kv[0]), kv[1]); } else { throw new IllegalArgumentException( "You have to set oss, steps, stack, or key(s) and value(s)."); diff --git a/key.util/src/main/java/org/key_project/util/lookup/PLookup.java b/key.util/src/main/java/org/key_project/util/lookup/PLookup.java new file mode 100644 index 00000000000..3233b38defd --- /dev/null +++ b/key.util/src/main/java/org/key_project/util/lookup/PLookup.java @@ -0,0 +1,87 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package org.key_project.util.lookup; + +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +/// This class handles the management of services and implementations. +/// +/// This class is a flexible alternative for a mediator. You can register and deregister +/// implementation for services. And also you can lookup them up. Multiple implementations are +/// possible; also notification on service change. +/// +/// [PLookup] can be arranged hierarchical, incl. support for notification. +/// +/// @author Alexander Weigl +/// @version 1 (15.03.19) +@NullMarked +public class PLookup { + private final Map, Object> serviceMap = new HashMap<>(); + private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this); + + /// Get the current value for the given \`key\`. + /// + /// @param key the property to be looked-uped + /// @param defaultValue the defaultValue return + @SuppressWarnings("unchecked") + public T get(Property key, @Nullable T defaultValue) { + return (T) serviceMap.getOrDefault(key, defaultValue); + } + + @SuppressWarnings("unchecked") + public @Nullable T get(Property key) { + return (T) serviceMap.get(key); + } + + @SuppressWarnings("unchecked") + public T putIfAbsent(Property key, Supplier value) { + return (T) serviceMap.computeIfAbsent(key, (k) -> value.get()); + } + + + /// Sets the given `key` to the `value`. + /// + /// @param key the property + /// @param value the new value to assigned to key + /// @return the previous value assigned to `value`; possible null. + @SuppressWarnings("unchecked") + public T set(Property key, T value) { + var old = serviceMap.put(key, value); + changeSupport.firePropertyChange(key.name(), old, value); + return (T) old; + } + + public Object remove(Property key) { + var old = serviceMap.remove(key); + changeSupport.firePropertyChange(key.name(), old, null); + return old; + } + + public void dispose() { + serviceMap.clear(); + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + changeSupport.addPropertyChangeListener(listener); + } + + public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + changeSupport.addPropertyChangeListener(propertyName, listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + changeSupport.removePropertyChangeListener(listener); + } + + public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + changeSupport.removePropertyChangeListener(propertyName, listener); + } +} diff --git a/key.util/src/main/java/org/key_project/util/lookup/Property.java b/key.util/src/main/java/org/key_project/util/lookup/Property.java new file mode 100644 index 00000000000..cc7a9c37a6d --- /dev/null +++ b/key.util/src/main/java/org/key_project/util/lookup/Property.java @@ -0,0 +1,9 @@ +package org.key_project.util.lookup; + +/** + * + * @author Alexander Weigl + * @version 1 (4/19/26) + */ +public record Property(String name) { +}