Skip to content

Commit 2d9f36c

Browse files
authored
A new grammar for configuration (KeYProject#3099)
This PR proposes an own JSON-like grammar for KeY settings and configuration. ## Status Quo Currently, we store our settings inside of Java Properties files. Either for global settings inside `$HOME/.key` or inside of proof files. Java Properties are a simple and easy solution, but also have some disadvantages. For example, * the lack of type safety (everything is a string), * missing positional information (we are not able to give error messages regarding incorrect settings), * no support for adding comments, and * no complex data structures. ## Proposal I propose an own grammar, written an ANTLR4, for KeY's configuration that is compatible with JSON and the grammar of KeY. ### Why an own grammar? (or why not JSON, TOML or Co.) There are many popular grammars for configuration files, e.g., JSON, TOML and YAML. I decided against them for the following reason: * The grammar must be embeddable into our KeY files and grammar. * No external dependencies and complexity. The grammar is small, and parsing is done in a few hundred boring LoC. * No support for storing additional information, like comments. ### The grammar The grammar is an extension of JSON, allowing comments, multiline strings, non-quoted keys, etc. An example is given in the comments below. The grammar is a recursive definition of values, which can simply be expressed as an ANTLR4 grammar. ```antlr4 // Config cfile: cvalue*; ckv: doc=DOC_COMMENT? ckey ':' cvalue; ckey: IDENT | STRING_LITERAL; cvalue: IDENT #csymbol | STRING_LITERAL #cstring | BIN_LITERAL #cintb | HEX_LITERAL #cinth | MINUS? INT_LITERAL #cintd | FLOAT_LITERAL #cfpf | DOUBLE_LITERAL #cfpd | REAL_LITERAL #cfpr | (TRUE|FALSE) #cbool | LBRACE (ckv (COMMA ckv)*)? COMMA? RBRACE #table | LBRACKET (cvalue (COMMA cvalue)*)? RBRACKET KeYProject#986 ``` ### Changes 1. Settings inside proof files would no longer be strings instead the section follows the grammar above. 2. All configuration files inside `$HOME/.key` will follow the new grammar. In case of the presence of a properties or and JSON file, the JSON takes advantage. 3. The interface for settings (`#writeSettings` and `#readSettings`) is extended with two new methods which take an object of `Configuration` (which is an extended `Map<String, Object>`) instead of a `Properties` object. ### Migration strategies In KeY files, we decide, based on the grammar (STRING_LITERAL vs. JSON-object), which settings handling should be to trigger. All settings are written as JSON-objects.
2 parents 1e5e4cc + d1b9485 commit 2d9f36c

Some content is hidden

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

44 files changed

+1830
-533
lines changed

gradle/wrapper/gradle-wrapper.jar

2.06 KB
Binary file not shown.

gradlew

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,8 @@ done
8383
# This is normally unused
8484
# shellcheck disable=SC2034
8585
APP_BASE_NAME=${0##*/}
86-
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
87-
88-
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89-
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
86+
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
87+
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
9088

9189
# Use the maximum available, or set MAX_FD != -1 to use that value.
9290
MAX_FD=maximum
@@ -133,10 +131,13 @@ location of your Java installation."
133131
fi
134132
else
135133
JAVACMD=java
136-
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
134+
if ! command -v java >/dev/null 2>&1
135+
then
136+
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137137
138138
Please set the JAVA_HOME variable in your environment to match the
139139
location of your Java installation."
140+
fi
140141
fi
141142

142143
# Increase the maximum file descriptors if we can.
@@ -197,6 +198,10 @@ if "$cygwin" || "$msys" ; then
197198
done
198199
fi
199200

201+
202+
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203+
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204+
200205
# Collect all arguments for the java command;
201206
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
202207
# shell script including quotes and variable substitutions, so put them in

key.core.testgen/src/main/java/de/uka/ilkd/key/settings/TestGenerationSettings.java

Lines changed: 82 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,19 @@ public class TestGenerationSettings extends AbstractSettings {
2626
// endregion
2727

2828
// region property names
29-
private static final String PROP_APPLY_SYMBOLIC_EXECUTION =
30-
"[TestGenSettings]applySymbolicExecution";
31-
private static final String PROP_MAX_UWINDS = "[TestGenSettings]maxUnwinds";
32-
private static final String PROP_OUTPUT_PATH = "[TestGenSettings]OutputPath";
33-
private static final String PROP_REMOVE_DUPLICATES = "[TestGenSettings]RemoveDuplicates";
34-
private static final String PROP_USE_RFL = "[TestGenSettings]UseRFL";
35-
private static final String PROP_USE_JUNIT = "[TestGenSettings]UseJUnit";
36-
private static final String PROP_CONCURRENT_PROCESSES = "[TestGenSettings]ConcurrentProcesses";
37-
private static final String PROP_INVARIANT_FOR_ALL = "[TestGenSettings]InvariantForAll";
38-
private static final String PROP_OPENJML_PATH = "[TestGenSettings]OpenJMLPath";
39-
private static final String PROP_OBJENESIS_PATH = "[TestGenSettings]ObjenesisPath";
29+
private static final String PROP_APPLY_SYMBOLIC_EXECUTION = "applySymbolicExecution";
30+
private static final String PROP_MAX_UWINDS = "maxUnwinds";
31+
private static final String PROP_OUTPUT_PATH = "OutputPath";
32+
private static final String PROP_REMOVE_DUPLICATES = "RemoveDuplicates";
33+
private static final String PROP_USE_RFL = "UseRFL";
34+
private static final String PROP_USE_JUNIT = "UseJUnit";
35+
private static final String PROP_CONCURRENT_PROCESSES = "ConcurrentProcesses";
36+
private static final String PROP_INVARIANT_FOR_ALL = "InvariantForAll";
37+
private static final String PROP_OPENJML_PATH = "OpenJMLPath";
38+
private static final String PROP_OBJENESIS_PATH = "ObjenesisPath";
4039
private static final String PROP_INCLUDE_POST_CONDITION =
41-
"[TestGenSettings]IncludePostCondition";
40+
"IncludePostCondition";
41+
private static final String CATEGORY = "TestGenSettings";
4242
// endregion
4343

4444
// Option fields
@@ -56,14 +56,14 @@ public class TestGenerationSettings extends AbstractSettings {
5656

5757

5858
public TestGenerationSettings() {
59-
applySymbolicExecution = TestGenerationSettings.DEFAULT_APPLYSYMBOLICEX;
60-
maxUnwinds = TestGenerationSettings.DEFAULT_MAXUNWINDS;
61-
outputPath = TestGenerationSettings.DEFAULT_OUTPUTPATH;
62-
removeDuplicates = TestGenerationSettings.DEFAULT_REMOVEDUPLICATES;
63-
useRFL = TestGenerationSettings.DEFAULT_USERFL;
64-
useJunit = TestGenerationSettings.DEFAULT_USEJUNIT;
65-
concurrentProcesses = TestGenerationSettings.DEFAULT_CONCURRENTPROCESSES;
66-
invariantForAll = TestGenerationSettings.DEFAULT_INVARIANTFORALL;
59+
applySymbolicExecution = DEFAULT_APPLYSYMBOLICEX;
60+
maxUnwinds = DEFAULT_MAXUNWINDS;
61+
outputPath = DEFAULT_OUTPUTPATH;
62+
removeDuplicates = DEFAULT_REMOVEDUPLICATES;
63+
useRFL = DEFAULT_USERFL;
64+
useJunit = DEFAULT_USEJUNIT;
65+
concurrentProcesses = DEFAULT_CONCURRENTPROCESSES;
66+
invariantForAll = DEFAULT_INVARIANTFORALL;
6767
openjmlPath = DEFAULT_OPENJMLPATH;
6868
objenesisPath = DEFAULT_OBJENESISPATH;
6969
includePostCondition = DEFAULT_INCLUDEPOSTCONDITION;
@@ -128,39 +128,24 @@ public boolean includePostCondition() {
128128

129129
@Override
130130
public void readSettings(Properties props) {
131+
var prefix = "[" + CATEGORY + "]";
131132
setApplySymbolicExecution(SettingsConverter.read(props,
132-
TestGenerationSettings.PROP_APPLY_SYMBOLIC_EXECUTION,
133-
TestGenerationSettings.DEFAULT_APPLYSYMBOLICEX));
134-
setMaxUnwinds(SettingsConverter.read(props,
135-
TestGenerationSettings.PROP_MAX_UWINDS,
136-
TestGenerationSettings.DEFAULT_MAXUNWINDS));
137-
setOutputPath(SettingsConverter.read(props,
138-
TestGenerationSettings.PROP_OUTPUT_PATH,
139-
TestGenerationSettings.DEFAULT_OUTPUTPATH));
133+
prefix + PROP_APPLY_SYMBOLIC_EXECUTION, DEFAULT_APPLYSYMBOLICEX));
134+
setMaxUnwinds(SettingsConverter.read(props, prefix + PROP_MAX_UWINDS, DEFAULT_MAXUNWINDS));
135+
setOutputPath(SettingsConverter.read(props, prefix + PROP_OUTPUT_PATH, DEFAULT_OUTPUTPATH));
140136
setRemoveDuplicates(SettingsConverter.read(props,
141-
TestGenerationSettings.PROP_REMOVE_DUPLICATES,
142-
TestGenerationSettings.DEFAULT_REMOVEDUPLICATES));
143-
setRFL(SettingsConverter.read(props,
144-
TestGenerationSettings.PROP_USE_RFL,
145-
TestGenerationSettings.DEFAULT_USERFL));
146-
setUseJunit(SettingsConverter.read(props,
147-
TestGenerationSettings.PROP_USE_JUNIT,
148-
TestGenerationSettings.DEFAULT_USEJUNIT));
137+
prefix + PROP_REMOVE_DUPLICATES, DEFAULT_REMOVEDUPLICATES));
138+
setRFL(SettingsConverter.read(props, prefix + PROP_USE_RFL, DEFAULT_USERFL));
139+
setUseJunit(SettingsConverter.read(props, prefix + PROP_USE_JUNIT, DEFAULT_USEJUNIT));
149140
setConcurrentProcesses(SettingsConverter.read(props,
150-
TestGenerationSettings.PROP_CONCURRENT_PROCESSES,
151-
TestGenerationSettings.DEFAULT_CONCURRENTPROCESSES));
141+
prefix + PROP_CONCURRENT_PROCESSES, DEFAULT_CONCURRENTPROCESSES));
152142
setInvariantForAll(SettingsConverter.read(props,
153-
TestGenerationSettings.PROP_INVARIANT_FOR_ALL,
154-
TestGenerationSettings.DEFAULT_INVARIANTFORALL));
155-
setOpenjmlPath(SettingsConverter.read(props,
156-
TestGenerationSettings.PROP_OPENJML_PATH,
157-
TestGenerationSettings.DEFAULT_OPENJMLPATH));
158-
setObjenesisPath(SettingsConverter.read(props,
159-
TestGenerationSettings.PROP_OBJENESIS_PATH,
160-
TestGenerationSettings.DEFAULT_OBJENESISPATH));
143+
prefix + PROP_INVARIANT_FOR_ALL, DEFAULT_INVARIANTFORALL));
144+
setOpenjmlPath(
145+
SettingsConverter.read(props, prefix + PROP_OPENJML_PATH, DEFAULT_OPENJMLPATH));
146+
setObjenesisPath(SettingsConverter.read(props, PROP_OBJENESIS_PATH, DEFAULT_OBJENESISPATH));
161147
setIncludePostCondition(SettingsConverter.read(props,
162-
TestGenerationSettings.PROP_INCLUDE_POST_CONDITION,
163-
TestGenerationSettings.DEFAULT_INCLUDEPOSTCONDITION));
148+
PROP_INCLUDE_POST_CONDITION, DEFAULT_INCLUDEPOSTCONDITION));
164149
}
165150

166151
public boolean removeDuplicates() {
@@ -252,22 +237,56 @@ public boolean useJunit() {
252237

253238
@Override
254239
public void writeSettings(Properties props) {
255-
SettingsConverter.store(props, TestGenerationSettings.PROP_APPLY_SYMBOLIC_EXECUTION,
240+
var prefix = "[" + CATEGORY + "]";
241+
SettingsConverter.store(props, prefix + PROP_APPLY_SYMBOLIC_EXECUTION,
256242
applySymbolicExecution);
257-
SettingsConverter.store(props, TestGenerationSettings.PROP_CONCURRENT_PROCESSES,
258-
concurrentProcesses);
259-
SettingsConverter.store(props, TestGenerationSettings.PROP_INVARIANT_FOR_ALL,
260-
invariantForAll);
261-
SettingsConverter.store(props, TestGenerationSettings.PROP_MAX_UWINDS, maxUnwinds);
262-
SettingsConverter.store(props, TestGenerationSettings.PROP_OUTPUT_PATH, outputPath);
263-
SettingsConverter.store(props, TestGenerationSettings.PROP_REMOVE_DUPLICATES,
264-
removeDuplicates);
265-
SettingsConverter.store(props, TestGenerationSettings.PROP_USE_RFL, useRFL);
266-
SettingsConverter.store(props, TestGenerationSettings.PROP_USE_JUNIT, useJunit);
267-
SettingsConverter.store(props, TestGenerationSettings.PROP_OPENJML_PATH, openjmlPath);
268-
SettingsConverter.store(props, TestGenerationSettings.PROP_OBJENESIS_PATH, objenesisPath);
269-
SettingsConverter.store(props, TestGenerationSettings.PROP_INCLUDE_POST_CONDITION,
270-
includePostCondition);
243+
SettingsConverter.store(props, prefix + PROP_CONCURRENT_PROCESSES, concurrentProcesses);
244+
SettingsConverter.store(props, prefix + PROP_INVARIANT_FOR_ALL, invariantForAll);
245+
SettingsConverter.store(props, prefix + PROP_MAX_UWINDS, maxUnwinds);
246+
SettingsConverter.store(props, prefix + PROP_OUTPUT_PATH, outputPath);
247+
SettingsConverter.store(props, prefix + PROP_REMOVE_DUPLICATES, removeDuplicates);
248+
SettingsConverter.store(props, prefix + PROP_USE_RFL, useRFL);
249+
SettingsConverter.store(props, prefix + PROP_USE_JUNIT, useJunit);
250+
SettingsConverter.store(props, prefix + PROP_OPENJML_PATH, openjmlPath);
251+
SettingsConverter.store(props, prefix + PROP_OBJENESIS_PATH, objenesisPath);
252+
SettingsConverter.store(props, prefix + PROP_INCLUDE_POST_CONDITION, includePostCondition);
253+
}
254+
255+
@Override
256+
public void readSettings(Configuration props) {
257+
var cat = props.getSection(CATEGORY);
258+
if (cat == null)
259+
return;
260+
setApplySymbolicExecution(
261+
cat.getBool(PROP_APPLY_SYMBOLIC_EXECUTION, DEFAULT_APPLYSYMBOLICEX));
262+
setMaxUnwinds(cat.getInt(PROP_MAX_UWINDS, DEFAULT_MAXUNWINDS));
263+
setOutputPath(cat.getString(PROP_OUTPUT_PATH, DEFAULT_OUTPUTPATH));
264+
setRemoveDuplicates(cat.getBool(PROP_REMOVE_DUPLICATES, DEFAULT_REMOVEDUPLICATES));
265+
setRFL(cat.getBool(PROP_USE_RFL, DEFAULT_USERFL));
266+
setUseJunit(cat.getBool(PROP_USE_JUNIT, DEFAULT_USEJUNIT));
267+
setConcurrentProcesses(cat.getInt(PROP_CONCURRENT_PROCESSES, DEFAULT_CONCURRENTPROCESSES));
268+
setInvariantForAll(cat.getBool(PROP_INVARIANT_FOR_ALL, DEFAULT_INVARIANTFORALL));
269+
setOpenjmlPath(cat.getString(PROP_OPENJML_PATH, DEFAULT_OPENJMLPATH));
270+
setObjenesisPath(cat.getString(PROP_OBJENESIS_PATH, DEFAULT_OBJENESISPATH));
271+
setIncludePostCondition(
272+
cat.getBool(PROP_INCLUDE_POST_CONDITION, DEFAULT_INCLUDEPOSTCONDITION));
273+
}
274+
275+
@Override
276+
public void writeSettings(Configuration props) {
277+
var cat = props.getOrCreateSection(CATEGORY);
278+
279+
cat.set(PROP_APPLY_SYMBOLIC_EXECUTION, applySymbolicExecution);
280+
cat.set(PROP_CONCURRENT_PROCESSES, concurrentProcesses);
281+
cat.set(PROP_INVARIANT_FOR_ALL, invariantForAll);
282+
cat.set(PROP_MAX_UWINDS, maxUnwinds);
283+
cat.set(PROP_OUTPUT_PATH, outputPath);
284+
cat.set(PROP_REMOVE_DUPLICATES, removeDuplicates);
285+
cat.set(PROP_USE_RFL, useRFL);
286+
cat.set(PROP_USE_JUNIT, useJunit);
287+
cat.set(PROP_OPENJML_PATH, openjmlPath);
288+
cat.set(PROP_OBJENESIS_PATH, objenesisPath);
289+
cat.set(PROP_INCLUDE_POST_CONDITION, includePostCondition);
271290
}
272291

273292
public void set(TestGenerationSettings settings) {

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,8 +408,6 @@ SL_COMMENT
408408

409409
DOC_COMMENT: '/*!' -> more, pushMode(docComment);
410410
ML_COMMENT: '/*' -> more, pushMode(COMMENT);
411-
412-
413411
BIN_LITERAL: '0' 'b' ('0' | '1' | '_')+ ('l'|'L')?;
414412

415413
HEX_LITERAL: '0' 'x' (DIGIT | 'a'..'f' | 'A'..'F' | '_')+ ('l'|'L')?;
@@ -450,7 +448,7 @@ DOUBLE_LITERAL:
450448
;
451449

452450
REAL_LITERAL:
453-
RATIONAL_LITERAL ('r' | 'R')
451+
RATIONAL_LITERAL ('r' | 'R')?
454452
;
455453

456454

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,8 @@ profile: PROFILE name=string_value SEMI;
814814

815815
preferences
816816
:
817-
KEYSETTINGS LBRACE (s=string_value)? RBRACE
817+
KEYSETTINGS (LBRACE s=string_value? RBRACE
818+
| c=cvalue ) // LBRACE, RBRACE included in cvalue#table
818819
;
819820

820821
proofScript
@@ -824,3 +825,23 @@ proofScript
824825

825826
// PROOF
826827
proof: PROOF EOF;
828+
829+
// Config
830+
cfile: cvalue* EOF;
831+
//csection: LBRACKET IDENT RBRACKET;
832+
ckv: doc=DOC_COMMENT? ckey ':' cvalue;
833+
ckey: IDENT | STRING_LITERAL;
834+
cvalue:
835+
IDENT #csymbol
836+
| STRING_LITERAL #cstring
837+
| BIN_LITERAL #cintb
838+
| HEX_LITERAL #cinth
839+
| MINUS? INT_LITERAL #cintd
840+
| MINUS? FLOAT_LITERAL #cfpf
841+
| MINUS? DOUBLE_LITERAL #cfpd
842+
| MINUS? REAL_LITERAL #cfpr
843+
| (TRUE|FALSE) #cbool
844+
| LBRACE
845+
(ckv (COMMA ckv)*)? COMMA?
846+
RBRACE #table
847+
| LBRACKET (cvalue (COMMA cvalue)*)? COMMA? RBRACKET #list;
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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.nparser;
5+
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import java.util.stream.Collectors;
9+
10+
import de.uka.ilkd.key.settings.Configuration;
11+
import de.uka.ilkd.key.util.LinkedHashMap;
12+
13+
import org.jspecify.annotations.NonNull;
14+
15+
/**
16+
* Translates the configuration grammar (something like JSON) into a {@link Configuration} object.
17+
*
18+
* @author Alexander Weigl
19+
* @version 1 (03.04.23)
20+
* @see KeyAst.ConfigurationFile#asConfiguration()
21+
*/
22+
class ConfigurationBuilder extends KeYParserBaseVisitor<Object> {
23+
@Override
24+
public List<Object> visitCfile(KeYParser.CfileContext ctx) {
25+
return ctx.cvalue().stream().map(it -> it.accept(this)).collect(Collectors.toList());
26+
}
27+
28+
@Override
29+
public Object visitCkey(KeYParser.CkeyContext ctx) {
30+
if (ctx.STRING_LITERAL() != null)
31+
return sanitizeStringLiteral(ctx.STRING_LITERAL().getText());
32+
return ctx.IDENT().getText();
33+
}
34+
35+
@Override
36+
public String visitCsymbol(KeYParser.CsymbolContext ctx) {
37+
return ctx.IDENT().getText();
38+
}
39+
40+
41+
@Override
42+
public String visitCstring(KeYParser.CstringContext ctx) {
43+
final var text = ctx.getText();
44+
return sanitizeStringLiteral(text);
45+
}
46+
47+
@NonNull
48+
private static String sanitizeStringLiteral(String text) {
49+
return text.substring(1, text.length() - 1)
50+
.replace("\\\"", "\"")
51+
.replace("\\\\", "\\");
52+
}
53+
54+
@Override
55+
public Object visitCintb(KeYParser.CintbContext ctx) {
56+
return Long.parseLong(ctx.getText(), 2);
57+
}
58+
59+
@Override
60+
public Object visitCinth(KeYParser.CinthContext ctx) {
61+
return Long.parseLong(ctx.getText(), 16);
62+
}
63+
64+
@Override
65+
public Object visitCintd(KeYParser.CintdContext ctx) {
66+
final var text = ctx.getText();
67+
if (text.endsWith("L") || text.endsWith("l")) {
68+
return Long.parseLong(text.substring(0, text.length() - 1), 10);
69+
} else {
70+
return Long.parseLong(text, 10);
71+
}
72+
}
73+
74+
@Override
75+
public Object visitCfpf(KeYParser.CfpfContext ctx) {
76+
return Double.parseDouble(ctx.getText());
77+
}
78+
79+
@Override
80+
public Object visitCfpd(KeYParser.CfpdContext ctx) {
81+
return Double.parseDouble(ctx.getText());
82+
}
83+
84+
@Override
85+
public Object visitCfpr(KeYParser.CfprContext ctx) {
86+
return Double.parseDouble(ctx.getText());
87+
}
88+
89+
@Override
90+
public Object visitCbool(KeYParser.CboolContext ctx) {
91+
return Boolean.parseBoolean(ctx.getText());
92+
}
93+
94+
@Override
95+
public Object visitTable(KeYParser.TableContext ctx) {
96+
final var data = new LinkedHashMap<String, Object>();
97+
for (KeYParser.CkvContext context : ctx.ckv()) {
98+
var name = context.ckey().accept(this).toString();
99+
var val = context.cvalue().accept(this);
100+
data.put(name, val);
101+
}
102+
return new Configuration(data);
103+
}
104+
105+
@Override
106+
public Object visitList(KeYParser.ListContext ctx) {
107+
var seq = new ArrayList<>(ctx.children.size());
108+
for (KeYParser.CvalueContext context : ctx.cvalue()) {
109+
seq.add(context.accept(this));
110+
}
111+
return seq;
112+
}
113+
}

0 commit comments

Comments
 (0)