Skip to content

Commit a06eb4d

Browse files
committed
Higher Order Proof Scripts
1 parent 559f4f3 commit a06eb4d

File tree

10 files changed

+152
-78
lines changed

10 files changed

+152
-78
lines changed

key.core/src/main/antlr4/KeYParser.g4

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ parser grammar KeYParser;
55
}
66

77
@members {
8+
private boolean lastPosArgWasCodeBlock=false;
89
private SyntaxErrorReporter errorReporter = new SyntaxErrorReporter(getClass());
910
public SyntaxErrorReporter getErrorReporter() { return errorReporter;}
1011
}
@@ -859,16 +860,18 @@ proofScriptEntry
859860
| LBRACE proofScript RBRACE
860861
)
861862
;
863+
862864
proofScriptEOF: proofScript EOF;
863865
proofScript: proofScriptCommand+;
864-
proofScriptCommand: cmd=IDENT proofScriptParameters?
865-
( LBRACE sub=proofScript RBRACE SEMI?
866-
| SEMI);
866+
proofScriptCommand: cmd=IDENT proofScriptParameters? SEMI;
867+
// ( {lastPosArgWasCodeBlock}? SEMI?
868+
// | SEMI);
867869

868870
proofScriptParameters: proofScriptParameter+;
869871
proofScriptParameter : ((pname=proofScriptParameterName (COLON|EQUALS))? expr=proofScriptExpression);
870872
proofScriptParameterName: AT? IDENT; // someone thought, that the let-command parameters should have a leading "@"
871873
proofScriptExpression:
874+
// {lastPosArgWasCodeBlock=false;}
872875
boolean_literal
873876
| char_literal
874877
| integer
@@ -878,8 +881,10 @@ proofScriptExpression:
878881
| simple_ident
879882
| abbreviation
880883
| literals
884+
| proofScriptCodeBlock
881885
;
882886

887+
proofScriptCodeBlock: LBRACE proofScript RBRACE;
883888

884889
// PROOF
885890
proof: PROOF EOF;

key.core/src/main/java/de/uka/ilkd/key/nparser/KeyAst.java

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import de.uka.ilkd.key.nparser.builder.IncludeFinder;
1818
import de.uka.ilkd.key.parser.Location;
1919
import de.uka.ilkd.key.proof.init.Includes;
20+
import de.uka.ilkd.key.scripts.ScriptBlock;
2021
import de.uka.ilkd.key.scripts.ScriptCommandAst;
2122
import de.uka.ilkd.key.settings.Configuration;
2223
import de.uka.ilkd.key.settings.ProofSettings;
@@ -253,7 +254,11 @@ public static class ProofScript extends KeyAst<KeYParser.ProofScriptContext> {
253254
}
254255

255256
public URI getUri() {
256-
final var sourceName = ctx.start.getTokenSource().getSourceName();
257+
return getUri(ctx.start);
258+
}
259+
260+
public static URI getUri(Token token) {
261+
final var sourceName = token.getTokenSource().getSourceName();
257262
try {
258263
if (sourceName.startsWith("file:") || sourceName.startsWith("http:")
259264
|| sourceName.startsWith("jar:"))
@@ -273,31 +278,41 @@ public List<ScriptCommandAst> asAst() {
273278
return asAst(fileUri, ctx.proofScriptCommand());
274279
}
275280

276-
private List<ScriptCommandAst> asAst(URI file,
281+
private static List<ScriptCommandAst> asAst(URI file,
277282
List<KeYParser.ProofScriptCommandContext> cmds) {
278283
return cmds.stream().map(it -> asAst(file, it)).toList();
279284
}
280285

281-
private @NonNull ScriptCommandAst asAst(URI file, KeYParser.ProofScriptCommandContext it) {
282-
var loc = new Location(file, Position.fromToken(it.start));
283-
var sub = it.sub != null
284-
? asAst(file, it.sub.proofScriptCommand())
285-
: null;
286+
public static @NonNull ScriptBlock asAst(URI file, KeYParser.ProofScriptCodeBlockContext ctx) {
287+
var loc = new Location(file, Position.fromToken(ctx.start));
288+
return new ScriptBlock(
289+
ctx.proofScript().proofScriptCommand().stream()
290+
.map(it -> asAst(file, it))
291+
.toList(), loc);
292+
}
286293

294+
private static @NonNull ScriptCommandAst asAst(URI file, KeYParser.ProofScriptCommandContext it) {
295+
var loc = new Location(file, Position.fromToken(it.start));
287296
var nargs = new HashMap<String, Object>();
288297
var pargs = new ArrayList<>();
289298

290299
if (it.proofScriptParameters() != null) {
291300
for (var param : it.proofScriptParameters().proofScriptParameter()) {
301+
var expr = param.expr;
302+
Object value = expr;
303+
if(expr.proofScriptCodeBlock() != null) {
304+
value = asAst(file, expr.proofScriptCodeBlock());
305+
}
306+
292307
if (param.pname != null) {
293-
nargs.put(param.pname.getText(), param.expr);
308+
nargs.put(param.pname.getText(), value);
294309
} else {
295-
pargs.add(param.expr);
310+
pargs.add(value);
296311
}
297312
}
298313
}
299314

300-
return new ScriptCommandAst(it.cmd.getText(), nargs, pargs, sub, loc);
315+
return new ScriptCommandAst(it.cmd.getText(), nargs, pargs, loc);
301316
}
302317
}
303318
}

key.core/src/main/java/de/uka/ilkd/key/scripts/AllCommand.java

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,35 @@
33
* SPDX-License-Identifier: GPL-2.0-only */
44
package de.uka.ilkd.key.scripts;
55

6-
import java.util.List;
7-
86
import de.uka.ilkd.key.control.AbstractUserInterfaceControl;
97
import de.uka.ilkd.key.proof.Goal;
108
import de.uka.ilkd.key.scripts.meta.ProofScriptArgument;
119

12-
public class AllCommand implements ProofScriptCommand {
13-
private String documentation;
10+
import java.util.List;
1411

12+
public class AllCommand implements ProofScriptCommand {
1513
@Override
1614
public List<ProofScriptArgument> getArguments() {
1715
return List.of();
1816
}
1917

2018
@Override
2119
public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst args,
22-
EngineState stateMap) throws ScriptException, InterruptedException {
23-
var block = args.commands();
24-
25-
if (block == null) {
26-
throw new ScriptException("Missing command to apply onAll to");
20+
EngineState stateMap) throws ScriptException, InterruptedException {
21+
if (args.positionalArgs().size() != 1) {
22+
throw new ScriptException("Invalid number of positional arguments to 'onAll'. Pos. arguments: "
23+
+ args.positionalArgs().size());
2724
}
2825

26+
var block = stateMap.getValueInjector().convert(
27+
args.positionalArgs().getFirst(),
28+
ScriptBlock.class);
29+
2930
var proof = stateMap.getProof();
30-
// Node selectedNode = state.getSelectedNode();
3131
for (Goal g : proof.openGoals()) {
32-
// if (isBelow(g, selectedNode)) {
3332
stateMap.setGoal(g);
3433
stateMap.getEngine().execute(uiControl, block);
35-
// }
3634
}
37-
// state.setGoal(selectedNode);
3835
}
3936

4037

key.core/src/main/java/de/uka/ilkd/key/scripts/EngineState.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ private ValueInjector createDefaultValueInjector() {
100100
addContextTranslator(v, JTerm.class);
101101
addContextTranslator(v, Sequent.class);
102102
addContextTranslator(v, Semisequent.class);
103+
addContextTranslator(v, ScriptBlock.class);
103104
return v;
104105
}
105106

key.core/src/main/java/de/uka/ilkd/key/scripts/ExprEvaluator.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import de.uka.ilkd.key.nparser.KeYParser;
77
import de.uka.ilkd.key.nparser.KeYParser.*;
88
import de.uka.ilkd.key.nparser.KeYParserBaseVisitor;
9+
import de.uka.ilkd.key.nparser.KeyAst;
910
import de.uka.ilkd.key.nparser.builder.ExpressionBuilder;
1011

1112
import org.key_project.prover.sequent.Sequent;
@@ -14,6 +15,8 @@
1415
import org.slf4j.Logger;
1516
import org.slf4j.LoggerFactory;
1617

18+
import java.net.URI;
19+
1720
import static org.key_project.util.java.StringUtil.trim;
1821

1922
/// Evaluates expression inside of proof script to their appropriate type.
@@ -36,6 +39,12 @@ class ExprEvaluator extends KeYParserBaseVisitor<Object> {
3639
this.state = engineState;
3740
}
3841

42+
@Override
43+
public Object visitProofScriptCodeBlock(ProofScriptCodeBlockContext ctx) {
44+
URI uri = KeyAst.ProofScript.getUri(ctx.start);
45+
return KeyAst.ProofScript.asAst(uri, ctx);
46+
}
47+
3948
@Override
4049
public Object visitBoolean_literal(Boolean_literalContext ctx) {
4150
return Boolean.parseBoolean(ctx.getText());

key.core/src/main/java/de/uka/ilkd/key/scripts/ProofScriptEngine.java

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import de.uka.ilkd.key.proof.Proof;
2323

2424
import org.antlr.v4.runtime.RuleContext;
25-
import org.antlr.v4.runtime.misc.Interval;
2625
import org.jspecify.annotations.Nullable;
2726
import org.slf4j.Logger;
2827
import org.slf4j.LoggerFactory;
@@ -32,9 +31,6 @@
3231
* @author Alexander Weigl
3332
*/
3433
public class ProofScriptEngine {
35-
public static final int KEY_START_INDEX_PARAMETER = 2;
36-
public static final String KEY_SUB_SCRIPT = "#block";
37-
private static final int MAX_CHARS_PER_COMMAND = 80;
3834
private static final Map<String, ProofScriptCommand> COMMANDS = loadCommands();
3935
private static final Logger LOGGER = LoggerFactory.getLogger(ProofScriptEngine.class);
4036

@@ -117,6 +113,11 @@ public void execute(AbstractUserInterfaceControl uiControl, Proof proof)
117113
execute(uiControl, script);
118114
}
119115

116+
public void execute(AbstractUserInterfaceControl uiControl, ScriptBlock block)
117+
throws ScriptException, InterruptedException {
118+
execute(uiControl, block.commands());
119+
}
120+
120121
public void execute(AbstractUserInterfaceControl uiControl, List<ScriptCommandAst> commands)
121122
throws InterruptedException, ScriptException {
122123
if (script.isEmpty()) { // no commands given, no work to do
@@ -199,29 +200,6 @@ public static String prettyPrintCommand(KeYParser.ProofScriptCommandContext ctx)
199200
}
200201

201202

202-
private Map<String, Object> getArguments(KeYParser.ProofScriptCommandContext commandContext) {
203-
var map = new TreeMap<String, Object>();
204-
int i = KEY_START_INDEX_PARAMETER;
205-
206-
if (commandContext.proofScriptParameters() != null) {
207-
for (var pc : commandContext.proofScriptParameters().proofScriptParameter()) {
208-
String key = pc.pname != null ? pc.pname.getText() : "#" + (i++);
209-
map.put(key, pc.expr);
210-
}
211-
}
212-
213-
if (commandContext.sub != null) {
214-
map.put(KEY_SUB_SCRIPT, commandContext.sub);
215-
}
216-
217-
var in = commandContext.start.getTokenSource().getInputStream();
218-
var txt = in.getText(
219-
Interval.of(commandContext.start.getStartIndex(), commandContext.stop.getStopIndex()));
220-
map.put(ScriptLineParser.LITERAL_KEY, txt);
221-
return map;
222-
}
223-
224-
225203
public EngineState getStateMap() {
226204
return stateMap;
227205
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/* This file is part of KeY - https://key-project.org
2+
* KeY is licensed under the GNU General Public License Version 2
3+
* SPDX-License-Identifier: GPL-2.0-only */
4+
package de.uka.ilkd.key.scripts;
5+
6+
import de.uka.ilkd.key.parser.Location;
7+
import org.jspecify.annotations.NullMarked;
8+
import org.jspecify.annotations.Nullable;
9+
10+
import java.util.List;
11+
12+
import static java.util.stream.Collectors.joining;
13+
14+
/// This class represents is the AST of a proof script command.
15+
@NullMarked
16+
public record ScriptBlock(
17+
List<ScriptCommandAst> commands,
18+
@Nullable Location location) {
19+
20+
public String asCommandLine() {
21+
return " {"
22+
+ commands.stream().map(ScriptCommandAst::asCommandLine).collect(joining("\n"))
23+
+ "\n}";
24+
}
25+
}

key.core/src/main/java/de/uka/ilkd/key/scripts/ScriptCommandAst.java

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
* SPDX-License-Identifier: GPL-2.0-only */
44
package de.uka.ilkd.key.scripts;
55

6-
import java.util.Collections;
76
import java.util.List;
87
import java.util.Map;
98
import java.util.Objects;
109

10+
import de.uka.ilkd.key.nparser.KeYParser;
11+
import de.uka.ilkd.key.nparser.KeyAst;
1112
import de.uka.ilkd.key.parser.Location;
1213

1314
import org.antlr.v4.runtime.ParserRuleContext;
@@ -22,31 +23,27 @@
2223
/// ```
2324
/// <commandName> key_1:value_1 ... key_m:value_m positionalArgs_1 ... positionalArgs_n {
2425
/// commands_0; ...; commands_k;
25-
/// }
26-
/// ```
26+
///}
27+
///```
2728
///
28-
/// @param commandName the name of the command, e.g., "macro" for `macro auto;`
29-
/// @param namedArgs a map of the given named arguments and values.
30-
/// If a named argument is not given, the entry should be missing in the map. Null-values are not
31-
/// allowed.
29+
/// @param commandName the name of the command, e.g., "macro" for `macro auto;`
30+
/// @param namedArgs a map of the given named arguments and values.
31+
/// If a named argument is not given, the entry should be missing in the map. Null-values are not
32+
/// allowed.
3233
/// @param positionalArgs the list of given positional arguments
33-
/// @param commands a nullable block of proof script arguments (represents "higher-order proof
34-
/// scripts").
35-
/// If null, the block was omitted syntactically in contrast to an empty list.
36-
/// @param location the location of this command for error reporting.
34+
/// @param location the location of this command for error reporting. **excluded from equality**
3735
/// @author Alexander Weigl
3836
/// @version 1 (14.03.25)
3937
@NullMarked
4038
public record ScriptCommandAst(
4139
String commandName,
4240
Map<String, Object> namedArgs,
4341
List<Object> positionalArgs,
44-
@Nullable List<ScriptCommandAst> commands,
4542
@Nullable Location location) {
4643

4744
public ScriptCommandAst(String commandName, Map<String, Object> namedArgs,
48-
List<Object> positionalArgs) {
49-
this(commandName, namedArgs, positionalArgs, Collections.emptyList(), null);
45+
List<Object> positionalArgs) {
46+
this(commandName, namedArgs, positionalArgs, null);
5047
}
5148

5249
/// Renders this command a parsable string representation. The order of the arguments is as
@@ -62,18 +59,35 @@ public String asCommandLine() {
6259
+ ' '
6360
+ positionalArgs.stream().map(ScriptCommandAst::asReadableString)
6461
.collect(joining(" "))
65-
+ (commands != null
66-
? " {"
67-
+ commands.stream().map(ScriptCommandAst::asCommandLine)
68-
.collect(joining("\n"))
69-
+ "\n}"
70-
: ";");
62+
+ ";";
7163
}
7264

7365
public static String asReadableString(Object value) {
66+
if (value instanceof ScriptBlock b) {
67+
return b.asCommandLine();
68+
}
69+
70+
if (value instanceof KeYParser.ProofScriptCodeBlockContext ctx) {
71+
asReadableString(KeyAst.ProofScript.asAst(null, ctx));
72+
}
7473
if (value instanceof ParserRuleContext ctx) {
7574
return ctx.getText();
7675
}
7776
return Objects.toString(value);
7877
}
78+
79+
@Override
80+
public int hashCode() {
81+
return Objects.hash(commandName, namedArgs, positionalArgs);
82+
}
83+
84+
@Override
85+
public boolean equals(Object obj) {
86+
if (this == obj) { return true; }
87+
if(obj == null || getClass() != obj.getClass()) { return false; }
88+
ScriptCommandAst other = (ScriptCommandAst) obj;
89+
return Objects.equals(commandName, other.commandName)
90+
&& Objects.equals(positionalArgs, other.positionalArgs)
91+
&& Objects.equals(namedArgs, other.namedArgs);
92+
}
7993
}

0 commit comments

Comments
 (0)