Skip to content

Commit f5b19b3

Browse files
committed
Introduce ScriptCommandAst to keep the ProofEngine separated from ANTLR's parse tree
1 parent cd3c9c8 commit f5b19b3

File tree

63 files changed

+1404
-1020
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1404
-1020
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ subprojects {
101101
testImplementation ("org.junit.jupiter:junit-jupiter-params")
102102
testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine")
103103
testRuntimeOnly ("org.junit.platform:junit-platform-launcher")
104+
testImplementation ("org.assertj:assertj-core:3.26.3")
104105
testImplementation project(':key.util')
105106

106107
// test fixtures

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

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@
66
import java.net.URI;
77
import java.net.URISyntaxException;
88
import java.net.URL;
9+
import java.util.ArrayList;
10+
import java.util.HashMap;
911
import java.util.List;
1012

13+
import de.uka.ilkd.key.java.Position;
1114
import de.uka.ilkd.key.nparser.builder.BuilderHelpers;
1215
import de.uka.ilkd.key.nparser.builder.ChoiceFinder;
1316
import de.uka.ilkd.key.nparser.builder.FindProblemInformation;
1417
import de.uka.ilkd.key.nparser.builder.IncludeFinder;
1518
import de.uka.ilkd.key.parser.Location;
1619
import de.uka.ilkd.key.proof.init.Includes;
20+
import de.uka.ilkd.key.scripts.ScriptCommandAst;
1721
import de.uka.ilkd.key.settings.Configuration;
1822
import de.uka.ilkd.key.settings.ProofSettings;
1923
import de.uka.ilkd.key.speclang.njml.JmlParser;
@@ -48,7 +52,8 @@ protected KeyAst(@NonNull T ctx) {
4852
this.ctx = ctx;
4953
}
5054

51-
public <T> T accept(ParseTreeVisitor<T> visitor) {
55+
/// Apply the given `visitor` to the underlying ANTLR4 parse tree.
56+
public <R> R accept(ParseTreeVisitor<R> visitor) {
5257
return ctx.accept(visitor);
5358
}
5459

@@ -57,22 +62,27 @@ public String toString() {
5762
return getClass().getName() + ": " + BuilderHelpers.getPosition(ctx);
5863
}
5964

65+
/// Returns the start location of the first character of the first token of the underlying parse
66+
/// tree.
6067
public Location getStartLocation() {
6168
return Location.fromToken(ctx.start);
6269
}
6370

71+
/// Get the text of the parse tree as a cut out of the original input stream.
72+
/// Hence, Spaces and comments should be preserved.
6473
public String getText() {
6574
var interval = new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex() + 1);
6675
return ctx.start.getInputStream().getText(interval);
6776
}
6877

78+
/// An AST representing a complete KeY file.
6979
public static class File extends KeyAst<KeYParser.FileContext> {
7080
File(KeYParser.FileContext ctx) {
7181
super(ctx);
7282
}
7383

74-
@Nullable
75-
public ProofSettings findProofSettings() {
84+
/// Returns proof settings (aka `\settings`) if found in the parse tree.
85+
public @Nullable ProofSettings findProofSettings() {
7686
ProofSettings settings = new ProofSettings(ProofSettings.DEFAULT_SETTINGS);
7787

7888
if (ctx.preferences() != null && ctx.preferences().s != null) {
@@ -100,7 +110,6 @@ public ProofSettings findProofSettings() {
100110
if (ctx.problem() != null && ctx.problem().proofScriptEntry() != null) {
101111
KeYParser.ProofScriptEntryContext pctx = ctx.problem().proofScriptEntry();
102112

103-
KeYParser.ProofScriptContext ps;
104113
if (pctx.STRING_LITERAL() != null) {
105114
var ctx = pctx.STRING_LITERAL().getSymbol();
106115
String text = pctx.STRING_LITERAL().getText();
@@ -117,25 +126,34 @@ public ProofSettings findProofSettings() {
117126
return null;
118127
}
119128

129+
/// Returns the includes (possible empty but not null) computed from the underlying parse
130+
/// tree.
120131
public Includes getIncludes(URL base) {
121132
IncludeFinder finder = new IncludeFinder(base);
122133
accept(finder);
123134
return finder.getIncludes();
124135
}
125136

137+
/// Returns the information on choices from the underlying parse tree.
126138
public ChoiceInformation getChoices() {
127139
ChoiceFinder finder = new ChoiceFinder();
128140
accept(finder);
129141
return finder.getChoiceInformation();
130142
}
131143

144+
/// Returns information on `\problem`, `\proofObligation` or `\chooseContract` using the
145+
/// underlying
146+
/// parse tree.
132147
public ProblemInformation getProblemInformation() {
133148
FindProblemInformation fpi = new FindProblemInformation();
134149
ctx.accept(fpi);
135150
return fpi.getProblemInformation();
136151
}
137152

138-
public Token findProof() {
153+
/// Returns the token `\proof` in the underlying parse tree.`
154+
/// This token also marks the end of the parse tree (EOF) but not the end of the file.
155+
/// Positional information of the token is used to set up the proof replayer.
156+
public @Nullable Token findProof() {
139157
KeYParser.ProofContext a = ctx.proof();
140158
if (a != null) {
141159
return a.PROOF().getSymbol();
@@ -177,7 +195,7 @@ public Configuration asConfiguration() {
177195
final var cfg = new ConfigurationBuilder();
178196
List<Object> res = cfg.visitCfile(ctx);
179197
if (!res.isEmpty())
180-
return (Configuration) res.get(0);
198+
return (Configuration) res.getFirst();
181199
else
182200
throw new RuntimeException("Error in configuration. Source: "
183201
+ ctx.start.getTokenSource().getSourceName());
@@ -234,7 +252,7 @@ public static class ProofScript extends KeyAst<KeYParser.ProofScriptContext> {
234252
super(ctx);
235253
}
236254

237-
public URI getUrl() {
255+
public URI getUri() {
238256
final var sourceName = ctx.start.getTokenSource().getSourceName();
239257
try {
240258
if (sourceName.startsWith("file:") || sourceName.startsWith("http:")
@@ -244,5 +262,42 @@ public URI getUrl() {
244262
}
245263
return new java.io.File(sourceName).toURI();
246264
}
265+
266+
/// Translates this parse tree into an AST usable for the proof script engine.
267+
/// The current representation of a proof script is a list of commands. Each command
268+
/// can hold a list of sub-commands.
269+
///
270+
/// @return a non-null list of the parsed commands.
271+
public List<ScriptCommandAst> asAst() {
272+
var fileUri = getUri();
273+
return asAst(fileUri, ctx.proofScriptCommand());
274+
}
275+
276+
private List<ScriptCommandAst> asAst(URI file,
277+
List<KeYParser.ProofScriptCommandContext> cmds) {
278+
return cmds.stream().map(it -> asAst(file, it)).toList();
279+
}
280+
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+
287+
var nargs = new HashMap<String, Object>();
288+
var pargs = new ArrayList<>();
289+
290+
if (it.proofScriptParameters() != null) {
291+
for (var param : it.proofScriptParameters().proofScriptParameter()) {
292+
if (param.pname != null) {
293+
nargs.put(param.pname.getText(), param.expr);
294+
} else {
295+
pargs.add(param.expr);
296+
}
297+
}
298+
}
299+
300+
return new ScriptCommandAst(it.cmd.getText(), nargs, pargs, sub, loc);
301+
}
247302
}
248303
}

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

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import java.util.ArrayList;
77
import java.util.List;
8-
import java.util.Map;
8+
import java.util.Objects;
99

1010
import de.uka.ilkd.key.control.AbstractUserInterfaceControl;
1111
import de.uka.ilkd.key.java.Services;
@@ -26,10 +26,9 @@
2626
/// {@link AbstractCommand}. You need to override [AbstractCommand#execute(Object)] to implement the
2727
/// command logic.
2828
///
29-
/// @param <T> the expected parameter class
3029
/// @author Alexander Weigl
3130
@NullMarked
32-
public abstract class AbstractCommand<T> implements ProofScriptCommand<T> {
31+
public abstract class AbstractCommand implements ProofScriptCommand {
3332
protected @Nullable Proof proof;
3433
protected @Nullable Services service;
3534
protected @Nullable EngineState state;
@@ -40,34 +39,30 @@ public abstract class AbstractCommand<T> implements ProofScriptCommand<T> {
4039
*/
4140
protected @Nullable String documentation = null;
4241

42+
protected final EngineState state() {
43+
return Objects.requireNonNull(state);
44+
}
45+
4346
/**
4447
* ...
4548
*/
46-
private final Class<T> parameterClazz;
49+
private final @Nullable Class<?> parameterClazz;
4750

48-
protected AbstractCommand(Class<T> clazz) {
51+
protected AbstractCommand(@Nullable Class<?> clazz) {
4952
this.parameterClazz = clazz;
5053
}
5154

52-
public List<ProofScriptArgument<T>> getArguments() {
55+
public List<ProofScriptArgument> getArguments() {
5356
if (parameterClazz == null) {
5457
return new ArrayList<>();
5558
}
56-
return ArgumentsLifter.inferScriptArguments(parameterClazz, this);
59+
return ArgumentsLifter.inferScriptArguments(parameterClazz);
5760
}
5861

5962

6063
@Override
61-
public T evaluateArguments(EngineState state, Map<String, Object> arguments) throws Exception {
62-
if (parameterClazz != null) {
63-
T obj = parameterClazz.getDeclaredConstructor().newInstance();
64-
return state.getValueInjector().inject(this, obj, arguments);
65-
}
66-
return null;
67-
}
68-
69-
@Override
70-
public void execute(AbstractUserInterfaceControl uiControl, T args, EngineState stateMap)
64+
public final void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst args,
65+
EngineState stateMap)
7166
throws ScriptException, InterruptedException {
7267
proof = stateMap.getProof();
7368
service = proof.getServices();
@@ -89,11 +84,16 @@ public void execute(AbstractUserInterfaceControl uiControl, T args, EngineState
8984
/// @param args an instance of the parameters
9085
/// @throws ScriptException if something happened during execution
9186
/// @throws InterruptedException if thread was interrupted during execution
92-
protected void execute(T args) throws ScriptException, InterruptedException {
87+
public void execute(ScriptCommandAst args) throws ScriptException, InterruptedException {
9388
}
9489

9590
@Override
9691
public String getDocumentation() {
97-
return "";
92+
if (documentation == null && parameterClazz != null) {
93+
documentation =
94+
ArgumentsLifter.extractDocumentation(getName(), getClass(), parameterClazz);
95+
}
96+
return Objects.requireNonNullElse(documentation, "");
9897
}
98+
9999
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ public String getDocumentation() {
2525
}
2626

2727
@Override
28-
public void execute(AbstractUserInterfaceControl uiControl, Void args, EngineState state)
28+
public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst args,
29+
EngineState state)
2930
throws ScriptException, InterruptedException {
3031
Goal goal = state.getFirstOpenGoal(false);
3132
goal.setEnabled(true);

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

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,23 @@
44
package de.uka.ilkd.key.scripts;
55

66
import java.util.List;
7-
import java.util.Map;
87

98
import de.uka.ilkd.key.control.AbstractUserInterfaceControl;
10-
import de.uka.ilkd.key.nparser.KeYParser;
119
import de.uka.ilkd.key.proof.Goal;
1210
import de.uka.ilkd.key.scripts.meta.ProofScriptArgument;
1311

14-
public class AllCommand implements ProofScriptCommand<Map<String, Object>> {
12+
public class AllCommand implements ProofScriptCommand {
1513
private String documentation;
1614

1715
@Override
18-
public List<ProofScriptArgument<Map<String, Object>>> getArguments() {
16+
public List<ProofScriptArgument> getArguments() {
1917
return List.of();
2018
}
2119

2220
@Override
23-
public Map<String, Object> evaluateArguments(EngineState state, Map<String, Object> arguments) {
24-
return arguments;
25-
}
26-
27-
@Override
28-
public void execute(AbstractUserInterfaceControl uiControl, Map<String, Object> args,
21+
public void execute(AbstractUserInterfaceControl uiControl, ScriptCommandAst args,
2922
EngineState stateMap) throws ScriptException, InterruptedException {
30-
var block = (KeYParser.ProofScriptContext) args.get(ProofScriptEngine.KEY_SUB_SCRIPT);
23+
var block = args.commands();
3124

3225
if (block == null) {
3326
throw new ScriptException("Missing command to apply onAll to");
@@ -38,7 +31,7 @@ public void execute(AbstractUserInterfaceControl uiControl, Map<String, Object>
3831
for (Goal g : proof.openGoals()) {
3932
// if (isBelow(g, selectedNode)) {
4033
stateMap.setGoal(g);
41-
stateMap.getEngine().execute(uiControl, block.proofScriptCommand());
34+
stateMap.getEngine().execute(uiControl, block);
4235
// }
4336
}
4437
// state.setGoal(selectedNode);

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

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

6-
import java.util.Map;
76

7+
import de.uka.ilkd.key.scripts.meta.Documentation;
88
import de.uka.ilkd.key.scripts.meta.Option;
99

1010
/**
1111
* Halts the script if some condition is not met.
12+
* <p>
13+
* See exported documentation at {@link Parameters} at the end of this file.
1214
*
1315
* @author lanzinger
1416
*/
15-
public class AssertCommand extends AbstractCommand<AssertCommand.Parameters> {
17+
public class AssertCommand extends AbstractCommand {
1618

1719
/**
1820
* Instantiates a new assert command.
@@ -22,21 +24,16 @@ public AssertCommand() {
2224
}
2325

2426
@Override
25-
public Parameters evaluateArguments(EngineState state,
26-
Map<String, Object> arguments) throws Exception {
27-
return state.getValueInjector().inject(this, new Parameters(),
28-
arguments);
29-
}
27+
public void execute(ScriptCommandAst arguments) throws ScriptException, InterruptedException {
28+
var args = state().getValueInjector().inject(new Parameters(), arguments);
3029

31-
@Override
32-
public void execute(Parameters args) throws ScriptException, InterruptedException {
3330
if (args.goals == null) {
3431
throw new ScriptException("No parameter specified!");
3532
}
3633

37-
if (state.getProof().openEnabledGoals().size() != args.goals) {
34+
if (state().getProof().openEnabledGoals().size() != args.goals) {
3835
throw new ScriptException("Assertion failed: number of open goals is "
39-
+ state.getProof().openGoals().size() + ", but should be " + args.goals);
36+
+ state().getProof().openGoals().size() + ", but should be " + args.goals);
4037
}
4138
}
4239

@@ -48,6 +45,15 @@ public String getName() {
4845
/**
4946
* The Assigned parameters (currently only the passed goals).
5047
*/
48+
@Documentation("""
49+
The assert command checks if the number of open and enabled goals is equal to the given number.
50+
If not, the script is halted with an error message.
51+
52+
Deprecated: This command is deprecated and should not be used in new scripts.
53+
The name of this command is likely to change since "assert" will
54+
be used for a more general purpose. You may find that this is called
55+
"failUnless".
56+
""")
5157
public static class Parameters {
5258
/**
5359
* The number of open and enabled goals.

0 commit comments

Comments
 (0)