From 81b32569b9e4c7d61fcf162efacfdef1aa0deaf2 Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Thu, 5 Oct 2023 00:59:22 +0200 Subject: [PATCH 01/35] some functions for tff # Conflicts: # settings.gradle --- .../java/de/uka/ilkd/key/gui/Example.java | 196 +++++++++++++++ .../de/uka/ilkd/key/gui/ExampleChooser.java | 179 -------------- keyext.api/build.gradle | 6 + .../org/keyproject/key/FileTypeAdapter.java | 20 ++ .../org/keyproject/key/remoteapi/KeyApi.java | 58 +++++ .../keyproject/key/remoteapi/KeyApiImpl.java | 56 +++++ .../key/remoteapi/KeyExampleApi.java | 14 ++ .../keyproject/key/remoteapi/KeyMetaApi.java | 21 ++ .../key/remoteapi/MacroDescription.java | 9 + .../org/keyproject/key/remoteapi/Main.java | 63 +++++ .../org/keyproject/key/remoteapi/ProofId.java | 4 + .../key/remoteapi/ProofLoading.java | 67 ++++++ .../keyproject/key/remoteapi/TraceValue.java | 5 + .../keyproject/key/remoteclient/Client.java | 53 +++++ .../key/remoteclient/ClientApi.java | 224 ++++++++++++++++++ .../key/remoteclient/SimpleClient.java | 9 + settings.gradle | 2 + 17 files changed, 807 insertions(+), 179 deletions(-) create mode 100644 key.ui/src/main/java/de/uka/ilkd/key/gui/Example.java create mode 100644 keyext.api/build.gradle create mode 100644 keyext.api/src/main/java/org/keyproject/key/FileTypeAdapter.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApi.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApiImpl.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyExampleApi.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyMetaApi.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteapi/MacroDescription.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteapi/Main.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofId.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofLoading.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteapi/TraceValue.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteclient/Client.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteclient/ClientApi.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteclient/SimpleClient.java diff --git a/key.ui/src/main/java/de/uka/ilkd/key/gui/Example.java b/key.ui/src/main/java/de/uka/ilkd/key/gui/Example.java new file mode 100644 index 00000000000..85a85b65df1 --- /dev/null +++ b/key.ui/src/main/java/de/uka/ilkd/key/gui/Example.java @@ -0,0 +1,196 @@ +package de.uka.ilkd.key.gui; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; + +/** + * This class wraps a {@link java.io.File} and has a special {@link #toString()} method only using the + * short file name w/o path. + *

+ * Used for displaying files in the examples list w/o prefix + */ +public class Example { + private static final Logger LOGGER = LoggerFactory.getLogger(Example.class); + /** + * This constant is accessed by the eclipse based projects. + */ + public static final String KEY_FILE_NAME = "project.key"; + + private static final String PROOF_FILE_NAME = "project.proof"; + + /** + * The default category under which examples range if they do not have {@link #KEY_PATH} + * set. + */ + public static final String DEFAULT_CATEGORY_PATH = "Unsorted"; + + /** + * The {@link Properties} key to specify the path in the tree. + */ + public static final String KEY_PATH = "example.path"; + + /** + * The {@link Properties} key to specify the name of the example. Directory name if left + * open. + */ + public static final String KEY_NAME = "example.name"; + + /** + * The {@link Properties} key to specify the file for the example. KEY_FILE_NAME by default + */ + public static final String KEY_FILE = "example.file"; + + /** + * The {@link Properties} key to specify the proof file in the tree. May be left open + */ + public static final String KEY_PROOF_FILE = "example.proofFile"; + + /** + * The {@link Properties} key to specify the path in the tree. Prefix to specify additional + * files to load. Append 1, 2, 3, ... + */ + public static final String ADDITIONAL_FILE_PREFIX = "example.additionalFile."; + + /** + * The {@link Properties} key to specify the path in the tree. Prefix to specify export + * files which are not shown as tabs in the example wizard but are extracted to Java + * projects in the Eclipse integration. Append 1, 2, 3, ... + */ + public static final String EXPORT_FILE_PREFIX = "example.exportFile."; + + public final File exampleFile; + public final File directory; + public final String description; + public final Properties properties; + + public Example(File file) throws IOException { + this.exampleFile = file; + this.directory = file.getParentFile(); + this.properties = new Properties(); + StringBuilder sb = new StringBuilder(); + extractDescription(file, sb, properties); + this.description = sb.toString(); + } + + public File getDirectory() { + return directory; + } + + public File getProofFile() { + return new File(directory, properties.getProperty(KEY_PROOF_FILE, PROOF_FILE_NAME)); + } + + public File getObligationFile() { + return new File(directory, properties.getProperty(KEY_FILE, KEY_FILE_NAME)); + } + + public String getName() { + return properties.getProperty(KEY_NAME, directory.getName()); + } + + public String getDescription() { + return description; + } + + public File getExampleFile() { + return exampleFile; + } + + public List getAdditionalFiles() { + var result = new ArrayList(); + int i = 1; + while (properties.containsKey(ADDITIONAL_FILE_PREFIX + i)) { + result.add(new File(directory, properties.getProperty(ADDITIONAL_FILE_PREFIX + i))); + i++; + } + return result; + } + + public List getExportFiles() { + ArrayList result = new ArrayList<>(); + int i = 1; + while (properties.containsKey(EXPORT_FILE_PREFIX + i)) { + result.add(new File(directory, properties.getProperty(EXPORT_FILE_PREFIX + i))); + i++; + } + return result; + } + + public String[] getPath() { + return properties.getProperty(KEY_PATH, DEFAULT_CATEGORY_PATH).split("/"); + } + + @Override + public String toString() { + return getName(); + } + + public void addToTreeModel(DefaultTreeModel model) { + DefaultMutableTreeNode node = + findChild((DefaultMutableTreeNode) model.getRoot(), getPath(), 0); + node.add(new DefaultMutableTreeNode(this)); + } + + private DefaultMutableTreeNode findChild(DefaultMutableTreeNode root, String[] path, + int from) { + if (from == path.length) { + return root; + } + Enumeration en = root.children(); + while (en.hasMoreElements()) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) en.nextElement(); + if (node.getUserObject().equals(path[from])) { + return findChild(node, path, from + 1); + } + } + // not found ==> add new + DefaultMutableTreeNode node = new DefaultMutableTreeNode(path[from]); + root.add(node); + return findChild(node, path, from + 1); + } + + public boolean hasProof() { + return properties.containsKey(KEY_PROOF_FILE); + } + + private static StringBuilder extractDescription(File file, StringBuilder sb, + Properties properties) { + try (BufferedReader r = new BufferedReader(new FileReader(file, StandardCharsets.UTF_8))) { + String line; + boolean emptyLineSeen = false; + while ((line = r.readLine()) != null) { + if (emptyLineSeen) { + sb.append(line).append("\n"); + } else { + String trimmed = line.trim(); + if (trimmed.length() == 0) { + emptyLineSeen = true; + } else if (trimmed.startsWith("#")) { + // ignore + } else { + String[] entry = trimmed.split(" *[:=] *", 2); + if (entry.length > 1) { + properties.put(entry[0], entry[1]); + } + } + } + } + } catch (IOException e) { + LOGGER.error("", e); + return sb; + } + return sb; + } +} \ No newline at end of file diff --git a/key.ui/src/main/java/de/uka/ilkd/key/gui/ExampleChooser.java b/key.ui/src/main/java/de/uka/ilkd/key/gui/ExampleChooser.java index f5e40873058..02505ac383e 100644 --- a/key.ui/src/main/java/de/uka/ilkd/key/gui/ExampleChooser.java +++ b/key.ui/src/main/java/de/uka/ilkd/key/gui/ExampleChooser.java @@ -39,15 +39,6 @@ public final class ExampleChooser extends JDialog { */ public static final String EXAMPLES_PATH = "examples"; - private static final long serialVersionUID = -4405666868752394532L; - - /** - * This constant is accessed by the eclipse based projects. - */ - public static final String KEY_FILE_NAME = "project.key"; - - private static final String PROOF_FILE_NAME = "project.proof"; - /** * Java property name to specify a custom key example folder. */ @@ -77,149 +68,7 @@ public final class ExampleChooser extends JDialog { */ private Example selectedExample; - /** - * This class wraps a {@link File} and has a special {@link #toString()} method only using the - * short file name w/o path. - *

- * Used for displaying files in the examples list w/o prefix - */ - public static class Example { - /** - * The default category under which examples range if they do not have {@link #KEY_PATH} - * set. - */ - private static final String DEFAULT_CATEGORY_PATH = "Unsorted"; - - /** - * The {@link Properties} key to specify the path in the tree. - */ - private static final String KEY_PATH = "example.path"; - - /** - * The {@link Properties} key to specify the name of the example. Directory name if left - * open. - */ - private static final String KEY_NAME = "example.name"; - - /** - * The {@link Properties} key to specify the file for the example. KEY_FILE_NAME by default - */ - private static final String KEY_FILE = "example.file"; - - /** - * The {@link Properties} key to specify the proof file in the tree. May be left open - */ - private static final String KEY_PROOF_FILE = "example.proofFile"; - - /** - * The {@link Properties} key to specify the path in the tree. Prefix to specify additional - * files to load. Append 1, 2, 3, ... - */ - private static final String ADDITIONAL_FILE_PREFIX = "example.additionalFile."; - - /** - * The {@link Properties} key to specify the path in the tree. Prefix to specify export - * files which are not shown as tabs in the example wizard but are extracted to Java - * projects in the Eclipse integration. Append 1, 2, 3, ... - */ - private static final String EXPORT_FILE_PREFIX = "example.exportFile."; - - private final File exampleFile; - private final File directory; - private final String description; - private final Properties properties; - - public Example(File file) throws IOException { - this.exampleFile = file; - this.directory = file.getParentFile(); - this.properties = new Properties(); - StringBuilder sb = new StringBuilder(); - extractDescription(file, sb, properties); - this.description = sb.toString(); - } - - public File getDirectory() { - return directory; - } - public File getProofFile() { - return new File(directory, properties.getProperty(KEY_PROOF_FILE, PROOF_FILE_NAME)); - } - - public File getObligationFile() { - return new File(directory, properties.getProperty(KEY_FILE, KEY_FILE_NAME)); - } - - public String getName() { - return properties.getProperty(KEY_NAME, directory.getName()); - } - - public String getDescription() { - return description; - } - - public File getExampleFile() { - return exampleFile; - } - - public List getAdditionalFiles() { - ArrayList result = new ArrayList<>(); - int i = 1; - while (properties.containsKey(ADDITIONAL_FILE_PREFIX + i)) { - result.add(new File(directory, properties.getProperty(ADDITIONAL_FILE_PREFIX + i))); - i++; - } - return result; - } - - public List getExportFiles() { - ArrayList result = new ArrayList<>(); - int i = 1; - while (properties.containsKey(EXPORT_FILE_PREFIX + i)) { - result.add(new File(directory, properties.getProperty(EXPORT_FILE_PREFIX + i))); - i++; - } - return result; - } - - public String[] getPath() { - return properties.getProperty(KEY_PATH, DEFAULT_CATEGORY_PATH).split("/"); - } - - @Override - public String toString() { - return getName(); - } - - public void addToTreeModel(DefaultTreeModel model) { - DefaultMutableTreeNode node = - findChild((DefaultMutableTreeNode) model.getRoot(), getPath(), 0); - node.add(new DefaultMutableTreeNode(this)); - } - - private DefaultMutableTreeNode findChild(DefaultMutableTreeNode root, String[] path, - int from) { - if (from == path.length) { - return root; - } - Enumeration en = root.children(); - while (en.hasMoreElements()) { - DefaultMutableTreeNode node = (DefaultMutableTreeNode) en.nextElement(); - if (node.getUserObject().equals(path[from])) { - return findChild(node, path, from + 1); - } - } - // not found ==> add new - DefaultMutableTreeNode node = new DefaultMutableTreeNode(path[from]); - root.add(node); - return findChild(node, path, from + 1); - } - - public boolean hasProof() { - return properties.containsKey(KEY_PROOF_FILE); - } - - } // ------------------------------------------------------------------------- // constructors @@ -364,34 +213,6 @@ private static String fileAsString(File f) { } } - private static StringBuilder extractDescription(File file, StringBuilder sb, - Properties properties) { - try (BufferedReader r = new BufferedReader(new FileReader(file, StandardCharsets.UTF_8))) { - String line; - boolean emptyLineSeen = false; - while ((line = r.readLine()) != null) { - if (emptyLineSeen) { - sb.append(line).append("\n"); - } else { - String trimmed = line.trim(); - if (trimmed.length() == 0) { - emptyLineSeen = true; - } else if (trimmed.startsWith("#")) { - // ignore - } else { - String[] entry = trimmed.split(" *[:=] *", 2); - if (entry.length > 1) { - properties.put(entry[0], entry[1]); - } - } - } - } - } catch (IOException e) { - LOGGER.error("", e); - return sb; - } - return sb; - } private void updateDescription() { diff --git a/keyext.api/build.gradle b/keyext.api/build.gradle new file mode 100644 index 00000000000..463b7dbc11a --- /dev/null +++ b/keyext.api/build.gradle @@ -0,0 +1,6 @@ +dependencies { + implementation(project(":key.core")) + implementation(project(":key.ui")) + + implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.21.1") +} \ No newline at end of file diff --git a/keyext.api/src/main/java/org/keyproject/key/FileTypeAdapter.java b/keyext.api/src/main/java/org/keyproject/key/FileTypeAdapter.java new file mode 100644 index 00000000000..67240012e90 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/FileTypeAdapter.java @@ -0,0 +1,20 @@ +package org.keyproject.key; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import java.io.File; +import java.io.IOException; + +public class FileTypeAdapter extends TypeAdapter { + @Override + public void write(JsonWriter out, File value) throws IOException { + out.value(value.toString()); + } + + @Override + public File read(JsonReader in) throws IOException { + return new File(in.nextString()); + } +} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApi.java b/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApi.java new file mode 100644 index 00000000000..76c2d1eefdd --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApi.java @@ -0,0 +1,58 @@ +package org.keyproject.key.remoteapi; + +import de.uka.ilkd.key.gui.Example; +import de.uka.ilkd.key.gui.ExampleChooser; +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public interface KeyApi extends KeyExampleApi, KeyMetaApi { + //region general server management + /** + * Shutdown Request (:leftwards_arrow_with_hook:) + The shutdown request is sent from the client to the server. It asks the server to shut down, but to not exit (otherwise the response might not be delivered correctly to the client). There is a separate exit notification that asks the server to exit. Clients must not send any notifications other than exit or requests to a server to which they have sent a shutdown request. Clients should also wait with sending the exit notification until they have received a response from the shutdown request. + + If a server receives requests after a shutdown request those requests should error with InvalidRequest. + + Request: + + method: ‘shutdown’ + params: none + Response: + + result: null + error: code and message set in case an exception happens during shutdown request. + */ + @JsonRequest + Void shutdown(); + + /** + * Exit Notification (:arrow_right:) + * A notification to ask the server to exit its process. The server should exit with success code 0 if the shutdown request has been received before; otherwise with error code 1. + *

+ * Notification: + *

+ * method: ‘exit’ + * params: none + */ + @JsonNotification + void exit(); + + + + interface SetTraceParams { + /** + * The new value that should be assigned to the trace setting. + */ + TraceValue value = null; + } + /** + * SetTrace Notification + * A notification that should be used by the client to modify the trace setting of the server. + */ + @JsonNotification + void setTrace(SetTraceParams params); + //endregion +} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApiImpl.java b/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApiImpl.java new file mode 100644 index 00000000000..0a967998dac --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApiImpl.java @@ -0,0 +1,56 @@ +package org.keyproject.key.remoteapi; + +import de.uka.ilkd.key.api.KeYApi; +import de.uka.ilkd.key.gui.Example; +import de.uka.ilkd.key.gui.ExampleChooser; +import de.uka.ilkd.key.macros.scripts.ProofScriptCommand; +import de.uka.ilkd.key.util.KeYConstants; +import org.eclipse.lsp4j.jsonrpc.CompletableFutures; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@JsonSegment +class KeyApiImpl implements KeyApi { + @Override + @JsonRequest + public CompletableFuture> examples() { + return CompletableFutures.computeAsync((c) -> ExampleChooser.listExamples(ExampleChooser.lookForExamples())); + } + + @Override + public Void shutdown() { + return null; + } + + @Override + public void exit() { + + } + + @Override + public void setTrace(SetTraceParams params) { + + } + + @Override + public CompletableFuture getVersion() { + return CompletableFuture.completedFuture(KeYConstants.VERSION); + } + + @Override + public CompletableFuture> getAvailableMacros() { + return CompletableFuture.completedFuture( + KeYApi.getMacroApi().getMacros().stream().map(MacroDescription::from) + .toList() + ); + } + + @Override + public CompletableFuture>> getAvailableScriptCommands() { + return CompletableFuture.completedFuture( + KeYApi.getScriptCommandApi().getScriptCommands().stream().toList()); + } +} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyExampleApi.java b/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyExampleApi.java new file mode 100644 index 00000000000..f1fe80a6dfc --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyExampleApi.java @@ -0,0 +1,14 @@ +package org.keyproject.key.remoteapi; + +import de.uka.ilkd.key.gui.Example; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@JsonSegment("examples") +public interface KeyExampleApi { + @JsonRequest("list") + CompletableFuture> examples(); +} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyMetaApi.java b/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyMetaApi.java new file mode 100644 index 00000000000..e5538b30896 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyMetaApi.java @@ -0,0 +1,21 @@ +package org.keyproject.key.remoteapi; + +import de.uka.ilkd.key.macros.scripts.ProofScriptCommand; +import org.eclipse.lsp4j.jsonrpc.CompletableFutures; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@JsonSegment("meta") +public interface KeyMetaApi { + @JsonRequest + CompletableFuture getVersion(); + + @JsonRequest + CompletableFuture> getAvailableMacros(); + + @JsonRequest + CompletableFuture>> getAvailableScriptCommands(); +} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/MacroDescription.java b/keyext.api/src/main/java/org/keyproject/key/remoteapi/MacroDescription.java new file mode 100644 index 00000000000..2f34d3e03ed --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/remoteapi/MacroDescription.java @@ -0,0 +1,9 @@ +package org.keyproject.key.remoteapi; + +import de.uka.ilkd.key.macros.ProofMacro; + +public record MacroDescription(String name, String description, String category) { + public static MacroDescription from(ProofMacro m) { + return new MacroDescription(m.getName(), m.getDescription(), m.getCategory()); + } +} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/Main.java b/keyext.api/src/main/java/org/keyproject/key/remoteapi/Main.java new file mode 100644 index 00000000000..127b36a702c --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/remoteapi/Main.java @@ -0,0 +1,63 @@ +package org.keyproject.key.remoteapi; + +import com.google.gson.GsonBuilder; +import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.keyproject.key.FileTypeAdapter; +import org.keyproject.key.remoteclient.ClientApi; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; + +public class Main { + public static void main(String[] args) throws ExecutionException, InterruptedException { + var future = launch(System.out, System.in); + future.startListening(); + } + + public static Launcher launch(OutputStream out, InputStream in) { + var localServices = getLocalServices(); + var remoteInterfaces = getRemoteInterfaces(); + var launcherBuilder = new Launcher.Builder() + .setOutput(out) + .setInput(in) + .traceMessages(new PrintWriter(System.err)) + .validateMessages(true); + + launcherBuilder.configureGson(Main::configureJson); + + //if (localServices != null && !localServices.isEmpty()) + launcherBuilder.setLocalService(new KeyApiImpl()); + //if (remoteInterfaces != null && !remoteInterfaces.isEmpty()) + launcherBuilder.setRemoteInterface(ClientApi.class); + + var launcher = launcherBuilder.create(); + return launcher; + } + + public static void configureJson(GsonBuilder gsonBuilder) { + gsonBuilder.registerTypeAdapter(File.class, new FileTypeAdapter()); + } + + private static Collection> getRemoteInterfaces() { + return Collections.singleton(ClientApi.class); + /* return ServiceLoader.load(ClientService.class) + .stream() + .map(ServiceLoader.Provider::type) + .collect(Collectors.toSet()); + */ + } + + private static List getLocalServices() { + return Collections.singletonList(new KeyApiImpl()); + /*return ServiceLoader.load(KeyService.class) + .stream().map(ServiceLoader.Provider::get) + .map(it -> (Object) it) + .toList();*/ + } +} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofId.java b/keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofId.java new file mode 100644 index 00000000000..65db8662e4e --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofId.java @@ -0,0 +1,4 @@ +package org.keyproject.key.remoteapi; + +public record ProofId(String id) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofLoading.java b/keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofLoading.java new file mode 100644 index 00000000000..7e99ffd0cf5 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofLoading.java @@ -0,0 +1,67 @@ +package org.keyproject.key.remoteapi; + +import de.uka.ilkd.key.api.ProofManagementApi; +import de.uka.ilkd.key.control.KeYEnvironment; +import de.uka.ilkd.key.proof.io.ProblemLoaderException; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; + +import java.io.File; +import java.util.List; + +@JsonSegment +public interface ProofLoading { + @JsonRequest ProofId loadExample(String id); + + interface LoadParams { + File keyFile; + File javaFile; + + List classPath, + File bootClassPath, List includes + } + + /** + * @param keyFile + * @return + * @throws ProblemLoaderException + */ + @JsonRequest ProofId loadFromKeyFile(File keyFile) throws ProblemLoaderException { + return new ProofManagementApi(KeYEnvironment.load(keyFile)); + } + + /** + * @param location + * @param classPath + * @param bootClassPath + * @param includes + * @return + * @throws ProblemLoaderException + */ + @JsonRequest ProofId loadProof(File location, List classPath, + File bootClassPath, List includes) throws ProblemLoaderException { + return new ProofManagementApi( + KeYEnvironment.load(location, classPath, bootClassPath, includes)); + } + + /** + * @param javaSourceCode + * @return + * @throws ProblemLoaderException + */ + @JsonRequest ProofId loadProof(File javaSourceCode) throws ProblemLoaderException { + return loadProof(javaSourceCode, null, null, null); + } + + /** + * Load a proof file, creates a KeY environment that can be accessed with other methods from + * this facade + * + * @param file Path to the source code folder/file or to a *.proof file + * @param classPaths Optionally: Additional specifications for API classes + * @param bootClassPath Optionally: Different default specifications for Java API + * @param includes Optionally: Additional includes to consider + */ + @JsonRequest ProofId loadProofFile(File file, List classPaths, File bootClassPath, + List includes); +} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/TraceValue.java b/keyext.api/src/main/java/org/keyproject/key/remoteapi/TraceValue.java new file mode 100644 index 00000000000..1d1d4859e57 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/remoteapi/TraceValue.java @@ -0,0 +1,5 @@ +package org.keyproject.key.remoteapi; + +public enum TraceValue { + Off, Message, All; +} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteclient/Client.java b/keyext.api/src/main/java/org/keyproject/key/remoteclient/Client.java new file mode 100644 index 00000000000..4fd0b792189 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/remoteclient/Client.java @@ -0,0 +1,53 @@ +package org.keyproject.key.remoteclient; + +import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer; +import org.keyproject.key.remoteapi.Main; +import org.keyproject.key.remoteapi.KeyApi; + +import java.io.IOException; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.io.PrintWriter; +import java.util.Collections; +import java.util.concurrent.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class Client { + public static void main(String[] args) throws IOException, ExecutionException, InterruptedException, TimeoutException { + PipedInputStream inClient = new PipedInputStream(); + PipedOutputStream outClient = new PipedOutputStream(); + PipedInputStream inServer = new PipedInputStream(); + PipedOutputStream outServer = new PipedOutputStream(); + + inClient.connect(outServer); + outClient.connect(inServer); + + Launcher serverLauncher = Main.launch(outServer, inServer); + + var client = new SimpleClient(); + Launcher clientLauncher = new Launcher.Builder() + .setLocalService(client) + .setRemoteInterfaces(Collections.singleton(KeyApi.class)) + .setInput(inClient) + .setOutput(outClient) + .configureGson(Main::configureJson) + .traceMessages(new PrintWriter(System.err)) + .create(); + + Logger logger = Logger.getLogger(StreamMessageProducer.class.getName()); + logger.setLevel(Level.SEVERE); + + var clientListening = clientLauncher.startListening(); + var serverListening = serverLauncher.startListening(); + + //clientLauncher.getRemoteProxy().examples(); + serverLauncher.getRemoteProxy().sayHello("Alex"); + + serverListening.get(1, TimeUnit.SECONDS); + clientListening.get(1, TimeUnit.SECONDS); + } +} + + diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteclient/ClientApi.java b/keyext.api/src/main/java/org/keyproject/key/remoteclient/ClientApi.java new file mode 100644 index 00000000000..23c3ddf9e39 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/remoteclient/ClientApi.java @@ -0,0 +1,224 @@ +package org.keyproject.key.remoteclient; + +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; +import org.eclipse.lsp4j.jsonrpc.validation.NonNull; + +import javax.annotation.Nullable; + +@JsonSegment +public interface ClientApi { + @JsonNotification + void sayHello(String e); + + /** + * LogTrace Notification (:arrow_left:) + * A notification to log the trace of the server’s execution. The amount and content of these notifications depends on the current trace configuration. If trace is 'off', the server should not send any logTrace notification. If trace is 'messages', the server should not add the 'verbose' field in the LogTraceParams. + *

+ * $/logTrace should be used for systematic trace reporting. For single debugging messages, the server should send window/logMessage notifications. + *

+ * Notification: + *

+ * method: ‘$/logTrace’ + * params: LogTraceParams defined as follows: + */ + @JsonNotification + void logTrace(LogTraceParams params); + + interface LogTraceParams { + /** + * The message to be logged. + */ + @NonNull + String message = null; + /** + * Additional information that can be computed if the `trace` configuration + * is set to `'verbose'` + */ + String verbose = null; + } + + //region Window + + /** + * ShowMessage Notification (:arrow_left:) + * The show message notification is sent from a server to a client to ask the client to display a particular message in the user interface. + *

+ * Notification: + *

+ * method: ‘window/showMessage’ + * params: ShowMessageParams defined as follows: + */ + @JsonNotification + public void ShowMessage(ShowMessageParams params); + + interface ShowMessageParams { + /** + * The message type. See {@link MessageType}. + */ + MessageType type = null; + + /** + * The actual message. + */ + String message = null; + } + + enum MessageType { + + Unused, + /** + * An error message. + */ + Error, + /** + * A warning message. + */ + Warning, + /** + * An information message. + */ + Info, + /** + * A log message. + */ + Log, + /** + * A debug message. + * + * @proposed + * @since 3.18.0 + */ + Debug + } + + /** + * ShowMessage Request (:arrow_right_hook:) + * The show message request is sent from a server to a client to ask the client to display a particular message in the user interface. In addition to the show message notification the request allows to pass actions and to wait for an answer from the client. + */ + @JsonRequest + @Nullable + MessageActionItem ShowMessage(ShowMessageRequestParams params); + + interface ShowMessageRequestParams { + /** + * The message type. See {@link MessageType} + */ + MessageType type = null; + + /** + * The actual message + */ + String message = null; + + /** + * The message action items to present. + */ + MessageActionItem[] actions = new MessageActionItem[0]; + } + + interface MessageActionItem { + /** + * A short title like 'Retry', 'Open Log' etc. + */ + String title = null; + } + + /** + * Show Document Request (:arrow_right_hook:) + * New in version 3.16.0 + *

+ * The show document request is sent from a server to a client to ask the client to display a particular resource referenced by a URI in the user interface. + *

+ * property path (optional): window.showDocument + * property type: ShowDocumentClientCapabilities defined as follows: + */ + @JsonRequest + ShowDocumentResult showDocument(ShowDocumentParams params); + + interface ShowDocumentParams { + /** + * The uri to show. + */ + String uri; + + /** + * Indicates to show the resource in an external program. + * To show, for example, `https://code.visualstudio.com/` + * in the default WEB browser set `external` to `true`. + */ + Boolean external; + + /** + * An optional property to indicate whether the editor + * showing the document should take focus or not. + * Clients might ignore this property if an external + * program is started. + */ + Boolean takeFocus; + + /** + * An optional selection range if the document is a text + * document. Clients might ignore the property if an + * external program is started or the file is not a text + * file. + */ + Range selection; + } + + defined + + interface ShowDocumentResult { + /** + * A boolean indicating if the show was successful. + */ + boolean success; + } + + /** + * LogMessage Notification(:arrow_left:) + *

+ * The log message notification is sent from the server to the client to ask the client to log + * a particular message. + */ + interface LogMessageParams { + /** + * The message type. See {@link MessageType} + */ + MessageType type; + + /** + * The actual message + */ + String message; + } + + /** + Create Work + + Done Progress(:arrow_right_hook:) + + The window/workDoneProgress/ create request is sent from the server to the client to ask + the clie nt to create a work done progress. + */ + + interface WorkDoneProgressCreateParams { + /** + * The token to be used to report progress. + */ + ProgressToken token; + } + + @JsonNotification workDoneProgressCancel(); + WorkDoneProgressCancelParams defined + interface WorkDoneProgressCancelParams { + /** + * The token to be used to report progress. + */ + ProgressToken token; + } + + + //endregion +} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteclient/SimpleClient.java b/keyext.api/src/main/java/org/keyproject/key/remoteclient/SimpleClient.java new file mode 100644 index 00000000000..9578fb8b171 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/remoteclient/SimpleClient.java @@ -0,0 +1,9 @@ +package org.keyproject.key.remoteclient; + +class SimpleClient implements ClientApi { + + @Override + public void sayHello(String e) { + System.out.format("Hello, %s%n", e); + } +} diff --git a/settings.gradle b/settings.gradle index b1ff8b8011b..8be9e773bee 100644 --- a/settings.gradle +++ b/settings.gradle @@ -30,6 +30,8 @@ include "keyext.slicing" include "keyext.caching" include "keyext.isabelletranslation" +include 'keyext.api' + // ENABLE NULLNESS here or on the CLI // This flag is activated to enable the checker framework. // System.setProperty("ENABLE_NULLNESS", "true") From 7b8ca788c6a5ffce7faaa2addad7a0785be7be8e Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Sun, 8 Oct 2023 13:43:38 +0200 Subject: [PATCH 02/35] different clients --- .../java/de/uka/ilkd/key/gui/Example.java | 24 +- keyext.api/build.gradle | 24 +- .../key/{ => api}/FileTypeAdapter.java | 11 +- .../keyproject/key/api/remoteapi/KeyApi.java | 66 ++++++ .../key/{ => api}/remoteapi/KeyApiImpl.java | 28 ++- .../{ => api}/remoteapi/KeyExampleApi.java | 12 +- .../key/{ => api}/remoteapi/KeyMetaApi.java | 13 +- .../{ => api}/remoteapi/MacroDescription.java | 5 +- .../keyproject/key/api/remoteapi/ProofId.java | 7 + .../key/api/remoteapi/ProofLoading.java | 23 ++ .../key/api/remoteapi/TraceValue.java | 8 + .../key/api/remoteclient/ClientApi.java | 76 ++++++ .../org/keyproject/key/remoteapi/KeyApi.java | 58 ----- .../org/keyproject/key/remoteapi/Main.java | 63 ----- .../org/keyproject/key/remoteapi/ProofId.java | 4 - .../key/remoteapi/ProofLoading.java | 67 ------ .../keyproject/key/remoteapi/TraceValue.java | 5 - .../key/remoteclient/ClientApi.java | 224 ------------------ .../key/remoteclient/SimpleClient.java | 9 - keyext.api/src/main/python/keyapi/__init__.py | 17 ++ .../java/org/keyproject/key/api}/Client.java | 30 ++- .../org/keyproject/key/api/SimpleClient.java | 38 +++ 22 files changed, 332 insertions(+), 480 deletions(-) rename keyext.api/src/main/java/org/keyproject/key/{ => api}/FileTypeAdapter.java (70%) create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApi.java rename keyext.api/src/main/java/org/keyproject/key/{ => api}/remoteapi/KeyApiImpl.java (62%) rename keyext.api/src/main/java/org/keyproject/key/{ => api}/remoteapi/KeyExampleApi.java (63%) rename keyext.api/src/main/java/org/keyproject/key/{ => api}/remoteapi/KeyMetaApi.java (72%) rename keyext.api/src/main/java/org/keyproject/key/{ => api}/remoteapi/MacroDescription.java (57%) create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofId.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofLoading.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteapi/TraceValue.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java delete mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApi.java delete mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteapi/Main.java delete mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofId.java delete mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofLoading.java delete mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteapi/TraceValue.java delete mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteclient/ClientApi.java delete mode 100644 keyext.api/src/main/java/org/keyproject/key/remoteclient/SimpleClient.java create mode 100644 keyext.api/src/main/python/keyapi/__init__.py rename keyext.api/src/{main/java/org/keyproject/key/remoteclient => test/java/org/keyproject/key/api}/Client.java (65%) create mode 100644 keyext.api/src/test/java/org/keyproject/key/api/SimpleClient.java diff --git a/key.ui/src/main/java/de/uka/ilkd/key/gui/Example.java b/key.ui/src/main/java/de/uka/ilkd/key/gui/Example.java index 85a85b65df1..3d9c18f83f7 100644 --- a/key.ui/src/main/java/de/uka/ilkd/key/gui/Example.java +++ b/key.ui/src/main/java/de/uka/ilkd/key/gui/Example.java @@ -1,10 +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.gui; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeModel; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; @@ -14,9 +12,15 @@ import java.util.Enumeration; import java.util.List; import java.util.Properties; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * This class wraps a {@link java.io.File} and has a special {@link #toString()} method only using the + * This class wraps a {@link java.io.File} and has a special {@link #toString()} method only using + * the * short file name w/o path. *

* Used for displaying files in the examples list w/o prefix @@ -139,12 +143,12 @@ public String toString() { public void addToTreeModel(DefaultTreeModel model) { DefaultMutableTreeNode node = - findChild((DefaultMutableTreeNode) model.getRoot(), getPath(), 0); + findChild((DefaultMutableTreeNode) model.getRoot(), getPath(), 0); node.add(new DefaultMutableTreeNode(this)); } private DefaultMutableTreeNode findChild(DefaultMutableTreeNode root, String[] path, - int from) { + int from) { if (from == path.length) { return root; } @@ -166,7 +170,7 @@ public boolean hasProof() { } private static StringBuilder extractDescription(File file, StringBuilder sb, - Properties properties) { + Properties properties) { try (BufferedReader r = new BufferedReader(new FileReader(file, StandardCharsets.UTF_8))) { String line; boolean emptyLineSeen = false; @@ -193,4 +197,4 @@ private static StringBuilder extractDescription(File file, StringBuilder sb, } return sb; } -} \ No newline at end of file +} diff --git a/keyext.api/build.gradle b/keyext.api/build.gradle index 463b7dbc11a..c1805081940 100644 --- a/keyext.api/build.gradle +++ b/keyext.api/build.gradle @@ -1,6 +1,28 @@ +plugins { + id 'application' + + // Used to create a single executable jar file with all dependencies + // see task "shadowJar" below + // https://imperceptiblethoughts.com/shadow/ + id 'com.gradleup.shadow' version "9.0.1" +} + +description "Verification server interface via JSON-RPC" + dependencies { implementation(project(":key.core")) implementation(project(":key.ui")) implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.21.1") -} \ No newline at end of file + implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.websocket.jakarta:0.21.1") + implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.websocket.jakarta:0.21.1") + implementation("org.eclipse.jetty.websocket:websocket-javax-server:10.0.16") + implementation("info.picocli:picocli:4.7.5") + //for GraalVM annotationProcessor 'info.picocli:picocli-codegen:4.7.5' +} + + +compileJava { + // for GraalVM options.compilerArgs += ["-Aproject=${project.group}/${project.name}"] +} + diff --git a/keyext.api/src/main/java/org/keyproject/key/FileTypeAdapter.java b/keyext.api/src/main/java/org/keyproject/key/api/FileTypeAdapter.java similarity index 70% rename from keyext.api/src/main/java/org/keyproject/key/FileTypeAdapter.java rename to keyext.api/src/main/java/org/keyproject/key/api/FileTypeAdapter.java index 67240012e90..11ec95d1430 100644 --- a/keyext.api/src/main/java/org/keyproject/key/FileTypeAdapter.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/FileTypeAdapter.java @@ -1,12 +1,15 @@ -package org.keyproject.key; +/* 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.keyproject.key.api; + +import java.io.File; +import java.io.IOException; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; -import java.io.File; -import java.io.IOException; - public class FileTypeAdapter extends TypeAdapter { @Override public void write(JsonWriter out, File value) throws IOException { diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApi.java new file mode 100644 index 00000000000..21c3400574d --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApi.java @@ -0,0 +1,66 @@ +/* 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.keyproject.key.api.remoteapi; + +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; + +public interface KeyApi extends KeyExampleApi, KeyMetaApi { + // region general server management + /** + * Shutdown Request (:leftwards_arrow_with_hook:) + * The shutdown request is sent from the client to the server. It asks the server to shut down, + * but to not exit (otherwise the response might not be delivered correctly to the client). + * There is a separate exit notification that asks the server to exit. Clients must not send any + * notifications other than exit or requests to a server to which they have sent a shutdown + * request. Clients should also wait with sending the exit notification until they have received + * a response from the shutdown request. + * + * If a server receives requests after a shutdown request those requests should error with + * InvalidRequest. + * + * Request: + * + * method: ‘shutdown’ + * params: none + * Response: + * + * result: null + * error: code and message set in case an exception happens during shutdown request. + */ + @JsonRequest + CompletableFuture shutdown(); + + /** + * Exit Notification (:arrow_right:) + * A notification to ask the server to exit its process. The server should exit with success + * code 0 if the shutdown request has been received before; otherwise with error code 1. + *

+ * Notification: + *

+ * method: ‘exit’ + * params: none + */ + @JsonNotification + void exit(); + + + + interface SetTraceParams { + /** + * The new value that should be assigned to the trace setting. + */ + TraceValue value = null; + } + + /** + * SetTrace Notification + * A notification that should be used by the client to modify the trace setting of the server. + */ + @JsonNotification + void setTrace(SetTraceParams params); + // endregion +} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApiImpl.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApiImpl.java similarity index 62% rename from keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApiImpl.java rename to keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApiImpl.java index 0a967998dac..d95e856b4b0 100644 --- a/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApiImpl.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApiImpl.java @@ -1,28 +1,33 @@ -package org.keyproject.key.remoteapi; +/* 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.keyproject.key.api.remoteapi; + +import java.util.List; +import java.util.concurrent.CompletableFuture; import de.uka.ilkd.key.api.KeYApi; import de.uka.ilkd.key.gui.Example; import de.uka.ilkd.key.gui.ExampleChooser; import de.uka.ilkd.key.macros.scripts.ProofScriptCommand; import de.uka.ilkd.key.util.KeYConstants; + import org.eclipse.lsp4j.jsonrpc.CompletableFutures; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; -import java.util.List; -import java.util.concurrent.CompletableFuture; - @JsonSegment -class KeyApiImpl implements KeyApi { +public class KeyApiImpl implements KeyApi { @Override @JsonRequest public CompletableFuture> examples() { - return CompletableFutures.computeAsync((c) -> ExampleChooser.listExamples(ExampleChooser.lookForExamples())); + return CompletableFutures + .computeAsync((c) -> ExampleChooser.listExamples(ExampleChooser.lookForExamples())); } @Override - public Void shutdown() { - return null; + public CompletableFuture shutdown() { + return CompletableFuture.completedFuture(null); } @Override @@ -43,14 +48,13 @@ public CompletableFuture getVersion() { @Override public CompletableFuture> getAvailableMacros() { return CompletableFuture.completedFuture( - KeYApi.getMacroApi().getMacros().stream().map(MacroDescription::from) - .toList() - ); + KeYApi.getMacroApi().getMacros().stream().map(MacroDescription::from) + .toList()); } @Override public CompletableFuture>> getAvailableScriptCommands() { return CompletableFuture.completedFuture( - KeYApi.getScriptCommandApi().getScriptCommands().stream().toList()); + KeYApi.getScriptCommandApi().getScriptCommands().stream().toList()); } } diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyExampleApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyExampleApi.java similarity index 63% rename from keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyExampleApi.java rename to keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyExampleApi.java index f1fe80a6dfc..9f91e82baa0 100644 --- a/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyExampleApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyExampleApi.java @@ -1,12 +1,16 @@ -package org.keyproject.key.remoteapi; +/* 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.keyproject.key.api.remoteapi; + +import java.util.List; +import java.util.concurrent.CompletableFuture; import de.uka.ilkd.key.gui.Example; + import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; -import java.util.List; -import java.util.concurrent.CompletableFuture; - @JsonSegment("examples") public interface KeyExampleApi { @JsonRequest("list") diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyMetaApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyMetaApi.java similarity index 72% rename from keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyMetaApi.java rename to keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyMetaApi.java index e5538b30896..3a1a027f233 100644 --- a/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyMetaApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyMetaApi.java @@ -1,13 +1,16 @@ -package org.keyproject.key.remoteapi; +/* 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.keyproject.key.api.remoteapi; + +import java.util.List; +import java.util.concurrent.CompletableFuture; import de.uka.ilkd.key.macros.scripts.ProofScriptCommand; -import org.eclipse.lsp4j.jsonrpc.CompletableFutures; + import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; -import java.util.List; -import java.util.concurrent.CompletableFuture; - @JsonSegment("meta") public interface KeyMetaApi { @JsonRequest diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/MacroDescription.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/MacroDescription.java similarity index 57% rename from keyext.api/src/main/java/org/keyproject/key/remoteapi/MacroDescription.java rename to keyext.api/src/main/java/org/keyproject/key/api/remoteapi/MacroDescription.java index 2f34d3e03ed..95084b3716d 100644 --- a/keyext.api/src/main/java/org/keyproject/key/remoteapi/MacroDescription.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/MacroDescription.java @@ -1,4 +1,7 @@ -package org.keyproject.key.remoteapi; +/* 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.keyproject.key.api.remoteapi; import de.uka.ilkd.key.macros.ProofMacro; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofId.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofId.java new file mode 100644 index 00000000000..8081219c642 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofId.java @@ -0,0 +1,7 @@ +/* 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.keyproject.key.api.remoteapi; + +public record ProofId(String id) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofLoading.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofLoading.java new file mode 100644 index 00000000000..745a93bc969 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofLoading.java @@ -0,0 +1,23 @@ +/* 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.keyproject.key.api.remoteapi; + +import de.uka.ilkd.key.proof.io.ProblemLoaderException; + +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; + +@JsonSegment +public interface ProofLoading { + @JsonRequest + ProofId loadExample(String id); + + /** + * @param params + * @return + * @throws ProblemLoaderException + */ + @JsonRequest + EnvId load(LoadParams params) throws ProblemLoaderException; +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/TraceValue.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/TraceValue.java new file mode 100644 index 00000000000..11a082303f0 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/TraceValue.java @@ -0,0 +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 org.keyproject.key.api.remoteapi; + +public enum TraceValue { + Off, Message, All; +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java new file mode 100644 index 00000000000..3bbf856af3d --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java @@ -0,0 +1,76 @@ +/* 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.keyproject.key.api.remoteclient; + +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nullable; + +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; + +@JsonSegment +public interface ClientApi { + @JsonNotification + void sayHello(String e); + + /** + * LogTrace Notification (:arrow_left:) + * A notification to log the trace of the server’s execution. The amount and content of these + * notifications depends on the current trace configuration. If trace is 'off', the server + * should not send any logTrace notification. If trace is 'messages', the server should not add + * the 'verbose' field in the LogTraceParams. + *

+ * $/logTrace should be used for systematic trace reporting. For single debugging messages, the + * server should send window/logMessage notifications. + *

+ * Notification: + *

+ * method: ‘$/logTrace’ + * params: LogTraceParams defined as follows: + */ + @JsonNotification + void logTrace(LogTraceParams params); + // region Window + + /** + * ShowMessage Notification (:arrow_left:) + * The show message notification is sent from a server to a client to ask the client to display + * a particular message in the user interface. + *

+ * Notification: + *

+ * method: ‘window/showMessage’ + * params: ShowMessageParams defined as follows: + */ + @JsonNotification("sm") + void showMessage(ShowMessageParams params); + + + + /** + * ShowMessage Request (:arrow_right_hook:) + * The show message request is sent from a server to a client to ask the client to display a + * particular message in the user interface. In addition to the show message notification the + * request allows to pass actions and to wait for an answer from the client. + */ + @JsonRequest + @Nullable + CompletableFuture userResponse(ShowMessageRequestParams params); + + + /** + * Show Document Request (:arrow_right_hook:) + * New in version 3.16.0 + *

+ * The show document request is sent from a server to a client to ask the client to display a + * particular resource referenced by a URI in the user interface. + *

+ * property path (optional): window.showDocument + * property type: ShowDocumentClientCapabilities defined as follows: + */ + @JsonRequest + CompletableFuture showDocument(ShowDocumentParams params); + // endregion +} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApi.java b/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApi.java deleted file mode 100644 index 76c2d1eefdd..00000000000 --- a/keyext.api/src/main/java/org/keyproject/key/remoteapi/KeyApi.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.keyproject.key.remoteapi; - -import de.uka.ilkd.key.gui.Example; -import de.uka.ilkd.key.gui.ExampleChooser; -import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; -import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; - -import java.util.List; -import java.util.concurrent.CompletableFuture; - -public interface KeyApi extends KeyExampleApi, KeyMetaApi { - //region general server management - /** - * Shutdown Request (:leftwards_arrow_with_hook:) - The shutdown request is sent from the client to the server. It asks the server to shut down, but to not exit (otherwise the response might not be delivered correctly to the client). There is a separate exit notification that asks the server to exit. Clients must not send any notifications other than exit or requests to a server to which they have sent a shutdown request. Clients should also wait with sending the exit notification until they have received a response from the shutdown request. - - If a server receives requests after a shutdown request those requests should error with InvalidRequest. - - Request: - - method: ‘shutdown’ - params: none - Response: - - result: null - error: code and message set in case an exception happens during shutdown request. - */ - @JsonRequest - Void shutdown(); - - /** - * Exit Notification (:arrow_right:) - * A notification to ask the server to exit its process. The server should exit with success code 0 if the shutdown request has been received before; otherwise with error code 1. - *

- * Notification: - *

- * method: ‘exit’ - * params: none - */ - @JsonNotification - void exit(); - - - - interface SetTraceParams { - /** - * The new value that should be assigned to the trace setting. - */ - TraceValue value = null; - } - /** - * SetTrace Notification - * A notification that should be used by the client to modify the trace setting of the server. - */ - @JsonNotification - void setTrace(SetTraceParams params); - //endregion -} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/Main.java b/keyext.api/src/main/java/org/keyproject/key/remoteapi/Main.java deleted file mode 100644 index 127b36a702c..00000000000 --- a/keyext.api/src/main/java/org/keyproject/key/remoteapi/Main.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.keyproject.key.remoteapi; - -import com.google.gson.GsonBuilder; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.keyproject.key.FileTypeAdapter; -import org.keyproject.key.remoteclient.ClientApi; - -import java.io.File; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.ExecutionException; - -public class Main { - public static void main(String[] args) throws ExecutionException, InterruptedException { - var future = launch(System.out, System.in); - future.startListening(); - } - - public static Launcher launch(OutputStream out, InputStream in) { - var localServices = getLocalServices(); - var remoteInterfaces = getRemoteInterfaces(); - var launcherBuilder = new Launcher.Builder() - .setOutput(out) - .setInput(in) - .traceMessages(new PrintWriter(System.err)) - .validateMessages(true); - - launcherBuilder.configureGson(Main::configureJson); - - //if (localServices != null && !localServices.isEmpty()) - launcherBuilder.setLocalService(new KeyApiImpl()); - //if (remoteInterfaces != null && !remoteInterfaces.isEmpty()) - launcherBuilder.setRemoteInterface(ClientApi.class); - - var launcher = launcherBuilder.create(); - return launcher; - } - - public static void configureJson(GsonBuilder gsonBuilder) { - gsonBuilder.registerTypeAdapter(File.class, new FileTypeAdapter()); - } - - private static Collection> getRemoteInterfaces() { - return Collections.singleton(ClientApi.class); - /* return ServiceLoader.load(ClientService.class) - .stream() - .map(ServiceLoader.Provider::type) - .collect(Collectors.toSet()); - */ - } - - private static List getLocalServices() { - return Collections.singletonList(new KeyApiImpl()); - /*return ServiceLoader.load(KeyService.class) - .stream().map(ServiceLoader.Provider::get) - .map(it -> (Object) it) - .toList();*/ - } -} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofId.java b/keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofId.java deleted file mode 100644 index 65db8662e4e..00000000000 --- a/keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofId.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.keyproject.key.remoteapi; - -public record ProofId(String id) { -} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofLoading.java b/keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofLoading.java deleted file mode 100644 index 7e99ffd0cf5..00000000000 --- a/keyext.api/src/main/java/org/keyproject/key/remoteapi/ProofLoading.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.keyproject.key.remoteapi; - -import de.uka.ilkd.key.api.ProofManagementApi; -import de.uka.ilkd.key.control.KeYEnvironment; -import de.uka.ilkd.key.proof.io.ProblemLoaderException; -import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; -import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; - -import java.io.File; -import java.util.List; - -@JsonSegment -public interface ProofLoading { - @JsonRequest ProofId loadExample(String id); - - interface LoadParams { - File keyFile; - File javaFile; - - List classPath, - File bootClassPath, List includes - } - - /** - * @param keyFile - * @return - * @throws ProblemLoaderException - */ - @JsonRequest ProofId loadFromKeyFile(File keyFile) throws ProblemLoaderException { - return new ProofManagementApi(KeYEnvironment.load(keyFile)); - } - - /** - * @param location - * @param classPath - * @param bootClassPath - * @param includes - * @return - * @throws ProblemLoaderException - */ - @JsonRequest ProofId loadProof(File location, List classPath, - File bootClassPath, List includes) throws ProblemLoaderException { - return new ProofManagementApi( - KeYEnvironment.load(location, classPath, bootClassPath, includes)); - } - - /** - * @param javaSourceCode - * @return - * @throws ProblemLoaderException - */ - @JsonRequest ProofId loadProof(File javaSourceCode) throws ProblemLoaderException { - return loadProof(javaSourceCode, null, null, null); - } - - /** - * Load a proof file, creates a KeY environment that can be accessed with other methods from - * this facade - * - * @param file Path to the source code folder/file or to a *.proof file - * @param classPaths Optionally: Additional specifications for API classes - * @param bootClassPath Optionally: Different default specifications for Java API - * @param includes Optionally: Additional includes to consider - */ - @JsonRequest ProofId loadProofFile(File file, List classPaths, File bootClassPath, - List includes); -} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteapi/TraceValue.java b/keyext.api/src/main/java/org/keyproject/key/remoteapi/TraceValue.java deleted file mode 100644 index 1d1d4859e57..00000000000 --- a/keyext.api/src/main/java/org/keyproject/key/remoteapi/TraceValue.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.keyproject.key.remoteapi; - -public enum TraceValue { - Off, Message, All; -} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteclient/ClientApi.java b/keyext.api/src/main/java/org/keyproject/key/remoteclient/ClientApi.java deleted file mode 100644 index 23c3ddf9e39..00000000000 --- a/keyext.api/src/main/java/org/keyproject/key/remoteclient/ClientApi.java +++ /dev/null @@ -1,224 +0,0 @@ -package org.keyproject.key.remoteclient; - -import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; -import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; -import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; -import org.eclipse.lsp4j.jsonrpc.validation.NonNull; - -import javax.annotation.Nullable; - -@JsonSegment -public interface ClientApi { - @JsonNotification - void sayHello(String e); - - /** - * LogTrace Notification (:arrow_left:) - * A notification to log the trace of the server’s execution. The amount and content of these notifications depends on the current trace configuration. If trace is 'off', the server should not send any logTrace notification. If trace is 'messages', the server should not add the 'verbose' field in the LogTraceParams. - *

- * $/logTrace should be used for systematic trace reporting. For single debugging messages, the server should send window/logMessage notifications. - *

- * Notification: - *

- * method: ‘$/logTrace’ - * params: LogTraceParams defined as follows: - */ - @JsonNotification - void logTrace(LogTraceParams params); - - interface LogTraceParams { - /** - * The message to be logged. - */ - @NonNull - String message = null; - /** - * Additional information that can be computed if the `trace` configuration - * is set to `'verbose'` - */ - String verbose = null; - } - - //region Window - - /** - * ShowMessage Notification (:arrow_left:) - * The show message notification is sent from a server to a client to ask the client to display a particular message in the user interface. - *

- * Notification: - *

- * method: ‘window/showMessage’ - * params: ShowMessageParams defined as follows: - */ - @JsonNotification - public void ShowMessage(ShowMessageParams params); - - interface ShowMessageParams { - /** - * The message type. See {@link MessageType}. - */ - MessageType type = null; - - /** - * The actual message. - */ - String message = null; - } - - enum MessageType { - - Unused, - /** - * An error message. - */ - Error, - /** - * A warning message. - */ - Warning, - /** - * An information message. - */ - Info, - /** - * A log message. - */ - Log, - /** - * A debug message. - * - * @proposed - * @since 3.18.0 - */ - Debug - } - - /** - * ShowMessage Request (:arrow_right_hook:) - * The show message request is sent from a server to a client to ask the client to display a particular message in the user interface. In addition to the show message notification the request allows to pass actions and to wait for an answer from the client. - */ - @JsonRequest - @Nullable - MessageActionItem ShowMessage(ShowMessageRequestParams params); - - interface ShowMessageRequestParams { - /** - * The message type. See {@link MessageType} - */ - MessageType type = null; - - /** - * The actual message - */ - String message = null; - - /** - * The message action items to present. - */ - MessageActionItem[] actions = new MessageActionItem[0]; - } - - interface MessageActionItem { - /** - * A short title like 'Retry', 'Open Log' etc. - */ - String title = null; - } - - /** - * Show Document Request (:arrow_right_hook:) - * New in version 3.16.0 - *

- * The show document request is sent from a server to a client to ask the client to display a particular resource referenced by a URI in the user interface. - *

- * property path (optional): window.showDocument - * property type: ShowDocumentClientCapabilities defined as follows: - */ - @JsonRequest - ShowDocumentResult showDocument(ShowDocumentParams params); - - interface ShowDocumentParams { - /** - * The uri to show. - */ - String uri; - - /** - * Indicates to show the resource in an external program. - * To show, for example, `https://code.visualstudio.com/` - * in the default WEB browser set `external` to `true`. - */ - Boolean external; - - /** - * An optional property to indicate whether the editor - * showing the document should take focus or not. - * Clients might ignore this property if an external - * program is started. - */ - Boolean takeFocus; - - /** - * An optional selection range if the document is a text - * document. Clients might ignore the property if an - * external program is started or the file is not a text - * file. - */ - Range selection; - } - - defined - - interface ShowDocumentResult { - /** - * A boolean indicating if the show was successful. - */ - boolean success; - } - - /** - * LogMessage Notification(:arrow_left:) - *

- * The log message notification is sent from the server to the client to ask the client to log - * a particular message. - */ - interface LogMessageParams { - /** - * The message type. See {@link MessageType} - */ - MessageType type; - - /** - * The actual message - */ - String message; - } - - /** - Create Work - - Done Progress(:arrow_right_hook:) - - The window/workDoneProgress/ create request is sent from the server to the client to ask - the clie nt to create a work done progress. - */ - - interface WorkDoneProgressCreateParams { - /** - * The token to be used to report progress. - */ - ProgressToken token; - } - - @JsonNotification workDoneProgressCancel(); - WorkDoneProgressCancelParams defined - interface WorkDoneProgressCancelParams { - /** - * The token to be used to report progress. - */ - ProgressToken token; - } - - - //endregion -} diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteclient/SimpleClient.java b/keyext.api/src/main/java/org/keyproject/key/remoteclient/SimpleClient.java deleted file mode 100644 index 9578fb8b171..00000000000 --- a/keyext.api/src/main/java/org/keyproject/key/remoteclient/SimpleClient.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.keyproject.key.remoteclient; - -class SimpleClient implements ClientApi { - - @Override - public void sayHello(String e) { - System.out.format("Hello, %s%n", e); - } -} diff --git a/keyext.api/src/main/python/keyapi/__init__.py b/keyext.api/src/main/python/keyapi/__init__.py new file mode 100644 index 00000000000..8523f3801f5 --- /dev/null +++ b/keyext.api/src/main/python/keyapi/__init__.py @@ -0,0 +1,17 @@ +from keyapi.rpc import JsonRPCHandler, Client + + +class KeyClient(Client): + def __int__(self): + pass + + def handle(self, res): + print(res) + + +class KeyStub(JsonRPCHandler): + def __init__(self, input, output): + super().__init__(input, output) + + def list_examples(self): + return self._send("examples/list", []) diff --git a/keyext.api/src/main/java/org/keyproject/key/remoteclient/Client.java b/keyext.api/src/test/java/org/keyproject/key/api/Client.java similarity index 65% rename from keyext.api/src/main/java/org/keyproject/key/remoteclient/Client.java rename to keyext.api/src/test/java/org/keyproject/key/api/Client.java index 4fd0b792189..5ff2d0725f4 100644 --- a/keyext.api/src/main/java/org/keyproject/key/remoteclient/Client.java +++ b/keyext.api/src/test/java/org/keyproject/key/api/Client.java @@ -1,21 +1,27 @@ -package org.keyproject.key.remoteclient; - -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer; -import org.keyproject.key.remoteapi.Main; -import org.keyproject.key.remoteapi.KeyApi; +/* 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.keyproject.key.api; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.PrintWriter; import java.util.Collections; -import java.util.concurrent.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; +import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer; +import org.keyproject.key.api.remoteapi.KeyApi; +import org.keyproject.key.api.remoteclient.ClientApi; + public class Client { - public static void main(String[] args) throws IOException, ExecutionException, InterruptedException, TimeoutException { + public static void main(String[] args) + throws IOException, ExecutionException, InterruptedException, TimeoutException { PipedInputStream inClient = new PipedInputStream(); PipedOutputStream outClient = new PipedOutputStream(); PipedInputStream inServer = new PipedInputStream(); @@ -24,7 +30,7 @@ public static void main(String[] args) throws IOException, ExecutionException, I inClient.connect(outServer); outClient.connect(inServer); - Launcher serverLauncher = Main.launch(outServer, inServer); + Launcher serverLauncher = StartServer.launch(outServer, inServer); var client = new SimpleClient(); Launcher clientLauncher = new Launcher.Builder() @@ -32,7 +38,7 @@ public static void main(String[] args) throws IOException, ExecutionException, I .setRemoteInterfaces(Collections.singleton(KeyApi.class)) .setInput(inClient) .setOutput(outClient) - .configureGson(Main::configureJson) + .configureGson(StartServer::configureJson) .traceMessages(new PrintWriter(System.err)) .create(); @@ -42,12 +48,10 @@ public static void main(String[] args) throws IOException, ExecutionException, I var clientListening = clientLauncher.startListening(); var serverListening = serverLauncher.startListening(); - //clientLauncher.getRemoteProxy().examples(); + // clientLauncher.getRemoteProxy().examples(); serverLauncher.getRemoteProxy().sayHello("Alex"); serverListening.get(1, TimeUnit.SECONDS); clientListening.get(1, TimeUnit.SECONDS); } } - - diff --git a/keyext.api/src/test/java/org/keyproject/key/api/SimpleClient.java b/keyext.api/src/test/java/org/keyproject/key/api/SimpleClient.java new file mode 100644 index 00000000000..6e92beff41c --- /dev/null +++ b/keyext.api/src/test/java/org/keyproject/key/api/SimpleClient.java @@ -0,0 +1,38 @@ +/* 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.keyproject.key.api; + +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nullable; + +import org.keyproject.key.api.remoteclient.*; + +class SimpleClient implements ClientApi { + + @Override + public void sayHello(String e) { + System.out.format("Hello, %s%n", e); + } + + @Override + public void logTrace(LogTraceParams params) { + + } + + @Override + public void userResponse(ShowMessageParams params) { + + } + + @Nullable + @Override + public CompletableFuture userResponse(ShowMessageRequestParams params) { + return null; + } + + @Override + public CompletableFuture showDocument(ShowDocumentParams params) { + return null; + } +} From 13b6f11f742a33de4cd0cc12beb74af75d5c1f5f Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Fri, 13 Oct 2023 13:26:27 +0200 Subject: [PATCH 03/35] wip --- .../org/keyproject/key/api/StartServer.java | 213 ++++++++++++++++++ .../keyproject/key/api/remoteapi/EnvId.java | 11 + .../key/api/remoteapi/LoadParams.java | 12 + .../api/remoteclient/LogMessageParams.java | 17 ++ .../key/api/remoteclient/LogTraceParams.java | 20 ++ .../api/remoteclient/MessageActionItem.java | 11 + .../key/api/remoteclient/MessageType.java | 31 +++ .../api/remoteclient/ShowDocumentParams.java | 37 +++ .../api/remoteclient/ShowDocumentResult.java | 12 + .../api/remoteclient/ShowMessageParams.java | 17 ++ .../ShowMessageRequestParams.java | 22 ++ keyext.api/src/main/python/keyapi/rpc.py | 76 +++++++ keyext.api/src/main/python/main.py | 13 ++ 13 files changed, 492 insertions(+) create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/StartServer.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvId.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteapi/LoadParams.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteclient/LogMessageParams.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteclient/LogTraceParams.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteclient/MessageActionItem.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteclient/MessageType.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowDocumentParams.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowDocumentResult.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowMessageParams.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowMessageRequestParams.java create mode 100644 keyext.api/src/main/python/keyapi/rpc.py create mode 100644 keyext.api/src/main/python/main.py diff --git a/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java b/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java new file mode 100644 index 00000000000..246181ebc43 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java @@ -0,0 +1,213 @@ +/* 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.keyproject.key.api; + + +import java.io.*; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; +import javax.annotation.Nullable; + +import com.google.gson.GsonBuilder; +import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.eclipse.lsp4j.websocket.jakarta.WebSocketLauncherBuilder; +import org.keyproject.key.api.remoteapi.KeyApiImpl; +import org.keyproject.key.api.remoteclient.ClientApi; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import picocli.CommandLine; +import picocli.CommandLine.Option; + +/** + * @author Alexander Weigl + * @version 1 (07.10.23) + */ +@CommandLine.Command +public class StartServer implements Runnable { + private static final Logger LOGGER = LoggerFactory.getLogger(StartServer.class); + + // region CLI arguments + @Option(names = "--std", description = "use stdout and stdin for communication") + boolean stdStreams; + @Option(names = "--trace", description = "use stdout and stdin for communication") + boolean enableTracing; + + @Option(names = "--server", paramLabel = "PORT", description = "enables the TCP server mode") + @Nullable + Integer serverPort; + + @Option(names = "--connectTo", paramLabel = "PORT", description = "enables the TCP client mode") + @Nullable + Integer clientPort; + + + @Option(names = "--infile", paramLabel = "FILE or PIPE", + description = "read from named pipe or file") + @Nullable + File inFile; + + @Option(names = "--outfile", paramLabel = "FILE or PIPE", + description = "write to named pipe or file") + File outFile; + + @Option(names = { "-h", "--help" }, usageHelp = true, description = "display a help message") + boolean helpRequested = false; + + @Option(names = "--websocket") + boolean websocket; + // endregion + + public static void main(String[] args) { + new CommandLine(new StartServer()).execute(args); + } + + private InputStream in; + private OutputStream out; + + private void establishStreams() throws IOException { + in = System.in; + out = System.out; + + if (stdStreams) { + return; + } + + if (clientPort != null) { + var socket = new Socket(InetAddress.getLocalHost(), clientPort); + socket.setKeepAlive(true); + socket.setTcpNoDelay(true); + in = socket.getInputStream(); + out = socket.getOutputStream(); + return; + } + + if (serverPort != null) { + var server = new ServerSocket(serverPort); + LOGGER.info("Waiting on port {}", serverPort); + var socket = server.accept(); + LOGGER.info("Connection to client established: {}", socket.getRemoteSocketAddress()); + socket.setKeepAlive(true); + socket.setTcpNoDelay(true); + in = socket.getInputStream(); + out = socket.getOutputStream(); + return; + } + + + if (inFile != null) { + in = new FileInputStream(inFile); + } + + if (outFile != null) { + out = new FileOutputStream(outFile); + } + + if (out == null || in == null) { + throw new IllegalStateException("Could not initialize the streams"); + } + } + + + @Override + public void run() { + if (helpRequested) { + return; + } + + /* + * var server = new Server(); + * var connector = new ServerConnector(); + * server.addConnector(connector); + * // Setup the basic application "context" for this application at "/" + * // This is also known as the handler tree (in jetty speak) + * var context = new ServletContextHandler(ServletContextHandler.SESSIONS); + * context.setContextPath("/"); + * server.setHandler(context); + * + * // Initialize javax.websocket layer + * JavaxWebSocketServletContainerInitializer.configure(context, (servletContext, + * wsContainer) -> + * { + * // Configure defaults for container + * wsContainer.setDefaultMaxTextMessageBufferSize(65535); + * + * // Add WebSocket endpoint to javax.websocket layer + * wsContainer.addEndpoint(WebSocketEndpoint.class); + * }); + */ + + + try { + if (websocket) { + var launcherBuilder = new WebSocketLauncherBuilder() + .setOutput(out) + .setInput(in) + .traceMessages(new PrintWriter(System.err)) + .validateMessages(true); + launcherBuilder.configureGson(StartServer::configureJson); + launcherBuilder.setLocalService(new KeyApiImpl()); + launcherBuilder.setRemoteInterface(ClientApi.class); + launcherBuilder.create().startListening().get(); + } else { + establishStreams(); + try (var lin = in; var lout = out) { + var listener = launch(lout, lin); + LOGGER.info("JSON-RPC is listening for requests"); + listener.startListening().get(); + } + } + } catch (IOException | InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + public static void configureJson(GsonBuilder gsonBuilder) { + gsonBuilder.registerTypeAdapter(File.class, new FileTypeAdapter()); + } + + public static Launcher launch(OutputStream out, InputStream in) { + // var localServices = getLocalServices(); + // var remoteInterfaces = getRemoteInterfaces(); + var launcherBuilder = new Launcher.Builder() + .setOutput(out) + .setInput(in) + .traceMessages(new PrintWriter(System.err)) + .validateMessages(true); + + launcherBuilder.configureGson(StartServer::configureJson); + + // if (localServices != null && !localServices.isEmpty()) + launcherBuilder.setLocalService(new KeyApiImpl()); + // if (remoteInterfaces != null && !remoteInterfaces.isEmpty()) + launcherBuilder.setRemoteInterface(ClientApi.class); + + return launcherBuilder.create(); + } + + + private static Collection> getRemoteInterfaces() { + return Collections.singleton(ClientApi.class); + /* + * return ServiceLoader.load(ClientService.class) + * .stream() + * .map(ServiceLoader.Provider::type) + * .collect(Collectors.toSet()); + */ + } + + private static List getLocalServices() { + return Collections.singletonList(new KeyApiImpl()); + /* + * return ServiceLoader.load(KeyService.class) + * .stream().map(ServiceLoader.Provider::get) + * .map(it -> (Object) it) + * .toList(); + */ + } +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvId.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvId.java new file mode 100644 index 00000000000..52d4569aa3f --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvId.java @@ -0,0 +1,11 @@ +/* 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.keyproject.key.api.remoteapi; + +/** + * @author Alexander Weigl + * @version 1 (06.10.23) + */ +public record EnvId(String id) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/LoadParams.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/LoadParams.java new file mode 100644 index 00000000000..c22064a856a --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/LoadParams.java @@ -0,0 +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 org.keyproject.key.api.remoteapi; + +import java.io.File; +import java.util.List; + +public record LoadParams(File keyFile, File javaFile, List classPath, File bootClassPath, + List includes) { + +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/LogMessageParams.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/LogMessageParams.java new file mode 100644 index 00000000000..a399c097d2a --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/LogMessageParams.java @@ -0,0 +1,17 @@ +/* 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.keyproject.key.api.remoteclient; + +record LogMessageParams( + /** + * The message type. See {@link MessageType} + */ + MessageType type, + + /** + * The actual message + */ + String message) { + +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/LogTraceParams.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/LogTraceParams.java new file mode 100644 index 00000000000..a9f9bde6315 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/LogTraceParams.java @@ -0,0 +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 org.keyproject.key.api.remoteclient; + +import org.eclipse.lsp4j.jsonrpc.validation.NonNull; + +public record LogTraceParams( + /** + * The message to be logged. + */ + @NonNull String messag, + + /** + * Additional information that can be computed if the `trace` configuration + * is set to `'verbose'` + */ + String verbose) { + +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/MessageActionItem.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/MessageActionItem.java new file mode 100644 index 00000000000..e6948bf3dec --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/MessageActionItem.java @@ -0,0 +1,11 @@ +/* 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.keyproject.key.api.remoteclient; + +public record MessageActionItem( + /** + * A short title like 'Retry', 'Open Log' etc. + */ + String title) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/MessageType.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/MessageType.java new file mode 100644 index 00000000000..8564f136f36 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/MessageType.java @@ -0,0 +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 org.keyproject.key.api.remoteclient; + +public enum MessageType { + Unused, + /** + * An error message. + */ + Error, + /** + * A warning message. + */ + Warning, + /** + * An information message. + */ + Info, + /** + * A log message. + */ + Log, + /** + * A debug message. + * + * @proposed + * @since 3.18.0 + */ + Debug +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowDocumentParams.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowDocumentParams.java new file mode 100644 index 00000000000..919ed5b645d --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowDocumentParams.java @@ -0,0 +1,37 @@ +/* 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.keyproject.key.api.remoteclient; + +import de.uka.ilkd.key.pp.Range; + +public record ShowDocumentParams( + /** + * The uri to show. + */ + String uri, + + /** + * Indicates to show the resource in an external program. + * To show, for example, `https://code.visualstudio.com/` + * in the default WEB browser set `external` to `true`. + */ + Boolean external, + + /** + * An optional property to indicate whether the editor + * showing the document should take focus or not. + * Clients might ignore this property if an external + * program is started. + */ + Boolean takeFocus, + + /** + * An optional selection range if the document is a text + * document. Clients might ignore the property if an + * external program is started or the file is not a text + * file. + */ + Range selection) { + +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowDocumentResult.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowDocumentResult.java new file mode 100644 index 00000000000..616a0332897 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowDocumentResult.java @@ -0,0 +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 org.keyproject.key.api.remoteclient; + +public record ShowDocumentResult( + /** + * A boolean indicating if the show was successful. + */ + boolean success) { + +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowMessageParams.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowMessageParams.java new file mode 100644 index 00000000000..5b0a6fc0286 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowMessageParams.java @@ -0,0 +1,17 @@ +/* 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.keyproject.key.api.remoteclient; + +public record ShowMessageParams( + /** + * The message type. See {@link MessageType}. + */ + MessageType type, + + /** + * The actual message. + */ + String message) { + +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowMessageRequestParams.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowMessageRequestParams.java new file mode 100644 index 00000000000..1e893bbe067 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowMessageRequestParams.java @@ -0,0 +1,22 @@ +/* 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.keyproject.key.api.remoteclient; + +public record ShowMessageRequestParams( + /** + * The message type. See {@link MessageType} + */ + MessageType type, + + /** + * The actual message + */ + String message, + + /** + * The message action items to present. + * + */ + MessageActionItem[] actions) { +} diff --git a/keyext.api/src/main/python/keyapi/rpc.py b/keyext.api/src/main/python/keyapi/rpc.py new file mode 100644 index 00000000000..89dc6f3e041 --- /dev/null +++ b/keyext.api/src/main/python/keyapi/rpc.py @@ -0,0 +1,76 @@ +import abc +import json +from abc import abstractmethod +from multiprocessing import Process, SimpleQueue, Lock, Value + + +class Client(abc.ABC): + @abstractmethod + def handle(self, response): + pass + + +class JsonRPCHandler: + client: Client + + def __init__(self, in_stream, out_stream): + self.input = in_stream + self.out = out_stream + self.__id = 0 + self.client: Client + + self._events = dict() + self._responses = dict() + + # t: Process = Process(target=self.__read) + # t.start() + + def read_message(self): + length = 0 + for clength in self.input.readlines(): + if clength.startswith("Content-Length:"): + length = int(clength[14:]) + break + + payload = self.input.read(2 + length) + r = json.loads(payload) + if "id" in r: # JSON response for request + rid = r["id"] + # self._responses[rid] = r + # if rid in self._events: + # self._events[rid].set() + else: # JSON notification + self.client.handle(r) + return r + + def __read(self): + while True: + self.read_message() + + # def __create_event(self, number): + # self._events[number] = Event() + + def _send(self, method, params): + self.__id += 1 + id = self.__id + # self.__create_event(self.__id) + req = {"jsonrpc": "2.0", "method": method, "params": params, "id": self.__id} + + self._write(json.dumps(req)) + # self._wait_for(self.__id) + + r = dict() + while "id" in r and str(r[id]) != str(id): + r = self.read_message() + return r + + def _write(self, msg): + length = len(msg) + self.out.write(f"Content-Length: {length}\r\n") + self.out.write("\r\n") + self.out.write(msg) + self.out.write("\r\n") + self.out.flush() + + # def _wait_for(self, rid): + # self._events[rid].wait() diff --git a/keyext.api/src/main/python/main.py b/keyext.api/src/main/python/main.py new file mode 100644 index 00000000000..a5cb45321d8 --- /dev/null +++ b/keyext.api/src/main/python/main.py @@ -0,0 +1,13 @@ +import socket +from keyapi import KeyStub, KeyClient + +if __name__ == "__main__": + target = ("localhost", 5151) + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect(target) + input = s.makefile("r", newline="\r\n") + output = s.makefile("w", newline="\r\n") + stub = KeyStub(input, output) + stub.client = KeyClient() + print(stub.list_examples()) From ffe5b002cbfeaacc27868765b14a461bb177af80 Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Sun, 15 Oct 2023 17:27:16 +0200 Subject: [PATCH 04/35] Creating an JSON-RPC API # Conflicts: # keyext.api/src/main/java/org/keyproject/key/api/FileTypeAdapter.java # keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvId.java # keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApiImpl.java # keyext.api/src/main/java/org/keyproject/key/api/remoteapi/LoadParams.java # keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofId.java # keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofLoading.java # Conflicts: # key.core/src/main/java/de/uka/ilkd/key/proof/Proof.java --- .../java/de/uka/ilkd/key/Identifiable.java | 14 ++ .../uka/ilkd/key/control/KeYEnvironment.java | 39 ++++ .../main/java/de/uka/ilkd/key/proof/Goal.java | 42 +++++ .../java/de/uka/ilkd/key/proof/Proof.java | 14 +- .../de/uka/ilkd/key/gui/ExampleChooser.java | 2 +- keyext.api.doc/build.gradle | 4 + .../src/main/java/ExtractMetaData.java | 167 +++++++++++++++++ keyext.api/build.gradle | 3 + .../org/keyproject/key/api/KeyApiImpl.java | 174 ++++++++++++++++++ .../org/keyproject/key/api/StartServer.java | 61 +----- .../key/api/adapters/KeyAdapter.java | 104 +++++++++++ .../key/api/adapters/package-info.java | 7 + .../keyproject/key/api/data/ContractDesc.java | 11 ++ .../keyproject/key/api/data/FunctionDesc.java | 13 ++ .../org/keyproject/key/api/data/GoalText.java | 13 ++ .../keyproject/key/api/data/LoadParams.java | 19 ++ .../{remoteapi => data}/MacroDescription.java | 8 +- .../key/api/data/MacroStatistic.java | 11 ++ .../org/keyproject/key/api/data/NodeDesc.java | 11 ++ .../key/api/data/PredicateDesc.java | 11 ++ .../org/keyproject/key/api/data/PrintId.java | 11 ++ .../key/api/data/ProblemDefinition.java | 20 ++ .../keyproject/key/api/data/ProofLoading.java | 47 +++++ .../org/keyproject/key/api/data/SortDesc.java | 11 ++ .../key/api/data/StreategyOptions.java | 11 ++ .../keyproject/key/api/data/TermAction.java | 11 ++ .../keyproject/key/api/data/TermActionId.java | 11 ++ .../api/{remoteapi => data}/TraceValue.java | 2 +- .../keyproject/key/api/data/TreeNodeDesc.java | 11 ++ .../keyproject/key/api/data/TreeNodeId.java | 11 ++ .../keyproject/key/api/remoteapi/EnvApi.java | 35 ++++ .../{KeyExampleApi.java => ExampleApi.java} | 2 +- .../keyproject/key/api/remoteapi/GoalApi.java | 36 ++++ .../keyproject/key/api/remoteapi/KeyApi.java | 65 +------ .../{KeyMetaApi.java => MetaApi.java} | 11 +- .../key/api/remoteapi/ProofApi.java | 53 ++++++ .../key/api/remoteapi/ProofTreeApi.java | 30 +++ .../key/api/remoteapi/ServerManagement.java | 70 +++++++ .../key/api/remoteclient/ClientApi.java | 2 +- .../org/keyproject/key/api/SimpleClient.java | 2 +- .../key/api/{Client.java => TestRpc.java} | 46 ++++- settings.gradle | 1 + 42 files changed, 1090 insertions(+), 137 deletions(-) create mode 100644 key.core/src/main/java/de/uka/ilkd/key/Identifiable.java create mode 100644 keyext.api.doc/build.gradle create mode 100644 keyext.api.doc/src/main/java/ExtractMetaData.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/adapters/package-info.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/ContractDesc.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/FunctionDesc.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/GoalText.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/LoadParams.java rename keyext.api/src/main/java/org/keyproject/key/api/{remoteapi => data}/MacroDescription.java (80%) create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/MacroStatistic.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/PredicateDesc.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/PrintId.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/ProblemDefinition.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/ProofLoading.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/SortDesc.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/StreategyOptions.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/TermAction.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/TermActionId.java rename keyext.api/src/main/java/org/keyproject/key/api/{remoteapi => data}/TraceValue.java (83%) create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/TreeNodeDesc.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/TreeNodeId.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvApi.java rename keyext.api/src/main/java/org/keyproject/key/api/remoteapi/{KeyExampleApi.java => ExampleApi.java} (94%) create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java rename keyext.api/src/main/java/org/keyproject/key/api/remoteapi/{KeyMetaApi.java => MetaApi.java} (71%) create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofApi.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofTreeApi.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ServerManagement.java rename keyext.api/src/test/java/org/keyproject/key/api/{Client.java => TestRpc.java} (59%) diff --git a/key.core/src/main/java/de/uka/ilkd/key/Identifiable.java b/key.core/src/main/java/de/uka/ilkd/key/Identifiable.java new file mode 100644 index 00000000000..946fadb694a --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/Identifiable.java @@ -0,0 +1,14 @@ +/* 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; + +/** + * @author Alexander Weigl + * @version 1 (14.10.23) + */ +public interface Identifiable { + default String identification() { + return getClass().getName() + "_" + hashCode(); + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/control/KeYEnvironment.java b/key.core/src/main/java/de/uka/ilkd/key/control/KeYEnvironment.java index 74e7e5c33b7..e04d06c3a37 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/control/KeYEnvironment.java +++ b/key.core/src/main/java/de/uka/ilkd/key/control/KeYEnvironment.java @@ -333,6 +333,45 @@ public boolean isDisposed() { return proofScript; } + /** + * Retrieve a list of all available contracts for all known Java types. + * + * @return a non-null list, possible empty + */ + public List getAvailableContracts() { + List proofContracts = new ArrayList<>(512); + Set kjts = getJavaInfo().getAllKeYJavaTypes(); + for (KeYJavaType type : kjts) { + if (!KeYTypeUtil.isLibraryClass(type)) { + ImmutableSet targets = + getSpecificationRepository().getContractTargets(type); + for (IObserverFunction target : targets) { + ImmutableSet contracts = + getSpecificationRepository().getContracts(type, target); + for (Contract contract : contracts) { + proofContracts.add(contract); + } + } + } + } + return proofContracts; + } + + + /** + * Constructs a list containing all known rules that are registered in the current + * environment. + * + * @return always returns a non-null list + */ + public List getRules() { + var rules = new ArrayList(4096); + rules.addAll(getInitConfig().activatedTaclets()); + getInitConfig().builtInRules().forEach(rules::add); + return rules; + } + + /** * constructs the possible proof contracts from the java info in the environment. */ diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/Goal.java b/key.core/src/main/java/de/uka/ilkd/key/proof/Goal.java index f9f410bd843..6559425729c 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/Goal.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/Goal.java @@ -13,6 +13,7 @@ import de.uka.ilkd.key.logic.op.ProgramVariable; import de.uka.ilkd.key.pp.LogicPrinter; import de.uka.ilkd.key.pp.NotationInfo; +import de.uka.ilkd.key.proof.mgt.RuleJustification; import de.uka.ilkd.key.proof.proofevent.NodeChangeJournal; import de.uka.ilkd.key.proof.proofevent.RuleAppInfo; import de.uka.ilkd.key.rule.AbstractExternalSolverRuleApp; @@ -23,6 +24,7 @@ import de.uka.ilkd.key.rule.merge.MergeRule; import de.uka.ilkd.key.strategy.QueueRuleApplicationManager; import de.uka.ilkd.key.strategy.Strategy; +import de.uka.ilkd.key.util.MiscTools; import de.uka.ilkd.key.util.properties.MapProperties; import de.uka.ilkd.key.util.properties.Properties; import de.uka.ilkd.key.util.properties.Properties.Property; @@ -753,6 +755,13 @@ public List getAllBuiltInRuleApps() { return ruleApps; } + /** + * Return a list with available taclet application on this goal. + */ + public List getAllTacletApps() { + return getAllTacletApps(proof().getServices()); + } + public List getAllTacletApps(Services services) { RuleAppIndex index = ruleAppIndex(); index.autoModeStopped(); @@ -777,4 +786,37 @@ protected boolean filter(Taclet taclet) { return allApps; } + /** + * Returns a list with all known rule applications within this proof goal. + */ + public List getAllAvailableRules() { + var taclets = getAllTacletApps(); + var builtin = getAllBuiltInRuleApps(); + builtin.addAll(taclets); + return builtin; + } + + public List getAvailableRules() { + var s = new ArrayList(2048); + for (final BuiltInRule br : ruleAppIndex().builtInRuleAppIndex() + .builtInRuleIndex().rules()) { + s.add(br); + } + + Set set = ruleAppIndex().tacletIndex().allNoPosTacletApps(); + OneStepSimplifier simplifier = MiscTools.findOneStepSimplifier(proof()); + if (simplifier != null && !simplifier.isShutdown()) { + set.addAll(simplifier.getCapturedTaclets()); + } + + for (final NoPosTacletApp app : set) { + RuleJustification just = proof().mgt().getJustification(app); + if (just == null) { + continue; // do not break system because of this + } + s.add(app.taclet()); // TODO not good + } + return s; + } + } 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 a6f5cab1498..1859a048d1e 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 @@ -11,10 +11,10 @@ import java.util.function.Consumer; import java.util.function.Predicate; +import de.uka.ilkd.key.Identifiable; import de.uka.ilkd.key.java.JavaInfo; 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.logic.*; import de.uka.ilkd.key.pp.AbbrevMap; import de.uka.ilkd.key.proof.calculus.JavaDLSequentKit; import de.uka.ilkd.key.proof.event.ProofDisposedEvent; @@ -58,7 +58,7 @@ * goals, namespaces and several other information about the current state of the proof. */ @NullMarked -public class Proof implements ProofObject, Named { +public class Proof implements ProofObject, Named, Identifiable { /** * The time when the {@link Proof} instance was created. @@ -1380,4 +1380,12 @@ public void printSymbols(PrintWriter ps) { public long getCreationTime() { return creationTime; } + + /** + * {@inheritDoc} + */ + @Override + public String identification() { + return getClass().getName() + "_" + name + "_" + hashCode(); + } } diff --git a/key.ui/src/main/java/de/uka/ilkd/key/gui/ExampleChooser.java b/key.ui/src/main/java/de/uka/ilkd/key/gui/ExampleChooser.java index 02505ac383e..4df6d2a0c39 100644 --- a/key.ui/src/main/java/de/uka/ilkd/key/gui/ExampleChooser.java +++ b/key.ui/src/main/java/de/uka/ilkd/key/gui/ExampleChooser.java @@ -313,7 +313,7 @@ public static List listExamples(File examplesDir) { new BufferedReader(new FileReader(index, StandardCharsets.UTF_8))) { while ((line = br.readLine()) != null) { line = line.trim(); - if (line.startsWith("#") || line.length() == 0) { + if (line.startsWith("#") || line.isEmpty()) { continue; } File f = new File(examplesDir, line); diff --git a/keyext.api.doc/build.gradle b/keyext.api.doc/build.gradle new file mode 100644 index 00000000000..f7aadd6467c --- /dev/null +++ b/keyext.api.doc/build.gradle @@ -0,0 +1,4 @@ +dependencies { + implementation("com.github.javaparser:javaparser-core:3.25.5") + implementation("com.github.javaparser:javaparser-symbol-solver-core:3.25.5") +} \ No newline at end of file diff --git a/keyext.api.doc/src/main/java/ExtractMetaData.java b/keyext.api.doc/src/main/java/ExtractMetaData.java new file mode 100644 index 00000000000..07106c02d9f --- /dev/null +++ b/keyext.api.doc/src/main/java/ExtractMetaData.java @@ -0,0 +1,167 @@ +/* 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 */ +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; + +import com.github.javaparser.ParseResult; +import com.github.javaparser.ParserConfiguration; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.body.TypeDeclaration; +import com.github.javaparser.ast.expr.MemberValuePair; +import com.github.javaparser.ast.nodeTypes.NodeWithJavadoc; +import com.github.javaparser.resolution.SymbolResolver; +import com.github.javaparser.resolution.TypeSolver; +import com.github.javaparser.symbolsolver.JavaSymbolSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.TypeSolverBuilder; +import com.github.javaparser.utils.SourceRoot; + +/** + * @author Alexander Weigl + * @version 1 (14.10.23) + */ +public class ExtractMetaData { + private static PrintWriter out; + + public static void main(String[] args) throws IOException { + System.out.println("XXX" + Arrays.toString(args)); + ParserConfiguration config = new ParserConfiguration(); + config.setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17_PREVIEW); + /* + * config.setAttributeComments(true); + * config.setLexicalPreservationEnabled(false); + * config.setStoreTokens(false); + * config.setIgnoreAnnotationsWhenAttributingComments(true); + * config.setDoNotAssignCommentsPrecedingEmptyLines(true); + */ + // var root = Paths.get(args[0]); + TypeSolver typeSolver = new TypeSolverBuilder() + .withSourceCode("keyext.api/src/main/java") + .withSourceCode("key.core/src/main/java") + .withSourceCode("key.util/src/main/java") + .withCurrentJRE() + .build(); + SymbolResolver symbolResolver = new JavaSymbolSolver(typeSolver); + config.setSymbolResolver(symbolResolver); + var source = new SourceRoot(Paths.get("keyext.api", "src", "main", "java"), config); + var cu = source.tryToParse(); + var jsonSegment = Comparator.comparing(ExtractMetaData::getJsonSegment); + var segments = cu.stream().filter(ParseResult::isSuccessful) + .filter(it -> it.getResult().get().getPrimaryType().isPresent()) + .map(it -> it.getResult().get().getPrimaryType().get()) + .filter(ExtractMetaData::hasJsonSegment) + .sorted(jsonSegment) + .toList(); + + try (var out = new PrintWriter(new FileWriter("doc.md"))) { + ExtractMetaData.out = out; + printHeader(segments); + segments.forEach(ExtractMetaData::printDocumentation); + printFooter(segments); + } + + // "org.keyproject.key.api.remoteapi.KeyApi"; + } + + private static void printDocumentation(TypeDeclaration typeDeclaration) { + out.format("## Group: %s%n%n", getJsonSegment(typeDeclaration)); + out.println(getJavaDoc(typeDeclaration)); + out.println("\n"); + + for (MethodDeclaration method : typeDeclaration.getMethods()) { + if (!isExported(method)) + continue; + + String callName = getCallName(method, typeDeclaration); + + out.format("### %s (type: %s) %n%n", callName, type(method)); + + out.println(getJavaDoc(method)); + + out.println(); + } + } + + private static String getCallName(MethodDeclaration m, TypeDeclaration td) { + var annotationExpr = m.getAnnotationByName("JsonNotification").or( + () -> m.getAnnotationByName("JsonRequest")) + .orElseThrow(); + + var segment = getJsonSegment(td) + "/"; + String name = ""; + + if (annotationExpr.isMarkerAnnotationExpr()) { + name = m.getNameAsString(); + } else if (annotationExpr.isSingleMemberAnnotationExpr()) { + var sma = annotationExpr.asSingleMemberAnnotationExpr(); + name = sma.getMemberValue().asLiteralStringValueExpr().getValue(); + } else { + var ne = annotationExpr.asNormalAnnotationExpr(); + for (MemberValuePair pair : ne.getPairs()) { + switch (pair.getName().asString()) { + case "value": + name = pair.getValue().asLiteralStringValueExpr().getValue(); + break; + case "useSegment": + if (!pair.getValue().asBooleanLiteralExpr().getValue()) { + segment = ""; + } + } + } + } + return segment + name; + } + + + @Nonnull + private static String getJavaDoc(NodeWithJavadoc typeDeclaration) { + if (typeDeclaration.getJavadoc().isPresent()) { + final var javadoc = typeDeclaration.getJavadoc().get(); + return javadoc.getDescription().toText() + + "\n\n" + + javadoc.getBlockTags().stream().map(it -> "* " + it.toText()) + .collect(Collectors.joining("\n")); + } + return ""; + } + + private static String type(MethodDeclaration method) { + if (method.getAnnotationByName("JsonNotification").isPresent()) + return "notification"; + if (method.getAnnotationByName("JsonRequest").isPresent()) + return "request"; + return ""; + } + + private static boolean isExported(MethodDeclaration method) { + return method.getAnnotationByName("JsonNotification").isPresent() + || method.getAnnotationByName("JsonRequest").isPresent(); + } + + private static void printHeader(List> segments) { + out.format("# KeY-API%n%n"); + } + + private static void printFooter(List> segments) { + + + } + + + private static boolean hasJsonSegment(TypeDeclaration it) { + return it.getAnnotationByName("JsonSegment").isPresent(); + } + + private static String getJsonSegment(TypeDeclaration it) { + var ae = it.getAnnotationByName("JsonSegment").get(); + return ae.asSingleMemberAnnotationExpr().getMemberValue() + .asLiteralStringValueExpr().getValue(); + } +} diff --git a/keyext.api/build.gradle b/keyext.api/build.gradle index c1805081940..0ddb34926e0 100644 --- a/keyext.api/build.gradle +++ b/keyext.api/build.gradle @@ -18,6 +18,9 @@ dependencies { implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.websocket.jakarta:0.21.1") implementation("org.eclipse.jetty.websocket:websocket-javax-server:10.0.16") implementation("info.picocli:picocli:4.7.5") + + implementation("com.google.guava:guava:32.1.3-jre") + //for GraalVM annotationProcessor 'info.picocli:picocli-codegen:4.7.5' } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java b/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java new file mode 100644 index 00000000000..bc9c24b5e5a --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java @@ -0,0 +1,174 @@ +/* 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.keyproject.key.api; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import de.uka.ilkd.key.control.KeYEnvironment; +import de.uka.ilkd.key.gui.Example; +import de.uka.ilkd.key.gui.ExampleChooser; +import de.uka.ilkd.key.logic.op.Function; +import de.uka.ilkd.key.macros.ProofMacro; +import de.uka.ilkd.key.macros.ProofMacroFacade; +import de.uka.ilkd.key.macros.scripts.ProofScriptCommand; +import de.uka.ilkd.key.macros.scripts.ProofScriptCommandFacade; +import de.uka.ilkd.key.proof.Node; +import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.proof.Statistics; +import de.uka.ilkd.key.util.KeYConstants; + +import org.eclipse.lsp4j.jsonrpc.CompletableFutures; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.keyproject.key.api.adapters.KeyAdapter; +import org.keyproject.key.api.data.*; +import org.keyproject.key.api.remoteapi.KeyApi; + +public record KeyApiImpl(KeyAdapter adapter) implements KeyApi { + + @Override + @JsonRequest + public CompletableFuture> examples() { + return CompletableFutures + .computeAsync((c) -> ExampleChooser.listExamples(ExampleChooser.lookForExamples())); + } + + @Override + public CompletableFuture shutdown() { + return CompletableFuture.completedFuture(null); + } + + @Override + public void exit() { + } + + @Override + public void setTrace(SetTraceParams params) { + + } + + @Override + public CompletableFuture getVersion() { + return CompletableFuture.completedFuture(KeYConstants.VERSION); + } + + @Override + public CompletableFuture> getAvailableMacros() { + return CompletableFuture.completedFuture( + ProofMacroFacade.instance().getMacros().stream().toList()); + } + + @Override + public CompletableFuture>> getAvailableScriptCommands() { + return CompletableFuture.completedFuture( + ProofScriptCommandFacade.instance().getScriptCommands().stream().toList()); + } + + @Override + public CompletableFuture script(Proof proof, String scriptLine, + StreategyOptions options) { + return null; + } + + @Override + public CompletableFuture macro(Proof id, String macroId, + StreategyOptions options) { + return null; + } + + @Override + public CompletableFuture auto(Proof id, StreategyOptions options) { + return null; + } + + @Override + public CompletableFuture dispose(Proof id) { + return null; + } + + @Override + public CompletableFuture goals(Proof id) { + return null; + } + + @Override + public CompletableFuture tree(Proof proof) { + return null; + } + + @Override + public CompletableFuture root(Proof proof) { + return null; + } + + @Override + public CompletableFuture> children(Proof proof, Node nodeId) { + return null; + } + + @Override + public CompletableFuture> pruneTo(Proof proof, Node nodeId) { + return null; + } + + @Override + public CompletableFuture statistics(Proof proof) { + return null; + } + + @Override + public CompletableFuture treeRoot(Proof proof) { + return null; + } + + @Override + public CompletableFuture> treeChildren(Proof proof, TreeNodeId nodeId) { + return null; + } + + @Override + public CompletableFuture> treeSubtree(Proof proof, TreeNodeId nodeId) { + return null; + } + + @Override + public CompletableFuture> sorts(KeYEnvironment env) { + return null; + } + + @Override + public CompletableFuture> functions(KeYEnvironment env) { + return null; + } + + @Override + public CompletableFuture> contracts(KeYEnvironment env) { + return null; + } + + @Override + public CompletableFuture openContract(KeYEnvironment env, String contractId) { + return null; + } + + @Override + public CompletableFuture print(Node id) { + return null; + } + + @Override + public CompletableFuture> actions(PrintId id, int pos) { + return null; + } + + @Override + public CompletableFuture> applyAction(TermActionId id) { + return null; + } + + @Override + public void freePrint(PrintId id) { + + } +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java b/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java index 246181ebc43..0a7531097e9 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java @@ -8,16 +8,13 @@ import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; -import java.util.Collection; -import java.util.Collections; -import java.util.List; import java.util.concurrent.ExecutionException; import javax.annotation.Nullable; import com.google.gson.GsonBuilder; import org.eclipse.lsp4j.jsonrpc.Launcher; import org.eclipse.lsp4j.websocket.jakarta.WebSocketLauncherBuilder; -import org.keyproject.key.api.remoteapi.KeyApiImpl; +import org.keyproject.key.api.adapters.KeyAdapter; import org.keyproject.key.api.remoteclient.ClientApi; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,6 +29,8 @@ public class StartServer implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(StartServer.class); + private static KeyAdapter adapter; + // region CLI arguments @Option(names = "--std", description = "use stdout and stdin for communication") boolean stdStreams; @@ -60,7 +59,7 @@ public class StartServer implements Runnable { boolean helpRequested = false; @Option(names = "--websocket") - boolean websocket; + boolean websocket = false; // endregion public static void main(String[] args) { @@ -120,29 +119,6 @@ public void run() { return; } - /* - * var server = new Server(); - * var connector = new ServerConnector(); - * server.addConnector(connector); - * // Setup the basic application "context" for this application at "/" - * // This is also known as the handler tree (in jetty speak) - * var context = new ServletContextHandler(ServletContextHandler.SESSIONS); - * context.setContextPath("/"); - * server.setHandler(context); - * - * // Initialize javax.websocket layer - * JavaxWebSocketServletContainerInitializer.configure(context, (servletContext, - * wsContainer) -> - * { - * // Configure defaults for container - * wsContainer.setDefaultMaxTextMessageBufferSize(65535); - * - * // Add WebSocket endpoint to javax.websocket layer - * wsContainer.addEndpoint(WebSocketEndpoint.class); - * }); - */ - - try { if (websocket) { var launcherBuilder = new WebSocketLauncherBuilder() @@ -151,7 +127,7 @@ public void run() { .traceMessages(new PrintWriter(System.err)) .validateMessages(true); launcherBuilder.configureGson(StartServer::configureJson); - launcherBuilder.setLocalService(new KeyApiImpl()); + launcherBuilder.setLocalService(new KeyApiImpl(adapter)); launcherBuilder.setRemoteInterface(ClientApi.class); launcherBuilder.create().startListening().get(); } else { @@ -167,8 +143,9 @@ public void run() { } } + public static void configureJson(GsonBuilder gsonBuilder) { - gsonBuilder.registerTypeAdapter(File.class, new FileTypeAdapter()); + adapter = new KeyAdapter(gsonBuilder); } public static Launcher launch(OutputStream out, InputStream in) { @@ -181,33 +158,11 @@ public static Launcher launch(OutputStream out, InputStream in) { .validateMessages(true); launcherBuilder.configureGson(StartServer::configureJson); - // if (localServices != null && !localServices.isEmpty()) - launcherBuilder.setLocalService(new KeyApiImpl()); + launcherBuilder.setLocalService(new KeyApiImpl(adapter)); // if (remoteInterfaces != null && !remoteInterfaces.isEmpty()) launcherBuilder.setRemoteInterface(ClientApi.class); return launcherBuilder.create(); } - - - private static Collection> getRemoteInterfaces() { - return Collections.singleton(ClientApi.class); - /* - * return ServiceLoader.load(ClientService.class) - * .stream() - * .map(ServiceLoader.Provider::type) - * .collect(Collectors.toSet()); - */ - } - - private static List getLocalServices() { - return Collections.singletonList(new KeyApiImpl()); - /* - * return ServiceLoader.load(KeyService.class) - * .stream().map(ServiceLoader.Provider::get) - * .map(it -> (Object) it) - * .toList(); - */ - } } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java b/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java new file mode 100644 index 00000000000..af874cda8b5 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java @@ -0,0 +1,104 @@ +/* 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.keyproject.key.api.adapters; + +import java.io.File; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.lang.reflect.Type; + +import de.uka.ilkd.key.Identifiable; +import de.uka.ilkd.key.logic.op.Function; +import de.uka.ilkd.key.macros.ProofMacro; +import de.uka.ilkd.key.proof.Proof; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.gson.*; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import org.keyproject.key.api.data.MacroDescription; + +/** + * @author Alexander Weigl + * @version 1 (14.10.23) + */ +public class KeyAdapter { + private final BiMap> map = HashBiMap.create(1024); + // private final TypeAdapter adaptor; + + public KeyAdapter(GsonBuilder gson) { + gson.registerTypeAdapter(File.class, new FileTypeAdapter()); + gson.registerTypeAdapter(Function.class, new FunctionTypeAdapter()); + gson.registerTypeAdapter(Proof.class, new IdentifiableTypeAdapter()); + gson.registerTypeAdapter(ProofMacro.class, new MacroTypeAdapter()); + // adaptor = gson.create().getAdapter(Object.class); + } + + + // translating entities to identification strings + public String insert(Identifiable p) { + var id = p.identification(); + if (!map.containsKey(id)) { + map.put(id, new WeakReference<>(p)); + } + return id; + } + + public Object find(String id) { + return map.get(id).get(); + } + // endregion + + class MacroTypeAdapter implements JsonSerializer { + @Override + public JsonElement serialize(ProofMacro src, Type typeOfSrc, + JsonSerializationContext context) { + return context.serialize(MacroDescription.from(src)); + } + } + + class FileTypeAdapter extends TypeAdapter { + @Override + public void write(JsonWriter out, File value) throws IOException { + out.value(value.toString()); + } + + @Override + public File read(JsonReader in) throws IOException { + return new File(in.nextString()); + } + } + + class FunctionTypeAdapter implements JsonSerializer { + @Override + public JsonElement serialize(Function src, Type typeOfSrc, + JsonSerializationContext context) { + var obj = new JsonObject(); + obj.add("name", context.serialize(src.name().toString())); + obj.add("skolemConstant", context.serialize(src.isSkolemConstant())); + obj.add("isRigid", context.serialize(src.isRigid())); + obj.add("isUnique", context.serialize(src.isUnique())); + obj.add("sort", context.serialize(src.sort())); + obj.add("argSorts", context.serialize(src.argSorts())); + return obj; + } + } + + class IdentifiableTypeAdapter + implements JsonSerializer, JsonDeserializer { + @Override + public Identifiable deserialize(JsonElement json, Type typeOfT, + JsonDeserializationContext context) throws JsonParseException { + return (Identifiable) find(json.getAsString()); + } + + @Override + public JsonElement serialize(Identifiable src, Type typeOfSrc, + JsonSerializationContext context) { + insert(src); + return context.serialize(src.identification()); + } + } +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/adapters/package-info.java b/keyext.api/src/main/java/org/keyproject/key/api/adapters/package-info.java new file mode 100644 index 00000000000..522f32d9b3b --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/adapters/package-info.java @@ -0,0 +1,7 @@ +/** + * Adapter classes for translating things from Java to JSON via GSON api. + * + * @author Alexander Weigl + * @version 1 (14.10.23) + */ +package org.keyproject.key.api.adapters; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/ContractDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/ContractDesc.java new file mode 100644 index 00000000000..d17a8bad65c --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/ContractDesc.java @@ -0,0 +1,11 @@ +/* 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.keyproject.key.api.data; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +public record ContractDesc() { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/FunctionDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/FunctionDesc.java new file mode 100644 index 00000000000..cbd2c547182 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/FunctionDesc.java @@ -0,0 +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 org.keyproject.key.api.data; + +import java.util.List; + +/** + * @author Alexander Weigl + * @version 1 (15.10.23) + */ +public record FunctionDesc(String name, String sort, List sorts) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/GoalText.java b/keyext.api/src/main/java/org/keyproject/key/api/data/GoalText.java new file mode 100644 index 00000000000..b4269ac1505 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/GoalText.java @@ -0,0 +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 org.keyproject.key.api.data; + +import de.uka.ilkd.key.pp.PositionTable; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +public record GoalText(PrintId id, String text, PositionTable table) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/LoadParams.java b/keyext.api/src/main/java/org/keyproject/key/api/data/LoadParams.java new file mode 100644 index 00000000000..41e9341585e --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/LoadParams.java @@ -0,0 +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 org.keyproject.key.api.data; + +import java.io.File; +import java.util.List; + +/** + * + * @param keyFile + * @param javaFile + * @param classPath + * @param bootClassPath + * @param includes + */ +public record LoadParams(File keyFile, File javaFile, List classPath, File bootClassPath, + List includes) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/MacroDescription.java b/keyext.api/src/main/java/org/keyproject/key/api/data/MacroDescription.java similarity index 80% rename from keyext.api/src/main/java/org/keyproject/key/api/remoteapi/MacroDescription.java rename to keyext.api/src/main/java/org/keyproject/key/api/data/MacroDescription.java index 95084b3716d..fc2a8ba0e95 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/MacroDescription.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/MacroDescription.java @@ -1,10 +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 org.keyproject.key.api.remoteapi; +package org.keyproject.key.api.data; import de.uka.ilkd.key.macros.ProofMacro; +/** + * + * @param name + * @param description + * @param category + */ public record MacroDescription(String name, String description, String category) { public static MacroDescription from(ProofMacro m) { return new MacroDescription(m.getName(), m.getDescription(), m.getCategory()); diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/MacroStatistic.java b/keyext.api/src/main/java/org/keyproject/key/api/data/MacroStatistic.java new file mode 100644 index 00000000000..c9a0b98ad44 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/MacroStatistic.java @@ -0,0 +1,11 @@ +/* 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.keyproject.key.api.data; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +public record MacroStatistic() { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java new file mode 100644 index 00000000000..ba90e14ff72 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java @@ -0,0 +1,11 @@ +/* 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.keyproject.key.api.data; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +public record NodeDesc() { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/PredicateDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/PredicateDesc.java new file mode 100644 index 00000000000..dbb0e899496 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/PredicateDesc.java @@ -0,0 +1,11 @@ +/* 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.keyproject.key.api.data; + +/** + * @author Alexander Weigl + * @version 1 (15.10.23) + */ +public record PredicateDesc(String name, String[] argSorts) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/PrintId.java b/keyext.api/src/main/java/org/keyproject/key/api/data/PrintId.java new file mode 100644 index 00000000000..895ca1b0979 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/PrintId.java @@ -0,0 +1,11 @@ +/* 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.keyproject.key.api.data; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +public record PrintId(String id) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/ProblemDefinition.java b/keyext.api/src/main/java/org/keyproject/key/api/data/ProblemDefinition.java new file mode 100644 index 00000000000..427e4751646 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/ProblemDefinition.java @@ -0,0 +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 org.keyproject.key.api.data; + +import java.util.List; + +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * @author Alexander Weigl + * @version 1 (15.10.23) + */ +public record ProblemDefinition( + @Nullable List sorts, + @Nullable List functions, + @Nullable List predicates, + @Nullable List antecTerms, + @Nullable List succTerms) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/ProofLoading.java b/keyext.api/src/main/java/org/keyproject/key/api/data/ProofLoading.java new file mode 100644 index 00000000000..dc8e199a77a --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/ProofLoading.java @@ -0,0 +1,47 @@ +/* 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.keyproject.key.api.data; + +import java.util.concurrent.CompletableFuture; + +import de.uka.ilkd.key.control.KeYEnvironment; +import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.proof.io.ProblemLoaderException; + +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; + +/** + * Functionalities for loading proofs either from a built-in example, or from a set of files. + * + * @author Alexander Weigl + * @since v1 + */ +@JsonSegment("loading") +public interface ProofLoading { + /** + * I am not sure whether this is helpful. Mainly a feature for testing?! + * + * @param id + * @return + */ + @JsonRequest + CompletableFuture loadExample(String id); + + /** + * + */ + @JsonRequest + CompletableFuture loadProblem(ProblemDefinition problem); + + /** + * Test! + * + * @param params parameters for loading + * @return + * @throws ProblemLoaderException if something went wrong + */ + @JsonRequest + CompletableFuture> load(LoadParams params) throws ProblemLoaderException; +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/SortDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/SortDesc.java new file mode 100644 index 00000000000..7c0a99e5a17 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/SortDesc.java @@ -0,0 +1,11 @@ +/* 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.keyproject.key.api.data; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +public record SortDesc() { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/StreategyOptions.java b/keyext.api/src/main/java/org/keyproject/key/api/data/StreategyOptions.java new file mode 100644 index 00000000000..ea804f0562b --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/StreategyOptions.java @@ -0,0 +1,11 @@ +/* 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.keyproject.key.api.data; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +public record StreategyOptions() { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/TermAction.java b/keyext.api/src/main/java/org/keyproject/key/api/data/TermAction.java new file mode 100644 index 00000000000..3b5149d275f --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/TermAction.java @@ -0,0 +1,11 @@ +/* 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.keyproject.key.api.data; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +public record TermAction(TermActionId commandId, String displayName) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionId.java b/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionId.java new file mode 100644 index 00000000000..cdf2d63ba92 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionId.java @@ -0,0 +1,11 @@ +/* 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.keyproject.key.api.data; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +public record TermActionId(String id) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/TraceValue.java b/keyext.api/src/main/java/org/keyproject/key/api/data/TraceValue.java similarity index 83% rename from keyext.api/src/main/java/org/keyproject/key/api/remoteapi/TraceValue.java rename to keyext.api/src/main/java/org/keyproject/key/api/data/TraceValue.java index 11a082303f0..99a258f79d4 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/TraceValue.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/TraceValue.java @@ -1,7 +1,7 @@ /* 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.keyproject.key.api.remoteapi; +package org.keyproject.key.api.data; public enum TraceValue { Off, Message, All; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/TreeNodeDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/TreeNodeDesc.java new file mode 100644 index 00000000000..7f8509df70c --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/TreeNodeDesc.java @@ -0,0 +1,11 @@ +/* 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.keyproject.key.api.data; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +public class TreeNodeDesc { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/TreeNodeId.java b/keyext.api/src/main/java/org/keyproject/key/api/data/TreeNodeId.java new file mode 100644 index 00000000000..c476521fa1a --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/TreeNodeId.java @@ -0,0 +1,11 @@ +/* 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.keyproject.key.api.data; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +public record TreeNodeId(String id) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvApi.java new file mode 100644 index 00000000000..5d61e479c3a --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvApi.java @@ -0,0 +1,35 @@ +/* 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.keyproject.key.api.remoteapi; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import de.uka.ilkd.key.control.KeYEnvironment; +import de.uka.ilkd.key.logic.op.Function; +import de.uka.ilkd.key.proof.Proof; + +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; +import org.keyproject.key.api.data.ContractDesc; +import org.keyproject.key.api.data.SortDesc; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +@JsonSegment("env") +public interface EnvApi { + @JsonRequest + CompletableFuture> sorts(KeYEnvironment env); + + @JsonRequest + CompletableFuture> functions(KeYEnvironment env); + + @JsonRequest + CompletableFuture> contracts(KeYEnvironment env); + + @JsonRequest + CompletableFuture openContract(KeYEnvironment env, String contractId); +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyExampleApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ExampleApi.java similarity index 94% rename from keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyExampleApi.java rename to keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ExampleApi.java index 9f91e82baa0..6da9c2f556b 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyExampleApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ExampleApi.java @@ -12,7 +12,7 @@ import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; @JsonSegment("examples") -public interface KeyExampleApi { +public interface ExampleApi { @JsonRequest("list") CompletableFuture> examples(); } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java new file mode 100644 index 00000000000..46eccd412fe --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java @@ -0,0 +1,36 @@ +/* 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.keyproject.key.api.remoteapi; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import de.uka.ilkd.key.proof.Node; + +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; +import org.keyproject.key.api.data.GoalText; +import org.keyproject.key.api.data.PrintId; +import org.keyproject.key.api.data.TermAction; +import org.keyproject.key.api.data.TermActionId; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +@JsonSegment("goal") +public interface GoalApi { + @JsonRequest + CompletableFuture print(Node id); + + @JsonRequest + CompletableFuture> actions(PrintId id, int pos); + + @JsonRequest("apply_action") + CompletableFuture> applyAction(TermActionId id); + + @JsonNotification("free") + void freePrint(PrintId id); +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApi.java index 21c3400574d..7f269f0eceb 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApi.java @@ -3,64 +3,9 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.remoteapi; -import java.util.concurrent.CompletableFuture; - -import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; -import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; - -public interface KeyApi extends KeyExampleApi, KeyMetaApi { - // region general server management - /** - * Shutdown Request (:leftwards_arrow_with_hook:) - * The shutdown request is sent from the client to the server. It asks the server to shut down, - * but to not exit (otherwise the response might not be delivered correctly to the client). - * There is a separate exit notification that asks the server to exit. Clients must not send any - * notifications other than exit or requests to a server to which they have sent a shutdown - * request. Clients should also wait with sending the exit notification until they have received - * a response from the shutdown request. - * - * If a server receives requests after a shutdown request those requests should error with - * InvalidRequest. - * - * Request: - * - * method: ‘shutdown’ - * params: none - * Response: - * - * result: null - * error: code and message set in case an exception happens during shutdown request. - */ - @JsonRequest - CompletableFuture shutdown(); - - /** - * Exit Notification (:arrow_right:) - * A notification to ask the server to exit its process. The server should exit with success - * code 0 if the shutdown request has been received before; otherwise with error code 1. - *

- * Notification: - *

- * method: ‘exit’ - * params: none - */ - @JsonNotification - void exit(); - - - - interface SetTraceParams { - /** - * The new value that should be assigned to the trace setting. - */ - TraceValue value = null; - } - - /** - * SetTrace Notification - * A notification that should be used by the client to modify the trace setting of the server. - */ - @JsonNotification - void setTrace(SetTraceParams params); - // endregion +/** + * The combined interface that is provided by KeY. + */ +public interface KeyApi + extends ExampleApi, MetaApi, ServerManagement, ProofApi, ProofTreeApi, GoalApi, EnvApi { } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyMetaApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/MetaApi.java similarity index 71% rename from keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyMetaApi.java rename to keyext.api/src/main/java/org/keyproject/key/api/remoteapi/MetaApi.java index 3a1a027f233..5a0454d8b93 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyMetaApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/MetaApi.java @@ -6,19 +6,20 @@ import java.util.List; import java.util.concurrent.CompletableFuture; +import de.uka.ilkd.key.macros.ProofMacro; import de.uka.ilkd.key.macros.scripts.ProofScriptCommand; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; @JsonSegment("meta") -public interface KeyMetaApi { - @JsonRequest +public interface MetaApi { + @JsonRequest("version") CompletableFuture getVersion(); - @JsonRequest - CompletableFuture> getAvailableMacros(); + @JsonRequest("available_macros") + CompletableFuture> getAvailableMacros(); - @JsonRequest + @JsonRequest("available_script_commands") CompletableFuture>> getAvailableScriptCommands(); } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofApi.java new file mode 100644 index 00000000000..93089544d7b --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofApi.java @@ -0,0 +1,53 @@ +/* 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.keyproject.key.api.remoteapi; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import de.uka.ilkd.key.proof.Node; +import de.uka.ilkd.key.proof.Proof; +import de.uka.ilkd.key.proof.Statistics; + +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.keyproject.key.api.data.MacroStatistic; +import org.keyproject.key.api.data.NodeDesc; +import org.keyproject.key.api.data.StreategyOptions; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +public interface ProofApi { + @JsonRequest + CompletableFuture script(Proof proof, String scriptLine, + StreategyOptions options); + + @JsonRequest + CompletableFuture macro(Proof proof, String macroId, StreategyOptions options); + + @JsonRequest + CompletableFuture auto(Proof proof, StreategyOptions options); + + @JsonRequest + CompletableFuture dispose(Proof proof); + + @JsonRequest + CompletableFuture goals(Proof proof); + + @JsonRequest + CompletableFuture tree(Proof proof); + + @JsonRequest + CompletableFuture root(Proof proof); + + @JsonRequest + CompletableFuture> children(Proof proof, Node nodeId); + + @JsonRequest + CompletableFuture> pruneTo(Proof proof, Node nodeId); + + @JsonRequest + CompletableFuture statistics(Proof proof); +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofTreeApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofTreeApi.java new file mode 100644 index 00000000000..162aa884c87 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofTreeApi.java @@ -0,0 +1,30 @@ +/* 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.keyproject.key.api.remoteapi; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import de.uka.ilkd.key.proof.Proof; + +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; +import org.keyproject.key.api.data.TreeNodeDesc; +import org.keyproject.key.api.data.TreeNodeId; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +@JsonSegment("proofTree") +public interface ProofTreeApi { + @JsonRequest("root") + CompletableFuture treeRoot(Proof id); + + @JsonRequest("children") + CompletableFuture> treeChildren(Proof proof, TreeNodeId nodeId); + + @JsonRequest("subtree") + CompletableFuture> treeSubtree(Proof proof, TreeNodeId nodeId); +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ServerManagement.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ServerManagement.java new file mode 100644 index 00000000000..b593d0ed66b --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ServerManagement.java @@ -0,0 +1,70 @@ +/* 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.keyproject.key.api.remoteapi; + +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; +import org.keyproject.key.api.data.TraceValue; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +@JsonSegment("server") +public interface ServerManagement { + /** + * Shutdown Request (:leftwards_arrow_with_hook:) + * The shutdown request is sent from the client to the server. It asks the server to shut down, + * but to not exit (otherwise the response might not be delivered correctly to the client). + * There is a separate exit notification that asks the server to exit. Clients must not send any + * notifications other than exit or requests to a server to which they have sent a shutdown + * request. Clients should also wait with sending the exit notification until they have received + * a response from the shutdown request. + *

+ * If a server receives requests after a shutdown request those requests should error with + * InvalidRequest. + *

+ * Request: + *

+ * method: ‘shutdown’ + * params: none + * Response: + *

+ * result: null + * error: code and message set in case an exception happens during shutdown request. + */ + @JsonRequest + CompletableFuture shutdown(); + + /** + * Exit Notification (:arrow_right:) + * A notification to ask the server to exit its process. The server should exit with success + * code 0 if the shutdown request has been received before; otherwise with error code 1. + *

+ * Notification: + *

+ * method: ‘exit’ + * params: none + */ + @JsonNotification + void exit(); + + + interface SetTraceParams { + /** + * The new value that should be assigned to the trace setting. + */ + TraceValue value = null; + } + + /** + * SetTrace Notification + * A notification that should be used by the client to modify the trace setting of the server. + */ + @JsonNotification + void setTrace(SetTraceParams params); +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java index 3bbf856af3d..9dc420279d3 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java @@ -10,7 +10,7 @@ import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; -@JsonSegment +@JsonSegment("client") public interface ClientApi { @JsonNotification void sayHello(String e); diff --git a/keyext.api/src/test/java/org/keyproject/key/api/SimpleClient.java b/keyext.api/src/test/java/org/keyproject/key/api/SimpleClient.java index 6e92beff41c..bba9b06d709 100644 --- a/keyext.api/src/test/java/org/keyproject/key/api/SimpleClient.java +++ b/keyext.api/src/test/java/org/keyproject/key/api/SimpleClient.java @@ -21,7 +21,7 @@ public void logTrace(LogTraceParams params) { } @Override - public void userResponse(ShowMessageParams params) { + public void showMessage(ShowMessageParams params) { } diff --git a/keyext.api/src/test/java/org/keyproject/key/api/Client.java b/keyext.api/src/test/java/org/keyproject/key/api/TestRpc.java similarity index 59% rename from keyext.api/src/test/java/org/keyproject/key/api/Client.java rename to keyext.api/src/test/java/org/keyproject/key/api/TestRpc.java index 5ff2d0725f4..9c21634adae 100644 --- a/keyext.api/src/test/java/org/keyproject/key/api/Client.java +++ b/keyext.api/src/test/java/org/keyproject/key/api/TestRpc.java @@ -9,19 +9,25 @@ import java.io.PrintWriter; import java.util.Collections; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; import org.eclipse.lsp4j.jsonrpc.Launcher; import org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.keyproject.key.api.remoteapi.KeyApi; import org.keyproject.key.api.remoteclient.ClientApi; -public class Client { - public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException, TimeoutException { +public class TestRpc { + private Future clientListening, serverListening; + private KeyApi keyApi; + + @BeforeEach + void setup() throws IOException, ExecutionException, InterruptedException, TimeoutException { PipedInputStream inClient = new PipedInputStream(); PipedOutputStream outClient = new PipedOutputStream(); PipedInputStream inServer = new PipedInputStream(); @@ -45,13 +51,33 @@ public static void main(String[] args) Logger logger = Logger.getLogger(StreamMessageProducer.class.getName()); logger.setLevel(Level.SEVERE); - var clientListening = clientLauncher.startListening(); - var serverListening = serverLauncher.startListening(); + clientListening = clientLauncher.startListening(); + serverListening = serverLauncher.startListening(); + + keyApi = clientLauncher.getRemoteProxy(); + } + + @AfterEach + void teardown() throws ExecutionException, InterruptedException, TimeoutException { + serverListening.cancel(true); + clientListening.cancel(true); + } + + + @Test + public void hello() { - // clientLauncher.getRemoteProxy().examples(); - serverLauncher.getRemoteProxy().sayHello("Alex"); + } + + @Test + public void listMacros() throws ExecutionException, InterruptedException { + var examples = keyApi.getAvailableMacros().get(); + System.out.println(examples); + } - serverListening.get(1, TimeUnit.SECONDS); - clientListening.get(1, TimeUnit.SECONDS); + @Test + public void listExamples() throws ExecutionException, InterruptedException { + var examples = keyApi.examples().get(); + System.out.println(examples); } } diff --git a/settings.gradle b/settings.gradle index 8be9e773bee..f17504647e7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -31,6 +31,7 @@ include "keyext.caching" include "keyext.isabelletranslation" include 'keyext.api' +include 'keyext.api.doc' // ENABLE NULLNESS here or on the CLI // This flag is activated to enable the checker framework. From ab3ca5e648f4601c033ad39ce65e8bd25dfb30fe Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Sun, 29 Oct 2023 16:45:43 +0100 Subject: [PATCH 05/35] working on a working minimal version --- .../src/main/java/org/key_project/Main.java | 4 +- .../key/control/AbstractProofControl.java | 14 +- .../de/uka/ilkd/key/control/ProofControl.java | 11 +- .../ilkd/key/macros/ProofMacroListener.java | 2 +- .../main/java/de/uka/ilkd/key/proof/Node.java | 6 + .../uka/ilkd/key/gui/ProofScriptWorker.java | 7 + .../org/keyproject/key/api/KeyApiImpl.java | 505 ++++++++++++++++-- .../org/keyproject/key/api/NodeTextDesc.java | 11 + .../org/keyproject/key/api/NodeTextId.java | 13 + .../org/keyproject/key/api/StartServer.java | 16 +- .../keyproject/key/api/TermActionUtil.java | 134 +++++ .../key/api/adapters/KeyAdapter.java | 79 ++- .../keyproject/key/api/data/ContractDesc.java | 13 +- .../keyproject/key/api/data/FunctionDesc.java | 14 +- .../key/api/data/KeyIdentifications.java | 171 ++++++ .../key/api/data/MacroStatistic.java | 11 +- .../org/keyproject/key/api/data/NodeDesc.java | 13 +- .../key/api/data/ProofMacroDesc.java | 18 + .../key/api/data/ProofScriptCommandDesc.java | 16 + .../org/keyproject/key/api/data/SortDesc.java | 13 +- .../key/api/data/TermActionDesc.java | 15 + .../keyproject/key/api/data/TermActionId.java | 7 - .../key/api/data/TermActionKind.java | 12 + .../keyproject/key/api/data/TreeNodeId.java | 7 - .../keyproject/key/api/internal/NodeText.java | 13 + .../keyproject/key/api/remoteapi/EnvApi.java | 17 +- .../keyproject/key/api/remoteapi/GoalApi.java | 21 +- .../keyproject/key/api/remoteapi/KeyApi.java | 4 +- .../keyproject/key/api/remoteapi/MetaApi.java | 10 +- .../key/api/remoteapi/PrintOptions.java | 12 + .../key/api/remoteapi/ProofApi.java | 27 +- .../ProofLoadApi.java} | 27 +- .../key/api/remoteapi/ProofTreeApi.java | 14 +- .../key/api/remoteclient/ClientApi.java | 11 +- .../org/keyproject/key/api/SimpleClient.java | 18 + .../java/org/keyproject/key/api/TestRpc.java | 7 +- 36 files changed, 1096 insertions(+), 197 deletions(-) create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/NodeTextDesc.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/NodeTextId.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/KeyIdentifications.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/ProofMacroDesc.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/ProofScriptCommandDesc.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/TermActionDesc.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/TermActionKind.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/internal/NodeText.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteapi/PrintOptions.java rename keyext.api/src/main/java/org/keyproject/key/api/{data/ProofLoading.java => remoteapi/ProofLoadApi.java} (54%) diff --git a/key.core.example/src/main/java/org/key_project/Main.java b/key.core.example/src/main/java/org/key_project/Main.java index a1b583c7be3..39f0f1a958a 100644 --- a/key.core.example/src/main/java/org/key_project/Main.java +++ b/key.core.example/src/main/java/org/key_project/Main.java @@ -97,6 +97,7 @@ private static void proveEnvironmemt(KeYEnvironment env) { LOGGER.info("Found contract '" + contract.getDisplayName()); proveContract(env, contract); } + } catch (InterruptedException ignored) { } finally { env.dispose(); // Ensure always that all instances of KeYEnvironment are disposed } @@ -135,7 +136,8 @@ private static List getContracts(KeYEnvironment env) { * @param env the {@link KeYEnvironment} in which to prove the contract * @param contract the {@link Contract} to be proven */ - private static void proveContract(KeYEnvironment env, Contract contract) { + private static void proveContract(KeYEnvironment env, Contract contract) + throws InterruptedException { Proof proof = null; try { // Create proof diff --git a/key.core/src/main/java/de/uka/ilkd/key/control/AbstractProofControl.java b/key.core/src/main/java/de/uka/ilkd/key/control/AbstractProofControl.java index 535d6032acd..a7b0efc7b2b 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/control/AbstractProofControl.java +++ b/key.core/src/main/java/de/uka/ilkd/key/control/AbstractProofControl.java @@ -571,19 +571,12 @@ protected void fireAutoModeStopped(ProofEvent e) { } } - /** - * {@inheritDoc} - */ - @Override - public void startAutoMode(Proof proof) { - startAutoMode(proof, proof.openEnabledGoals()); - } /** * {@inheritDoc} */ @Override - public void startAndWaitForAutoMode(Proof proof) { + public void startAndWaitForAutoMode(Proof proof) throws InterruptedException { startAutoMode(proof); waitWhileAutoMode(); } @@ -592,7 +585,8 @@ public void startAndWaitForAutoMode(Proof proof) { * {@inheritDoc} */ @Override - public void startAndWaitForAutoMode(Proof proof, ImmutableList goals) { + public void startAndWaitForAutoMode(Proof proof, ImmutableList goals) + throws InterruptedException { startAutoMode(proof, goals); waitWhileAutoMode(); } @@ -601,7 +595,7 @@ public void startAndWaitForAutoMode(Proof proof, ImmutableList goals) { * {@inheritDoc} */ @Override - public void stopAndWaitAutoMode() { + public void stopAndWaitAutoMode() throws InterruptedException { stopAutoMode(); waitWhileAutoMode(); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/control/ProofControl.java b/key.core/src/main/java/de/uka/ilkd/key/control/ProofControl.java index c85a86d0f24..375939e4e44 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/control/ProofControl.java +++ b/key.core/src/main/java/de/uka/ilkd/key/control/ProofControl.java @@ -126,7 +126,7 @@ void selectedBuiltInRule(Goal goal, BuiltInRule rule, PosInOccurrence pos, * * @param proof The {@link Proof} to start auto mode of. */ - void startAutoMode(Proof proof); + default void startAutoMode(Proof proof) { startAutoMode(proof, proof.openEnabledGoals()); } /** * Starts the auto mode for the given {@link Proof} and the given {@link Goal}s. @@ -146,13 +146,13 @@ void selectedBuiltInRule(Goal goal, BuiltInRule rule, PosInOccurrence pos, * Stops the currently running auto mode and blocks the current {@link Thread} until auto mode * has stopped. */ - void stopAndWaitAutoMode(); + void stopAndWaitAutoMode() throws InterruptedException; /** * Blocks the current {@link Thread} while the auto mode of this {@link UserInterfaceControl} is * active. */ - void waitWhileAutoMode(); + void waitWhileAutoMode() throws InterruptedException; /** * Starts the auto mode for the given proof which must be contained in this user interface and @@ -161,7 +161,8 @@ void selectedBuiltInRule(Goal goal, BuiltInRule rule, PosInOccurrence pos, * @param proof The {@link Proof} to start auto mode and to wait for. * @param goals The {@link Goal}s to close. */ - void startAndWaitForAutoMode(Proof proof, ImmutableList goals); + void startAndWaitForAutoMode(Proof proof, ImmutableList goals) + throws InterruptedException; /** * Starts the auto mode for the given proof which must be contained in this user interface and @@ -169,7 +170,7 @@ void selectedBuiltInRule(Goal goal, BuiltInRule rule, PosInOccurrence pos, * * @param proof The {@link Proof} to start auto mode and to wait for. */ - void startAndWaitForAutoMode(Proof proof); + void startAndWaitForAutoMode(Proof proof) throws InterruptedException; void startFocussedAutoMode(PosInOccurrence focus, Goal goal); diff --git a/key.core/src/main/java/de/uka/ilkd/key/macros/ProofMacroListener.java b/key.core/src/main/java/de/uka/ilkd/key/macros/ProofMacroListener.java index 1e1d871c1ec..efeed331615 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/macros/ProofMacroListener.java +++ b/key.core/src/main/java/de/uka/ilkd/key/macros/ProofMacroListener.java @@ -36,7 +36,7 @@ public void taskStarted(TaskStartedInfo info) { numOfInvokedMacros++; if (superordinateListener != null) { superordinateListener.taskStarted(new DefaultTaskStartedInfo(TaskKind.Macro, - macroName + (macroName.length() == 0 ? "" : " -- ") + info.message(), + macroName + (macroName.isEmpty() ? "" : " -- ") + info.message(), info.size())); } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/proof/Node.java b/key.core/src/main/java/de/uka/ilkd/key/proof/Node.java index 93c58da43ce..74f85592bbe 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/proof/Node.java +++ b/key.core/src/main/java/de/uka/ilkd/key/proof/Node.java @@ -3,6 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-only */ package de.uka.ilkd.key.proof; +import java.util.*; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -12,6 +13,7 @@ import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.stream.Stream; import de.uka.ilkd.key.logic.RenamingTable; import de.uka.ilkd.key.logic.op.IProgramVariable; @@ -838,4 +840,8 @@ public int getStepIndex() { void setStepIndex(int stepIndex) { this.stepIndex = stepIndex; } + + public Stream childrenStream() { + return children.stream(); + } } 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..7fd88c78a74 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 @@ -163,6 +163,13 @@ public void done() { } mediator.removeInterruptedListener(this); + runWithDeadline(() -> mediator.startInterface(true), 1000); + runWithDeadline(() -> { + try { + mediator.getUI().getProofControl().stopAndWaitAutoMode(); + } catch (InterruptedException ignored) { + } + }, 1000); final Proof proof = initiallySelectedGoal != null ? initiallySelectedGoal.proof() : mediator.getSelectedProof(); diff --git a/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java b/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java index bc9c24b5e5a..c94607260fa 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java @@ -3,29 +3,78 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; +import de.uka.ilkd.key.control.AbstractUserInterfaceControl; +import de.uka.ilkd.key.control.DefaultUserInterfaceControl; import de.uka.ilkd.key.control.KeYEnvironment; import de.uka.ilkd.key.gui.Example; import de.uka.ilkd.key.gui.ExampleChooser; -import de.uka.ilkd.key.logic.op.Function; -import de.uka.ilkd.key.macros.ProofMacro; import de.uka.ilkd.key.macros.ProofMacroFacade; -import de.uka.ilkd.key.macros.scripts.ProofScriptCommand; +import de.uka.ilkd.key.macros.ProofMacroFinishedInfo; import de.uka.ilkd.key.macros.scripts.ProofScriptCommandFacade; -import de.uka.ilkd.key.proof.Node; -import de.uka.ilkd.key.proof.Proof; -import de.uka.ilkd.key.proof.Statistics; +import de.uka.ilkd.key.macros.scripts.ProofScriptEngine; +import de.uka.ilkd.key.macros.scripts.ScriptException; +import de.uka.ilkd.key.parser.Location; +import de.uka.ilkd.key.pp.IdentitySequentPrintFilter; +import de.uka.ilkd.key.pp.LogicPrinter; +import de.uka.ilkd.key.pp.NotationInfo; +import de.uka.ilkd.key.pp.PosTableLayouter; +import de.uka.ilkd.key.proof.*; +import de.uka.ilkd.key.proof.init.*; +import de.uka.ilkd.key.proof.io.AbstractProblemLoader; +import de.uka.ilkd.key.proof.io.ProblemLoaderException; +import de.uka.ilkd.key.prover.ProverTaskListener; +import de.uka.ilkd.key.prover.TaskFinishedInfo; +import de.uka.ilkd.key.prover.TaskStartedInfo; +import de.uka.ilkd.key.speclang.PositionedString; import de.uka.ilkd.key.util.KeYConstants; +import org.key_project.util.collection.ImmutableList; +import org.key_project.util.collection.ImmutableSet; + import org.eclipse.lsp4j.jsonrpc.CompletableFutures; +import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; -import org.keyproject.key.api.adapters.KeyAdapter; import org.keyproject.key.api.data.*; +import org.keyproject.key.api.data.KeyIdentifications.*; +import org.keyproject.key.api.internal.NodeText; import org.keyproject.key.api.remoteapi.KeyApi; - -public record KeyApiImpl(KeyAdapter adapter) implements KeyApi { +import org.keyproject.key.api.remoteapi.PrintOptions; +import org.keyproject.key.api.remoteclient.ClientApi; + +public final class KeyApiImpl implements KeyApi { + private final KeyIdentifications data = new KeyIdentifications(); + + private ClientApi clientApi; + private final ProverTaskListener clientListener = new ProverTaskListener() { + @Override + public void taskStarted(TaskStartedInfo info) { + clientApi.taskStarted(info); + } + + @Override + public void taskProgress(int position) { + clientApi.taskProgress(position); + } + + @Override + public void taskFinished(TaskFinishedInfo info) { + clientApi.taskFinished(info); + } + }; + private final AtomicInteger uniqueCounter = new AtomicInteger(); + + public KeyApiImpl() { + } @Override @JsonRequest @@ -54,121 +103,479 @@ public CompletableFuture getVersion() { } @Override - public CompletableFuture> getAvailableMacros() { + public CompletableFuture> getAvailableMacros() { return CompletableFuture.completedFuture( - ProofMacroFacade.instance().getMacros().stream().toList()); + ProofMacroFacade.instance().getMacros().stream() + .map(ProofMacroDesc::from).toList()); } @Override - public CompletableFuture>> getAvailableScriptCommands() { + public CompletableFuture> getAvailableScriptCommands() { return CompletableFuture.completedFuture( - ProofScriptCommandFacade.instance().getScriptCommands().stream().toList()); + ProofScriptCommandFacade.instance().getScriptCommands().stream() + .map(ProofScriptCommandDesc::from).toList()); } @Override - public CompletableFuture script(Proof proof, String scriptLine, + public CompletableFuture script(ProofId proofId, String scriptLine, StreategyOptions options) { - return null; + return CompletableFuture.supplyAsync(() -> { + var proof = data.find(proofId); + var env = data.find(proofId.env()); + var pe = new ProofScriptEngine(scriptLine, Location.UNDEFINED); + + try { + pe.execute((AbstractUserInterfaceControl) env.getProofControl(), proof); + return null; + } catch (IOException | InterruptedException | ScriptException e) { + throw new RuntimeException(e); + } + }); } @Override - public CompletableFuture macro(Proof id, String macroId, + public CompletableFuture macro(ProofId proofId, String macroId, StreategyOptions options) { - return null; + return CompletableFuture.supplyAsync(() -> { + var proof = data.find(proofId); + var env = data.find(proofId.env()); + var macro = Objects.requireNonNull(ProofMacroFacade.instance().getMacro(macroId)); + + try { + var info = + macro.applyTo(env.getUi(), proof, proof.openGoals(), null, clientListener); + return MacroStatistic.from(proofId, info); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } @Override - public CompletableFuture auto(Proof id, StreategyOptions options) { - return null; + public CompletableFuture auto(ProofId proofId, StreategyOptions options) { + return CompletableFuture.supplyAsync(() -> { + var proof = data.find(proofId); + var env = data.find(proofId.env()); + try { + env.getProofControl().startAndWaitForAutoMode(proof); + // clientListener); + return null;// MacroStatistic.from(proofId, info); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } @Override - public CompletableFuture dispose(Proof id) { - return null; + public CompletableFuture dispose(ProofId id) { + return CompletableFuture.supplyAsync(() -> { + var proof = data.find(id); + data.dispose(id); + proof.dispose(); + return true; + }); } @Override - public CompletableFuture goals(Proof id) { - return null; + public CompletableFuture> goals(ProofId proofId, boolean onlyOpened, + boolean onlyEnabled) { + return CompletableFuture.supplyAsync(() -> { + var proof = data.find(proofId); + if (onlyOpened && !onlyEnabled) { + return asNodeDesc(proofId, proof.openGoals()); + } else if (onlyEnabled && onlyOpened) { + return asNodeDesc(proofId, proof.openEnabledGoals()); + } else { + return asNodeDesc(proofId, proof.allGoals()); + } + }); + } + + private List asNodeDesc(ProofId proofId, ImmutableList goals) { + return asNodeDesc(proofId, goals.stream().map(Goal::node)); + } + + private List asNodeDesc(ProofId proofId, Stream nodes) { + return nodes.map(it -> asNodeDesc(proofId, it)).toList(); + } + + private NodeDesc asNodeDesc(ProofId proofId, Node it) { + return new NodeDesc(proofId, it.serialNr(), it.getNodeInfo().getBranchLabel(), + it.getNodeInfo().getScriptRuleApplication()); } @Override - public CompletableFuture tree(Proof proof) { - return null; + public CompletableFuture tree(ProofId proofId) { + return CompletableFuture.supplyAsync(() -> { + var proof = data.find(proofId); + return asNodeDescRecursive(proofId, proof.root()); + }); + } + + private NodeDesc asNodeDescRecursive(ProofId proofId, Node root) { + return new NodeDesc(new NodeId(proofId, root.serialNr()), + root.getNodeInfo().getBranchLabel(), + root.getNodeInfo().getScriptRuleApplication(), + root.childrenStream().map(it -> asNodeDescRecursive(proofId, it)).toList()); } @Override - public CompletableFuture root(Proof proof) { - return null; + public CompletableFuture root(ProofId proofId) { + return CompletableFuture.supplyAsync(() -> { + var proof = data.find(proofId); + return asNodeDesc(proofId, proof.root()); + }); } @Override - public CompletableFuture> children(Proof proof, Node nodeId) { - return null; + public CompletableFuture> children(NodeId nodeId) { + return CompletableFuture.supplyAsync(() -> { + var node = data.find(nodeId); + return asNodeDesc(nodeId.proofId(), node.childrenStream()); + }); } @Override - public CompletableFuture> pruneTo(Proof proof, Node nodeId) { + public CompletableFuture> pruneTo(NodeId nodeId) { return null; } @Override - public CompletableFuture statistics(Proof proof) { - return null; + public CompletableFuture statistics(ProofId proofId) { + return CompletableFuture.supplyAsync(() -> { + var proof = data.find(proofId); + return proof.getStatistics(); + }); } @Override - public CompletableFuture treeRoot(Proof proof) { + public CompletableFuture treeRoot(ProofId proof) { return null; } @Override - public CompletableFuture> treeChildren(Proof proof, TreeNodeId nodeId) { + public CompletableFuture> treeChildren(ProofId proof, TreeNodeId nodeId) { return null; } @Override - public CompletableFuture> treeSubtree(Proof proof, TreeNodeId nodeId) { + public CompletableFuture> treeSubtree(ProofId proof, TreeNodeId nodeId) { return null; } @Override - public CompletableFuture> sorts(KeYEnvironment env) { - return null; + public CompletableFuture> sorts(EnvironmentId envId) { + return CompletableFuture.supplyAsync(() -> { + var env = data.find(envId); + var sorts = env.getServices().getNamespaces().sorts().allElements(); + return sorts.stream().map(SortDesc::from).toList(); + }); } @Override - public CompletableFuture> functions(KeYEnvironment env) { - return null; + public CompletableFuture> functions(EnvironmentId envId) { + return CompletableFuture.supplyAsync(() -> { + var env = data.find(envId); + var functions = env.getServices().getNamespaces().functions().allElements(); + return functions.stream().map(FunctionDesc::from).toList(); + }); } @Override - public CompletableFuture> contracts(KeYEnvironment env) { - return null; + public CompletableFuture> contracts(EnvironmentId envId) { + return CompletableFuture.supplyAsync(() -> { + var env = data.find(envId); + var contracts = env.getAvailableContracts(); + return contracts.stream().map(it -> ContractDesc.from(envId, env.getServices(), it)) + .toList(); + }); } @Override - public CompletableFuture openContract(KeYEnvironment env, String contractId) { - return null; + public CompletableFuture openContract(ContractId contractId) { + return CompletableFuture.supplyAsync(() -> { + var env = data.find(contractId.envId()); + var contracts = env.getAvailableContracts(); + var contract = + contracts.stream().filter(it -> it.id() == contractId.contractId()).findFirst(); + if (contract.isPresent()) { + try { + var proof = env.createProof(contract.get().createProofObl(env.getInitConfig())); + return data.register(contractId.envId(), proof); + } catch (ProofInputException e) { + throw new RuntimeException(e); + } + } else { + return null; + } + }); } @Override - public CompletableFuture print(Node id) { - return null; + public CompletableFuture print(NodeId nodeId, PrintOptions options) { + return CompletableFuture.supplyAsync(() -> { + var node = data.find(nodeId); + var env = data.find(nodeId.proofId().env()); + var notInfo = new NotationInfo(); + final var layouter = + new PosTableLayouter(options.width(), options.indentation(), options.pure()); + var lp = new LogicPrinter(notInfo, env.getServices(), layouter); + lp.printSequent(node.sequent()); + + var id = new NodeTextId(nodeId, uniqueCounter.getAndIncrement()); + var t = new NodeText(lp.result(), layouter.getInitialPositionTable()); + data.register(id, t); + return new NodeTextDesc(id, lp.result()); + }); } + private final IdentitySequentPrintFilter filter = new IdentitySequentPrintFilter(); + @Override - public CompletableFuture> actions(PrintId id, int pos) { - return null; + public CompletableFuture> actions(NodeTextId printId, int pos) { + return CompletableFuture.supplyAsync(() -> { + var node = data.find(printId.nodeId()); + var proof = data.find(printId.nodeId().proofId()); + var goal = proof.getOpenGoal(node); + var nodeText = data.find(printId); + var pis = nodeText.table().getPosInSequent(pos, filter); + return new TermActionUtil(printId, data.find(printId.nodeId().proofId().env()), pis, + goal) + .getActions(); + }); + } @Override - public CompletableFuture> applyAction(TermActionId id) { + public CompletableFuture> applyAction(TermActionId id) { return null; } @Override - public void freePrint(PrintId id) { + public void freePrint(NodeTextId printId) { + CompletableFuture.runAsync(() -> data.dispose(printId)); + } + + public void setClientApi(ClientApi remoteProxy) { + clientApi = remoteProxy; + } + + private final DefaultUserInterfaceControl control = new MyDefaultUserInterfaceControl(); + + @Override + public CompletableFuture loadExample(String id) { + return CompletableFutures.computeAsync((c) -> { + var examples = ExampleChooser.listExamples(ExampleChooser.lookForExamples()) + .stream().filter(it -> it.getName().equals(id)).findFirst(); + if (examples.isPresent()) { + var ex = examples.get(); + Proof proof = null; + KeYEnvironment env = null; + try { + var loader = control.load(JavaProfile.getDefaultProfile(), + ex.getObligationFile(), null, null, null, null, true, null); + InitConfig initConfig = loader.getInitConfig(); + + env = new KeYEnvironment<>(control, initConfig, loader.getProof(), + loader.getProofScript(), loader.getResult()); + var envId = new EnvironmentId(env.toString()); + data.register(envId, env); + proof = Objects.requireNonNull(env.getLoadedProof()); + var proofId = new ProofId(envId, proof.name().toString()); + return data.register(proofId, proof); + } catch (ProblemLoaderException e) { + if (proof != null) + proof.dispose(); + if (env != null) + env.dispose(); + throw new RuntimeException(e); + } + } + throw new IllegalArgumentException("Unknown example"); + }); + } + + @Override + public CompletableFuture loadProblem(ProblemDefinition problem) { + return CompletableFutures.computeAsync((c) -> { + Proof proof = null; + KeYEnvironment env = null; + /* + * var loader = control.load(JavaProfile.getDefaultProfile(), + * ex.getObligationFile(), null, null, null, null, true, null); + * InitConfig initConfig = loader.getInitConfig(); + * + * env = new KeYEnvironment<>(control, initConfig, loader.getProof(), + * loader.getProofScript(), loader.getResult()); + * var envId = new EnvironmentId(env.toString()); + * data.register(envId, env); + * proof = Objects.requireNonNull(env.getLoadedProof()); + * var proofId = new ProofId(envId, proof.name().toString()); + * return data.register(proofId, proof); + */ + return null; + }); } + + @Override + public CompletableFuture loadKey(String content) { + return CompletableFutures.computeAsync((c) -> { + Proof proof = null; + KeYEnvironment env = null; + try { + final var tempFile = File.createTempFile("json-rpc-", ".key"); + Files.writeString(tempFile.toPath(), content); + var loader = control.load(JavaProfile.getDefaultProfile(), + tempFile, null, null, null, null, true, null); + InitConfig initConfig = loader.getInitConfig(); + env = new KeYEnvironment<>(control, initConfig, loader.getProof(), + loader.getProofScript(), loader.getResult()); + var envId = new EnvironmentId(env.toString()); + data.register(envId, env); + proof = Objects.requireNonNull(env.getLoadedProof()); + var proofId = new ProofId(envId, proof.name().toString()); + return data.register(proofId, proof); + } catch (ProblemLoaderException | IOException e) { + if (proof != null) + proof.dispose(); + if (env != null) + env.dispose(); + throw new RuntimeException(e); + } + }); + } + + @Override + public CompletableFuture loadTerm(String term) { + return loadKey("\\problem{ " + term + " }"); + } + + @Override + public CompletableFuture> load(LoadParams params) { + return CompletableFutures.computeAsync((c) -> { + Proof proof = null; + KeYEnvironment env = null; + try { + var loader = control.load(JavaProfile.getDefaultProfile(), + params.keyFile(), + params.classPath(), + params.bootClassPath(), + params.includes(), + null, + true, + null); + InitConfig initConfig = loader.getInitConfig(); + env = new KeYEnvironment<>(control, initConfig, loader.getProof(), + loader.getProofScript(), loader.getResult()); + var envId = new EnvironmentId(env.toString()); + data.register(envId, env); + if ((proof = env.getLoadedProof()) != null) { + var proofId = new ProofId(envId, proof.name().toString()); + return Either.forRight(data.register(proofId, proof)); + } else { + return Either.forLeft(envId); + } + } catch (ProblemLoaderException e) { + if (proof != null) + proof.dispose(); + if (env != null) + env.dispose(); + throw new RuntimeException(e); + } + }); + } + + private class MyDefaultUserInterfaceControl extends DefaultUserInterfaceControl { + @Override + public void taskStarted(TaskStartedInfo info) { + clientApi.taskStarted(info); + } + + @Override + public void taskProgress(int position) { + clientApi.taskProgress(position); + } + + @Override + public void taskFinished(TaskFinishedInfo info) { + clientApi.taskFinished(info); + } + + @Override + protected void macroStarted(TaskStartedInfo info) { + clientApi.taskStarted(info); + } + + @Override + protected synchronized void macroFinished(ProofMacroFinishedInfo info) { + clientApi.taskFinished(info); + } + + @Override + public void loadingStarted(AbstractProblemLoader loader) { + super.loadingStarted(loader); + } + + @Override + public void loadingFinished(AbstractProblemLoader loader, + IPersistablePO.LoadedPOContainer poContainer, ProofAggregate proofList, + AbstractProblemLoader.ReplayResult result) throws ProblemLoaderException { + super.loadingFinished(loader, poContainer, proofList, result); + } + + @Override + public void progressStarted(Object sender) { + super.progressStarted(sender); + } + + @Override + public void progressStopped(Object sender) { + super.progressStopped(sender); + } + + @Override + public void reportStatus(Object sender, String status, int progress) { + super.reportStatus(sender, status, progress); + } + + @Override + public void reportStatus(Object sender, String status) { + super.reportStatus(sender, status); + } + + @Override + public void resetStatus(Object sender) { + super.resetStatus(sender); + } + + @Override + public void reportException(Object sender, ProofOblInput input, Exception e) { + super.reportException(sender, input, e); + } + + @Override + public void setProgress(int progress) { + super.setProgress(progress); + } + + @Override + public void setMaximum(int maximum) { + super.setMaximum(maximum); + } + + @Override + public void reportWarnings(ImmutableSet warnings) { + super.reportWarnings(warnings); + } + + @Override + public void showIssueDialog(Collection issues) { + super.showIssueDialog(issues); + } + } + + } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/NodeTextDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/NodeTextDesc.java new file mode 100644 index 00000000000..a7d9585c719 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/NodeTextDesc.java @@ -0,0 +1,11 @@ +/* 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.keyproject.key.api; + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +public record NodeTextDesc(NodeTextId id, String result) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/NodeTextId.java b/keyext.api/src/main/java/org/keyproject/key/api/NodeTextId.java new file mode 100644 index 00000000000..7e956689c08 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/NodeTextId.java @@ -0,0 +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 org.keyproject.key.api; + +import org.keyproject.key.api.data.KeyIdentifications.NodeId; + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +public record NodeTextId(NodeId nodeId, int nodeTextId) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java b/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java index 0a7531097e9..eefa000ac8c 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java @@ -120,6 +120,8 @@ public void run() { } try { + final var keyApi = new KeyApiImpl(); + if (websocket) { var launcherBuilder = new WebSocketLauncherBuilder() .setOutput(out) @@ -127,14 +129,18 @@ public void run() { .traceMessages(new PrintWriter(System.err)) .validateMessages(true); launcherBuilder.configureGson(StartServer::configureJson); - launcherBuilder.setLocalService(new KeyApiImpl(adapter)); + launcherBuilder.setLocalService(keyApi); launcherBuilder.setRemoteInterface(ClientApi.class); - launcherBuilder.create().startListening().get(); + + final var clientApiLauncher = launcherBuilder.create(); + keyApi.setClientApi(clientApiLauncher.getRemoteProxy()); + clientApiLauncher.startListening().get(); } else { establishStreams(); try (var lin = in; var lout = out) { - var listener = launch(lout, lin); + var listener = launch(lout, lin, keyApi); LOGGER.info("JSON-RPC is listening for requests"); + keyApi.setClientApi(listener.getRemoteProxy()); listener.startListening().get(); } } @@ -148,7 +154,7 @@ public static void configureJson(GsonBuilder gsonBuilder) { adapter = new KeyAdapter(gsonBuilder); } - public static Launcher launch(OutputStream out, InputStream in) { + public static Launcher launch(OutputStream out, InputStream in, KeyApiImpl keyApi) { // var localServices = getLocalServices(); // var remoteInterfaces = getRemoteInterfaces(); var launcherBuilder = new Launcher.Builder() @@ -159,7 +165,7 @@ public static Launcher launch(OutputStream out, InputStream in) { launcherBuilder.configureGson(StartServer::configureJson); // if (localServices != null && !localServices.isEmpty()) - launcherBuilder.setLocalService(new KeyApiImpl(adapter)); + launcherBuilder.setLocalService(keyApi); // if (remoteInterfaces != null && !remoteInterfaces.isEmpty()) launcherBuilder.setRemoteInterface(ClientApi.class); diff --git a/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java b/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java new file mode 100644 index 00000000000..3ac97bdc3d5 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java @@ -0,0 +1,134 @@ +/* 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.keyproject.key.api; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import de.uka.ilkd.key.control.KeYEnvironment; +import de.uka.ilkd.key.control.ProofControl; +import de.uka.ilkd.key.logic.Name; +import de.uka.ilkd.key.logic.PosInOccurrence; +import de.uka.ilkd.key.macros.ProofMacro; +import de.uka.ilkd.key.macros.ProofMacroFacade; +import de.uka.ilkd.key.pp.PosInSequent; +import de.uka.ilkd.key.proof.Goal; +import de.uka.ilkd.key.rule.*; + +import org.key_project.util.collection.ImmutableList; +import org.key_project.util.collection.ImmutableSLList; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.keyproject.key.api.data.KeyIdentifications; +import org.keyproject.key.api.data.TermActionDesc; +import org.keyproject.key.api.data.TermActionKind; + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +public class TermActionUtil { + private static final Set CLUTTER_RULESETS = + Set.of(new Name("notHumanReadable"), + new Name("obsolete"), + new Name("pullOutQuantifierAll"), + new Name("pullOutQuantifierEx")); + private static final Set CLUTTER_RULES = Set.of( + new Name("cut_direct_r"), + new Name("cut_direct_l"), + new Name("case_distinction_r"), + new Name("case_distinction_l"), + new Name("local_cut"), + new Name("commute_and_2"), + new Name("commute_or_2"), + new Name("boxToDiamond"), + new Name("pullOut"), + new Name("typeStatic"), + new Name("less_is_total"), + new Name("less_zero_is_total"), + new Name("applyEqReverse"), + + // the following are used for drag'n'drop interactions + new Name("eqTermCut"), + new Name("instAll"), + new Name("instEx")); + private static final Set FILTER_SCRIPT_COMMANDS = Set.of( + "exit", + "leave", + "javascript", + "skip", + "macro", + "rule", + "script"); + + private final PosInSequent pos; + private final Goal goal; + private final PosInOccurrence occ; + + private final List actions = new ArrayList<>(1024); + private final NodeTextId nodeTextId; + + public TermActionUtil(@NonNull NodeTextId nodeTextId, @NonNull KeYEnvironment env, + @NonNull PosInSequent pos, @NonNull Goal goal) { + this.pos = pos; + this.goal = goal; + this.nodeTextId = nodeTextId; + occ = pos.getPosInOccurrence(); + ProofControl c = env.getUi().getProofControl(); + final ImmutableList builtInRules = c.getBuiltInRule(goal, occ); + for (ProofMacro macro : ProofMacroFacade.instance().getMacros()) { + var id = new KeyIdentifications.TermActionId(nodeTextId.nodeId(), pos.toString(), + "macro:" + macro.getScriptCommandName()); + TermActionDesc ta = new TermActionDesc(id, macro.getName(), macro.getDescription(), + macro.getCategory(), TermActionKind.Macro); + add(ta); + } + ImmutableList findTaclet = c.getFindTaclet(goal, occ); + var find = removeRewrites(findTaclet) + .prepend(c.getRewriteTaclet(goal, occ)); + var nofind = c.getNoFindTaclet(goal); + + + for (TacletApp tacletApp : find) { + var id = new KeyIdentifications.TermActionId(nodeTextId.nodeId(), pos.toString(), + "find:" + tacletApp.rule()); + TermActionDesc ta = new TermActionDesc(id, tacletApp.rule().displayName(), + tacletApp.rule().toString(), "", TermActionKind.Taclet); + add(ta); + } + + for (TacletApp tacletApp : nofind) { + var id = new KeyIdentifications.TermActionId(nodeTextId.nodeId(), pos.toString(), + "nofind:" + tacletApp.rule()); + TermActionDesc ta = new TermActionDesc(id, tacletApp.rule().displayName(), + tacletApp.rule().toString(), "", TermActionKind.Taclet); + add(ta); + } + } + + private void add(TermActionDesc ta) { + actions.add(ta); + } + + /** + * Removes RewriteTaclet from the list. + * + * @param list the IList from where the RewriteTaclet are removed + * @return list without RewriteTaclets + */ + private static ImmutableList removeRewrites( + ImmutableList list) { + ImmutableList result = ImmutableSLList.nil(); + for (TacletApp tacletApp : list) { + Taclet taclet = tacletApp.taclet(); + result = (taclet instanceof RewriteTaclet ? result : result.prepend(tacletApp)); + } + return result; + } + + public List getActions() { + return actions; + } +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java b/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java index af874cda8b5..77ab08d832a 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java @@ -5,16 +5,11 @@ import java.io.File; import java.io.IOException; -import java.lang.ref.WeakReference; import java.lang.reflect.Type; -import de.uka.ilkd.key.Identifiable; import de.uka.ilkd.key.logic.op.Function; import de.uka.ilkd.key.macros.ProofMacro; -import de.uka.ilkd.key.proof.Proof; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; import com.google.gson.*; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; @@ -25,33 +20,32 @@ * @version 1 (14.10.23) */ public class KeyAdapter { - private final BiMap> map = HashBiMap.create(1024); + // private final BiMap> map = HashBiMap.create(1024); // private final TypeAdapter adaptor; public KeyAdapter(GsonBuilder gson) { gson.registerTypeAdapter(File.class, new FileTypeAdapter()); - gson.registerTypeAdapter(Function.class, new FunctionTypeAdapter()); - gson.registerTypeAdapter(Proof.class, new IdentifiableTypeAdapter()); - gson.registerTypeAdapter(ProofMacro.class, new MacroTypeAdapter()); - // adaptor = gson.create().getAdapter(Object.class); + // gson.registerTypeAdapter(Function.class, new FunctionSerializer()); + // gson.registerTypeAdapter(ProofMacro.class, new MacroSerializer()); } - // translating entities to identification strings - public String insert(Identifiable p) { - var id = p.identification(); - if (!map.containsKey(id)) { - map.put(id, new WeakReference<>(p)); - } - return id; - } - - public Object find(String id) { - return map.get(id).get(); - } - // endregion + /* + * //translating entities to identification strings + * public void insert(Identifiable p) { + * var id = p.identification(); + * if (!map.containsKey(id)) { + * map.put(id, new WeakReference<>(p)); + * } + * } + * + * public Object find(String id) { + * return map.get(id).get(); + * } + * //endregion + */ - class MacroTypeAdapter implements JsonSerializer { + static class MacroSerializer implements JsonSerializer { @Override public JsonElement serialize(ProofMacro src, Type typeOfSrc, JsonSerializationContext context) { @@ -59,7 +53,7 @@ public JsonElement serialize(ProofMacro src, Type typeOfSrc, } } - class FileTypeAdapter extends TypeAdapter { + static class FileTypeAdapter extends TypeAdapter { @Override public void write(JsonWriter out, File value) throws IOException { out.value(value.toString()); @@ -71,7 +65,7 @@ public File read(JsonReader in) throws IOException { } } - class FunctionTypeAdapter implements JsonSerializer { + static class FunctionSerializer implements JsonSerializer { @Override public JsonElement serialize(Function src, Type typeOfSrc, JsonSerializationContext context) { @@ -86,19 +80,22 @@ public JsonElement serialize(Function src, Type typeOfSrc, } } - class IdentifiableTypeAdapter - implements JsonSerializer, JsonDeserializer { - @Override - public Identifiable deserialize(JsonElement json, Type typeOfT, - JsonDeserializationContext context) throws JsonParseException { - return (Identifiable) find(json.getAsString()); - } - - @Override - public JsonElement serialize(Identifiable src, Type typeOfSrc, - JsonSerializationContext context) { - insert(src); - return context.serialize(src.identification()); - } - } + /* + * class IdentifiableTypeAdapter implements JsonSerializer, + * JsonDeserializer { + * + * @Override + * public Identifiable deserialize(JsonElement json, Type typeOfT, + * JsonDeserializationContext context) throws JsonParseException { + * return (Identifiable) find(json.getAsString()); + * } + * + * @Override + * public JsonElement serialize(Identifiable src, Type typeOfSrc, + * JsonSerializationContext context) { + * insert(src); + * return context.serialize(src.identification()); + * } + * } + */ } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/ContractDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/ContractDesc.java index d17a8bad65c..1865f01685d 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/ContractDesc.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/ContractDesc.java @@ -3,9 +3,20 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.data; +import de.uka.ilkd.key.java.Services; +import de.uka.ilkd.key.speclang.Contract; + /** * @author Alexander Weigl * @version 1 (13.10.23) */ -public record ContractDesc() { +public record ContractDesc(KeyIdentifications.ContractId contractId, String name, + String displayName, + String typeName, String htmlText, String plainText) { + public static ContractDesc from(KeyIdentifications.EnvironmentId envId, Services services, + Contract it) { + return new ContractDesc(new KeyIdentifications.ContractId(envId, it.id()), + it.getName(), it.getDisplayName(), it.getTypeName(), + it.getHTMLText(services), it.getPlainText(services)); + } } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/FunctionDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/FunctionDesc.java index cbd2c547182..df3e8f285ef 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/FunctionDesc.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/FunctionDesc.java @@ -5,9 +5,21 @@ import java.util.List; +import de.uka.ilkd.key.logic.op.Function; + /** * @author Alexander Weigl * @version 1 (15.10.23) */ -public record FunctionDesc(String name, String sort, List sorts) { +public record FunctionDesc(String name, String sort, SortDesc retSort, List argSorts, + boolean rigid, + boolean unique, boolean skolemConstant) { + public static FunctionDesc from(Function fn) { + return new FunctionDesc(fn.name().toString(), fn.proofToString(), + SortDesc.from(fn.sort()), + fn.argSorts().stream().map(SortDesc::from).toList(), + fn.isRigid(), + fn.isUnique(), + fn.isSkolemConstant()); + } } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/KeyIdentifications.java b/keyext.api/src/main/java/org/keyproject/key/api/data/KeyIdentifications.java new file mode 100644 index 00000000000..83fb93bd517 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/KeyIdentifications.java @@ -0,0 +1,171 @@ +/* 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.keyproject.key.api.data; + +import java.lang.ref.WeakReference; +import java.util.Objects; + +import de.uka.ilkd.key.control.KeYEnvironment; +import de.uka.ilkd.key.proof.Node; +import de.uka.ilkd.key.proof.Proof; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.keyproject.key.api.NodeTextId; +import org.keyproject.key.api.internal.NodeText; + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +public class KeyIdentifications { + private final BiMap mapEnv = HashBiMap.create(16); + + public KeyEnvironmentContainer getContainer(EnvironmentId environmentId) { + return Objects.requireNonNull(mapEnv.get(environmentId), + "Could not find environment for id" + environmentId); + } + + public ProofContainer getContainer(ProofId proofId) { + return Objects.requireNonNull(getContainer(proofId.env()).mapProof.get(proofId), + "Could not find proof for id" + proofId); + } + + public KeYEnvironment find(EnvironmentId envid) { + return Objects.requireNonNull(getContainer(envid).env.get(), + "Environment was removed by gc"); + } + + @NonNull + public Proof find(ProofId proofId) { + return Objects.requireNonNull(getContainer(proofId).wProof.get(), + "Could not find a proof for id " + proofId); + } + + @NonNull + public NodeText find(NodeTextId nodeTextId) { + return Objects.requireNonNull( + getContainer(nodeTextId.nodeId().proofId()).mapGoalText.get(nodeTextId), + "Could not find a print-out with the id " + nodeTextId); + } + + public void dispose(NodeTextId nodeTextId) { + var c = getContainer(nodeTextId.nodeId().proofId()); + c.mapGoalText.remove(nodeTextId); + } + + public void dispose(ProofId id) { + var c = getContainer(id); + getContainer(id.env).mapProof.remove(id); + c.dispose(); + } + + public Node find(NodeId nodeId) { + @NonNull + Proof p = find(nodeId.proofId); + var opt = p.findAny(it -> it.serialNr() == nodeId.nodeId()); + return Objects.requireNonNull(opt, "Could not find node with serialNr " + nodeId.nodeId); + } + + public ProofId register(EnvironmentId envId, Proof proof) { + var id = new ProofId(envId, proof.name().toString()); + getContainer(envId).mapProof.put(id, new ProofContainer(proof)); + return id; + } + + public void register(NodeTextId nodeId, NodeText nodeText) { + var c = getContainer(nodeId.nodeId().proofId()); + c.mapGoalText().put(nodeId, nodeText); + + } + + public EnvironmentId register(EnvironmentId envId, KeYEnvironment env) { + mapEnv.put(envId, new KeyEnvironmentContainer(env)); + return envId; + } + + public ProofId register(ProofId proofId, Proof proof) { + getContainer(proofId.env()).mapProof.put(proofId, new ProofContainer(proof)); + return proofId; + } + + + /** + * @author Alexander Weigl + * @version 1 (28.10.23) + */ + public record EnvironmentId(String envId) { + } + + /** + * @author Alexander Weigl + * @version 1 (28.10.23) + */ + public record ContractId(EnvironmentId envId, int contractId) { + } + + /** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ + public record NodeId(ProofId proofId, int nodeId) { + } + + public record ProofId(EnvironmentId env, String proofId) { + } + + /** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ + public record PrintId(String id) { + } + + /** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ + public record TermActionId(NodeId nodeId, String pio, String id) { + } + + /** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ + public record TreeNodeId(String id) { + } + + + /** + * @author Alexander Weigl + * @version 1 (28.10.23) + */ + public record KeyEnvironmentContainer(WeakReference> env, + BiMap mapProof) { + + public KeyEnvironmentContainer(KeYEnvironment env) { + this(new WeakReference<>(env), HashBiMap.create(1)); + } + + void dispose() { + env.clear(); + mapProof.clear(); + } + } + + private record ProofContainer(WeakReference wProof, + BiMap> mapNode, + BiMap> mapTreeNode, + BiMap mapGoalText) { + public ProofContainer(Proof proof) { + this(new WeakReference<>(proof), HashBiMap.create(16), HashBiMap.create(16), + HashBiMap.create(16)); + } + + void dispose() { + mapNode.clear(); + } + } +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/MacroStatistic.java b/keyext.api/src/main/java/org/keyproject/key/api/data/MacroStatistic.java index c9a0b98ad44..50cd735bd82 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/MacroStatistic.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/MacroStatistic.java @@ -3,9 +3,18 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.data; +import de.uka.ilkd.key.macros.ProofMacroFinishedInfo; + /** * @author Alexander Weigl * @version 1 (13.10.23) */ -public record MacroStatistic() { +public record MacroStatistic(KeyIdentifications.ProofId proofId, String macroId, boolean cancelled, + int appliedRules, + int closedGoals) { + public static MacroStatistic from(KeyIdentifications.ProofId proofId, + ProofMacroFinishedInfo info) { + return new MacroStatistic(proofId, info.getMacro().getName(), info.isCancelled(), + info.getAppliedRules(), info.getClosedGoals()); + } } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java index ba90e14ff72..44941cb8dfd 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java @@ -3,9 +3,20 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.data; +import java.util.List; + +import org.checkerframework.checker.nullness.qual.Nullable; + /** * @author Alexander Weigl * @version 1 (13.10.23) */ -public record NodeDesc() { +public record NodeDesc(KeyIdentifications.NodeId nodeid, String branchLabel, + boolean scriptRuleApplication, + @Nullable List children) { + public NodeDesc(KeyIdentifications.ProofId proofId, int serialNr, String branchLabel, + boolean scriptRuleApplication) { + this(new KeyIdentifications.NodeId(proofId, serialNr), branchLabel, scriptRuleApplication, + null); + } } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/ProofMacroDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/ProofMacroDesc.java new file mode 100644 index 00000000000..56fba7086c6 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/ProofMacroDesc.java @@ -0,0 +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 org.keyproject.key.api.data; + +import de.uka.ilkd.key.macros.ProofMacro; + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +public record ProofMacroDesc(String name, String category, String description, + String scriptCommandName) { + public static ProofMacroDesc from(ProofMacro proofMacro) { + return new ProofMacroDesc(proofMacro.getName(), proofMacro.getCategory(), + proofMacro.getDescription(), proofMacro.getScriptCommandName()); + } +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/ProofScriptCommandDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/ProofScriptCommandDesc.java new file mode 100644 index 00000000000..0f086d8f936 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/ProofScriptCommandDesc.java @@ -0,0 +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 org.keyproject.key.api.data; + +import de.uka.ilkd.key.macros.scripts.ProofScriptCommand; + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +public record ProofScriptCommandDesc() { + public static ProofScriptCommandDesc from(ProofScriptCommand proofScriptCommand) { + return null; + } +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/SortDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/SortDesc.java index 7c0a99e5a17..dbb45fa68c8 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/SortDesc.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/SortDesc.java @@ -3,9 +3,20 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.data; +import java.util.List; + +import de.uka.ilkd.key.logic.sort.Sort; + /** * @author Alexander Weigl * @version 1 (13.10.23) */ -public record SortDesc() { +public record SortDesc(String string, String documentation, + List extendsSorts, + boolean anAbstract, String s) { + public static SortDesc from(Sort sort) { + return new SortDesc(sort.name().toString(), sort.getDocumentation(), + sort.extendsSorts().stream().map(SortDesc::from).toList(), + sort.isAbstract(), sort.declarationString()); + } } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionDesc.java new file mode 100644 index 00000000000..e3953a6c8bd --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionDesc.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 org.keyproject.key.api.data; + +import org.keyproject.key.api.data.KeyIdentifications.TermActionId; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +public record TermActionDesc(TermActionId commandId, String displayName, String description, + String category, + TermActionKind kind) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionId.java b/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionId.java index cdf2d63ba92..4b61cb5bcee 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionId.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionId.java @@ -2,10 +2,3 @@ * KeY is licensed under the GNU General Public License Version 2 * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.data; - -/** - * @author Alexander Weigl - * @version 1 (13.10.23) - */ -public record TermActionId(String id) { -} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionKind.java b/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionKind.java new file mode 100644 index 00000000000..0a371aa48e4 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionKind.java @@ -0,0 +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 org.keyproject.key.api.data; + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +public enum TermActionKind { + BuiltIn, Script, Macro, Taclet +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/TreeNodeId.java b/keyext.api/src/main/java/org/keyproject/key/api/data/TreeNodeId.java index c476521fa1a..4b61cb5bcee 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/TreeNodeId.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/TreeNodeId.java @@ -2,10 +2,3 @@ * KeY is licensed under the GNU General Public License Version 2 * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.data; - -/** - * @author Alexander Weigl - * @version 1 (13.10.23) - */ -public record TreeNodeId(String id) { -} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/internal/NodeText.java b/keyext.api/src/main/java/org/keyproject/key/api/internal/NodeText.java new file mode 100644 index 00000000000..84898829e9a --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/internal/NodeText.java @@ -0,0 +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 org.keyproject.key.api.internal; + +import de.uka.ilkd.key.pp.InitialPositionTable; + +/** + * @author Alexander Weigl + * @version 1 (13.10.23) + */ +public record NodeText(String text, InitialPositionTable table) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvApi.java index 5d61e479c3a..c70fe8a15d2 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvApi.java @@ -3,16 +3,11 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.remoteapi; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -import de.uka.ilkd.key.control.KeYEnvironment; -import de.uka.ilkd.key.logic.op.Function; -import de.uka.ilkd.key.proof.Proof; - import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; import org.keyproject.key.api.data.ContractDesc; +import org.keyproject.key.api.data.FunctionDesc; +import org.keyproject.key.api.data.KeyIdentifications.*; import org.keyproject.key.api.data.SortDesc; /** @@ -22,14 +17,14 @@ @JsonSegment("env") public interface EnvApi { @JsonRequest - CompletableFuture> sorts(KeYEnvironment env); + CompletableFuture> sorts(EnvironmentId env); @JsonRequest - CompletableFuture> functions(KeYEnvironment env); + CompletableFuture> functions(EnvironmentId env); @JsonRequest - CompletableFuture> contracts(KeYEnvironment env); + CompletableFuture> contracts(EnvironmentId env); @JsonRequest - CompletableFuture openContract(KeYEnvironment env, String contractId); + CompletableFuture openContract(ContractId contractId); } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java index 46eccd412fe..e072f01cf82 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java @@ -3,18 +3,13 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.remoteapi; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -import de.uka.ilkd.key.proof.Node; - import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; -import org.keyproject.key.api.data.GoalText; -import org.keyproject.key.api.data.PrintId; -import org.keyproject.key.api.data.TermAction; -import org.keyproject.key.api.data.TermActionId; +import org.keyproject.key.api.NodeTextDesc; +import org.keyproject.key.api.NodeTextId; +import org.keyproject.key.api.data.*; +import org.keyproject.key.api.data.KeyIdentifications.*; /** * @author Alexander Weigl @@ -23,14 +18,14 @@ @JsonSegment("goal") public interface GoalApi { @JsonRequest - CompletableFuture print(Node id); + CompletableFuture print(NodeId id, PrintOptions options); @JsonRequest - CompletableFuture> actions(PrintId id, int pos); + CompletableFuture> actions(NodeTextId id, int pos); @JsonRequest("apply_action") - CompletableFuture> applyAction(TermActionId id); + CompletableFuture> applyAction(TermActionId id); @JsonNotification("free") - void freePrint(PrintId id); + void freePrint(NodeTextId id); } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApi.java index 7f269f0eceb..a94b35967af 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/KeyApi.java @@ -6,6 +6,6 @@ /** * The combined interface that is provided by KeY. */ -public interface KeyApi - extends ExampleApi, MetaApi, ServerManagement, ProofApi, ProofTreeApi, GoalApi, EnvApi { +public interface KeyApi extends ExampleApi, MetaApi, ServerManagement, ProofApi, ProofTreeApi, + GoalApi, EnvApi, ProofLoadApi { } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/MetaApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/MetaApi.java index 5a0454d8b93..ea2c8174a58 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/MetaApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/MetaApi.java @@ -6,11 +6,11 @@ import java.util.List; import java.util.concurrent.CompletableFuture; -import de.uka.ilkd.key.macros.ProofMacro; -import de.uka.ilkd.key.macros.scripts.ProofScriptCommand; - import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; +import org.keyproject.key.api.data.KeyIdentifications.*; +import org.keyproject.key.api.data.ProofMacroDesc; +import org.keyproject.key.api.data.ProofScriptCommandDesc; @JsonSegment("meta") public interface MetaApi { @@ -18,8 +18,8 @@ public interface MetaApi { CompletableFuture getVersion(); @JsonRequest("available_macros") - CompletableFuture> getAvailableMacros(); + CompletableFuture> getAvailableMacros(); @JsonRequest("available_script_commands") - CompletableFuture>> getAvailableScriptCommands(); + CompletableFuture> getAvailableScriptCommands(); } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/PrintOptions.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/PrintOptions.java new file mode 100644 index 00000000000..845c485ca7e --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/PrintOptions.java @@ -0,0 +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 org.keyproject.key.api.remoteapi; + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +public record PrintOptions(boolean unicode, int width, int indentation, boolean pure, + boolean termLabels) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofApi.java index 93089544d7b..80131fa143c 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofApi.java @@ -3,14 +3,10 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.remoteapi; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -import de.uka.ilkd.key.proof.Node; -import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.proof.Statistics; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.keyproject.key.api.data.KeyIdentifications.*; import org.keyproject.key.api.data.MacroStatistic; import org.keyproject.key.api.data.NodeDesc; import org.keyproject.key.api.data.StreategyOptions; @@ -21,33 +17,34 @@ */ public interface ProofApi { @JsonRequest - CompletableFuture script(Proof proof, String scriptLine, + CompletableFuture script(ProofId proof, String scriptLine, StreategyOptions options); @JsonRequest - CompletableFuture macro(Proof proof, String macroId, StreategyOptions options); + CompletableFuture macro(ProofId proof, String macroId, + StreategyOptions options); @JsonRequest - CompletableFuture auto(Proof proof, StreategyOptions options); + CompletableFuture auto(ProofId proof, StreategyOptions options); @JsonRequest - CompletableFuture dispose(Proof proof); + CompletableFuture dispose(ProofId proof); @JsonRequest - CompletableFuture goals(Proof proof); + CompletableFuture> goals(ProofId proof, boolean onlyOpened, boolean onlyEnabled); @JsonRequest - CompletableFuture tree(Proof proof); + CompletableFuture tree(ProofId proof); @JsonRequest - CompletableFuture root(Proof proof); + CompletableFuture root(ProofId proof); @JsonRequest - CompletableFuture> children(Proof proof, Node nodeId); + CompletableFuture> children(NodeId nodeId); @JsonRequest - CompletableFuture> pruneTo(Proof proof, Node nodeId); + CompletableFuture> pruneTo(NodeId nodeId); @JsonRequest - CompletableFuture statistics(Proof proof); + CompletableFuture statistics(ProofId proof); } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/ProofLoading.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofLoadApi.java similarity index 54% rename from keyext.api/src/main/java/org/keyproject/key/api/data/ProofLoading.java rename to keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofLoadApi.java index dc8e199a77a..2b5f5e78187 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/ProofLoading.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofLoadApi.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 org.keyproject.key.api.data; +package org.keyproject.key.api.remoteapi; import java.util.concurrent.CompletableFuture; -import de.uka.ilkd.key.control.KeYEnvironment; -import de.uka.ilkd.key.proof.Proof; import de.uka.ilkd.key.proof.io.ProblemLoaderException; +import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; +import org.keyproject.key.api.data.KeyIdentifications.EnvironmentId; +import org.keyproject.key.api.data.KeyIdentifications.ProofId; +import org.keyproject.key.api.data.LoadParams; +import org.keyproject.key.api.data.ProblemDefinition; /** * Functionalities for loading proofs either from a built-in example, or from a set of files. @@ -19,7 +22,7 @@ * @since v1 */ @JsonSegment("loading") -public interface ProofLoading { +public interface ProofLoadApi { /** * I am not sure whether this is helpful. Mainly a feature for testing?! * @@ -27,13 +30,22 @@ public interface ProofLoading { * @return */ @JsonRequest - CompletableFuture loadExample(String id); + CompletableFuture loadExample(String id); /** * */ @JsonRequest - CompletableFuture loadProblem(ProblemDefinition problem); + CompletableFuture loadProblem(ProblemDefinition problem); + + /** + * + */ + @JsonRequest + CompletableFuture loadKey(String content); + + @JsonRequest + CompletableFuture loadTerm(String term); /** * Test! @@ -43,5 +55,6 @@ public interface ProofLoading { * @throws ProblemLoaderException if something went wrong */ @JsonRequest - CompletableFuture> load(LoadParams params) throws ProblemLoaderException; + CompletableFuture> load(LoadParams params) + throws ProblemLoaderException; } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofTreeApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofTreeApi.java index 162aa884c87..459adfce637 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofTreeApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofTreeApi.java @@ -3,15 +3,11 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.remoteapi; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -import de.uka.ilkd.key.proof.Proof; - import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; +import org.keyproject.key.api.data.KeyIdentifications.ProofId; +import org.keyproject.key.api.data.KeyIdentifications.TreeNodeId; import org.keyproject.key.api.data.TreeNodeDesc; -import org.keyproject.key.api.data.TreeNodeId; /** * @author Alexander Weigl @@ -20,11 +16,11 @@ @JsonSegment("proofTree") public interface ProofTreeApi { @JsonRequest("root") - CompletableFuture treeRoot(Proof id); + CompletableFuture treeRoot(ProofId id); @JsonRequest("children") - CompletableFuture> treeChildren(Proof proof, TreeNodeId nodeId); + CompletableFuture> treeChildren(ProofId proof, TreeNodeId nodeId); @JsonRequest("subtree") - CompletableFuture> treeSubtree(Proof proof, TreeNodeId nodeId); + CompletableFuture> treeSubtree(ProofId proof, TreeNodeId nodeId); } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java index 9dc420279d3..9d5159cb901 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java @@ -3,8 +3,8 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.remoteclient; -import java.util.concurrent.CompletableFuture; -import javax.annotation.Nullable; +import de.uka.ilkd.key.prover.TaskFinishedInfo; +import de.uka.ilkd.key.prover.TaskStartedInfo; import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; @@ -72,5 +72,12 @@ public interface ClientApi { */ @JsonRequest CompletableFuture showDocument(ShowDocumentParams params); + + + void taskFinished(TaskFinishedInfo info); + + void taskProgress(int position); + + void taskStarted(TaskStartedInfo info); // endregion } diff --git a/keyext.api/src/test/java/org/keyproject/key/api/SimpleClient.java b/keyext.api/src/test/java/org/keyproject/key/api/SimpleClient.java index bba9b06d709..5f2335e5ef3 100644 --- a/keyext.api/src/test/java/org/keyproject/key/api/SimpleClient.java +++ b/keyext.api/src/test/java/org/keyproject/key/api/SimpleClient.java @@ -6,6 +6,9 @@ import java.util.concurrent.CompletableFuture; import javax.annotation.Nullable; +import de.uka.ilkd.key.prover.TaskFinishedInfo; +import de.uka.ilkd.key.prover.TaskStartedInfo; + import org.keyproject.key.api.remoteclient.*; class SimpleClient implements ClientApi { @@ -35,4 +38,19 @@ public CompletableFuture userResponse(ShowMessageRequestParam public CompletableFuture showDocument(ShowDocumentParams params) { return null; } + + @Override + public void taskFinished(TaskFinishedInfo info) { + System.out.println(info); + } + + @Override + public void taskProgress(int position) { + + } + + @Override + public void taskStarted(TaskStartedInfo info) { + System.out.println(info); + } } diff --git a/keyext.api/src/test/java/org/keyproject/key/api/TestRpc.java b/keyext.api/src/test/java/org/keyproject/key/api/TestRpc.java index 9c21634adae..5ebb562d474 100644 --- a/keyext.api/src/test/java/org/keyproject/key/api/TestRpc.java +++ b/keyext.api/src/test/java/org/keyproject/key/api/TestRpc.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.keyproject.key.api.adapters.KeyAdapter; import org.keyproject.key.api.remoteapi.KeyApi; import org.keyproject.key.api.remoteclient.ClientApi; @@ -27,7 +28,7 @@ public class TestRpc { private KeyApi keyApi; @BeforeEach - void setup() throws IOException, ExecutionException, InterruptedException, TimeoutException { + void setup() throws IOException { PipedInputStream inClient = new PipedInputStream(); PipedOutputStream outClient = new PipedOutputStream(); PipedInputStream inServer = new PipedInputStream(); @@ -36,7 +37,9 @@ void setup() throws IOException, ExecutionException, InterruptedException, Timeo inClient.connect(outServer); outClient.connect(inServer); - Launcher serverLauncher = StartServer.launch(outServer, inServer); + KeyApiImpl impl = new KeyApiImpl(new KeyAdapter(null)); + Launcher serverLauncher = StartServer.launch(outServer, inServer, impl); + impl.setClientApi(serverLauncher.getRemoteProxy()); var client = new SimpleClient(); Launcher clientLauncher = new Launcher.Builder() From 1178d46db7148a3716503f9962175e01382553d8 Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Sun, 29 Oct 2023 22:44:19 +0100 Subject: [PATCH 06/35] more doc and py generation --- api.meta.json | 1144 +++++++++++++++++ api.meta.md | 480 +++++++ api.py | 374 ++++++ keyext.api.doc/build.gradle | 1 + keyext.api.doc/src/main/java/DocGen.java | 190 +++ .../src/main/java/ExtractMetaData.java | 364 ++++-- keyext.api.doc/src/main/java/Metamodel.java | 100 ++ keyext.api.doc/src/main/java/PyGen.java | 145 +++ keyext.api/build.gradle | 6 +- .../org/keyproject/key/api/KeyApiImpl.java | 15 +- .../org/keyproject/key/api/NodeTextId.java | 9 - .../keyproject/key/api/TermActionUtil.java | 2 + .../key/api/data/KeyIdentifications.java | 6 +- .../org/keyproject/key/api/data/NodeDesc.java | 4 +- .../keyproject/key/api/data/NodeTextDesc.java | 12 + .../key/api/data/ProblemDefinition.java | 2 + .../key/api/remoteapi/ExampleApi.java | 4 +- .../key/api/remoteapi/ExampleDesc.java | 16 + .../keyproject/key/api/remoteapi/GoalApi.java | 3 +- .../key/api/remoteapi/ServerManagement.java | 2 +- keyext.api/src/main/python/keyapi/rpc.py | 76 -- .../java/org/keyproject/key/api/TestRpc.java | 6 +- .../keyapi/__init__.py | 11 +- keyext.client.python/keyapi/rpc.py | 200 +++ .../python => keyext.client.python}/main.py | 0 keyext.client.python/rwtest.py | 402 ++++++ 26 files changed, 3343 insertions(+), 231 deletions(-) create mode 100644 api.meta.json create mode 100644 api.meta.md create mode 100644 api.py create mode 100644 keyext.api.doc/src/main/java/DocGen.java create mode 100644 keyext.api.doc/src/main/java/Metamodel.java create mode 100644 keyext.api.doc/src/main/java/PyGen.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/NodeTextDesc.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ExampleDesc.java delete mode 100644 keyext.api/src/main/python/keyapi/rpc.py rename {keyext.api/src/main/python => keyext.client.python}/keyapi/__init__.py (54%) create mode 100644 keyext.client.python/keyapi/rpc.py rename {keyext.api/src/main/python => keyext.client.python}/main.py (100%) create mode 100644 keyext.client.python/rwtest.py diff --git a/api.meta.json b/api.meta.json new file mode 100644 index 00000000000..180b16a3019 --- /dev/null +++ b/api.meta.json @@ -0,0 +1,1144 @@ +{ + "endpoints": [ + { + "name": "examples/list", + "documentation": "", + "args": [], + "returnType": { + "type": { + "typeName": "ExampleDesc", + "fields": [ + { + "name": "name", + "type": "STRING" + }, + { + "name": "description", + "type": "STRING" + } + ], + "documentation": "" + }, + "documentation": "" + } + }, + { + "name": "meta/version", + "documentation": "", + "args": [], + "returnType": "STRING" + }, + { + "name": "meta/available_script_commands", + "documentation": "", + "args": [], + "returnType": { + "type": { + "typeName": "ProofScriptCommandDesc", + "fields": [], + "documentation": "" + }, + "documentation": "" + } + }, + { + "name": "meta/available_macros", + "documentation": "", + "args": [], + "returnType": { + "type": { + "typeName": "ProofMacroDesc", + "fields": [ + { + "name": "name", + "type": "STRING" + }, + { + "name": "category", + "type": "STRING" + }, + { + "name": "description", + "type": "STRING" + }, + { + "name": "scriptCommandName", + "type": "STRING" + } + ], + "documentation": "" + }, + "documentation": "" + } + }, + { + "name": "server/exit", + "documentation": "", + "args": [] + }, + { + "name": "server/shutdown", + "documentation": "", + "args": [], + "returnType": "BOOL" + }, + { + "name": "server/setTrace", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "SetTraceParams" + } + ] + }, + { + "name": "proofTree/root", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "ProofId" + } + ], + "returnType": { + "typeName": "TreeNodeDesc", + "fields": [], + "documentation": "" + } + }, + { + "name": "proofTree/children", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "ProofId" + }, + { + "name": "arg1", + "type": "TreeNodeId" + } + ], + "returnType": { + "type": { + "typeName": "TreeNodeDesc", + "fields": [], + "documentation": "" + }, + "documentation": "" + } + }, + { + "name": "proofTree/subtree", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "ProofId" + }, + { + "name": "arg1", + "type": "TreeNodeId" + } + ], + "returnType": { + "type": { + "typeName": "TreeNodeDesc", + "fields": [], + "documentation": "" + }, + "documentation": "" + } + }, + { + "name": "goal/print", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "NodeId" + }, + { + "name": "arg1", + "type": "PrintOptions" + } + ], + "returnType": { + "typeName": "NodeTextDesc", + "fields": [ + { + "name": "id", + "type": "NodeTextId" + }, + { + "name": "result", + "type": "STRING" + } + ], + "documentation": "" + } + }, + { + "name": "goal/actions", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "NodeTextId" + }, + { + "name": "arg1", + "type": "INT" + } + ], + "returnType": { + "type": { + "typeName": "TermActionDesc", + "fields": [ + { + "name": "commandId", + "type": "TermActionId" + }, + { + "name": "displayName", + "type": "STRING" + }, + { + "name": "description", + "type": "STRING" + }, + { + "name": "category", + "type": "STRING" + }, + { + "name": "kind", + "type": "TermActionKind" + } + ], + "documentation": "" + }, + "documentation": "" + } + }, + { + "name": "goal/apply_action", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "TermActionId" + } + ], + "returnType": { + "type": { + "typeName": "TermActionDesc", + "fields": [ + { + "name": "commandId", + "type": "TermActionId" + }, + { + "name": "displayName", + "type": "STRING" + }, + { + "name": "description", + "type": "STRING" + }, + { + "name": "category", + "type": "STRING" + }, + { + "name": "kind", + "type": "TermActionKind" + } + ], + "documentation": "" + }, + "documentation": "" + } + }, + { + "name": "goal/free", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "NodeTextId" + } + ] + }, + { + "name": "env/functions", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "EnvironmentId" + } + ], + "returnType": { + "type": { + "typeName": "FunctionDesc", + "fields": [ + { + "name": "name", + "type": "STRING" + }, + { + "name": "sort", + "type": "STRING" + }, + { + "name": "retSort", + "type": "SortDesc" + }, + { + "name": "argSorts", + "type": "List" + }, + { + "name": "rigid", + "type": "BOOL" + }, + { + "name": "unique", + "type": "BOOL" + }, + { + "name": "skolemConstant", + "type": "BOOL" + } + ], + "documentation": "" + }, + "documentation": "" + } + }, + { + "name": "env/sorts", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "EnvironmentId" + } + ], + "returnType": { + "type": { + "typeName": "SortDesc", + "fields": [ + { + "name": "string", + "type": "STRING" + }, + { + "name": "documentation", + "type": "STRING" + }, + { + "name": "extendsSorts", + "type": "List" + }, + { + "name": "anAbstract", + "type": "BOOL" + }, + { + "name": "s", + "type": "STRING" + } + ], + "documentation": "" + }, + "documentation": "" + } + }, + { + "name": "env/contracts", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "EnvironmentId" + } + ], + "returnType": { + "type": { + "typeName": "ContractDesc", + "fields": [ + { + "name": "contractId", + "type": "ContractId" + }, + { + "name": "name", + "type": "STRING" + }, + { + "name": "displayName", + "type": "STRING" + }, + { + "name": "typeName", + "type": "STRING" + }, + { + "name": "htmlText", + "type": "STRING" + }, + { + "name": "plainText", + "type": "STRING" + } + ], + "documentation": "" + }, + "documentation": "" + } + }, + { + "name": "env/openContract", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "ContractId" + } + ], + "returnType": { + "typeName": "ProofId", + "fields": [ + { + "name": "env", + "type": "EnvironmentId" + }, + { + "name": "proofId", + "type": "STRING" + } + ], + "documentation": "" + } + }, + { + "name": "loading/load", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "LoadParams" + } + ], + "returnType": { + "a": { + "typeName": "EnvironmentId", + "fields": [ + { + "name": "envId", + "type": "STRING" + } + ], + "documentation": "" + }, + "b": { + "typeName": "ProofId", + "fields": [ + { + "name": "env", + "type": "EnvironmentId" + }, + { + "name": "proofId", + "type": "STRING" + } + ], + "documentation": "" + }, + "documentation": "" + } + }, + { + "name": "loading/loadKey", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "STRING" + } + ], + "returnType": { + "typeName": "ProofId", + "fields": [ + { + "name": "env", + "type": "EnvironmentId" + }, + { + "name": "proofId", + "type": "STRING" + } + ], + "documentation": "" + } + }, + { + "name": "loading/loadExample", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "STRING" + } + ], + "returnType": { + "typeName": "ProofId", + "fields": [ + { + "name": "env", + "type": "EnvironmentId" + }, + { + "name": "proofId", + "type": "STRING" + } + ], + "documentation": "" + } + }, + { + "name": "loading/loadProblem", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "ProblemDefinition" + } + ], + "returnType": { + "typeName": "ProofId", + "fields": [ + { + "name": "env", + "type": "EnvironmentId" + }, + { + "name": "proofId", + "type": "STRING" + } + ], + "documentation": "" + } + }, + { + "name": "loading/loadTerm", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "STRING" + } + ], + "returnType": { + "typeName": "ProofId", + "fields": [ + { + "name": "env", + "type": "EnvironmentId" + }, + { + "name": "proofId", + "type": "STRING" + } + ], + "documentation": "" + } + }, + { + "name": "client/sayHello", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "STRING" + } + ] + }, + { + "name": "client/logTrace", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "LogTraceParams" + } + ] + }, + { + "name": "client/sm", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "ShowMessageParams" + } + ] + }, + { + "name": "client/userResponse", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "ShowMessageRequestParams" + } + ], + "returnType": { + "typeName": "MessageActionItem", + "fields": [ + { + "name": "title", + "type": "STRING" + } + ], + "documentation": "" + } + }, + { + "name": "client/showDocument", + "documentation": "", + "args": [ + { + "name": "arg0", + "type": "ShowDocumentParams" + } + ], + "returnType": { + "typeName": "ShowDocumentResult", + "fields": [ + { + "name": "success", + "type": "BOOL" + } + ], + "documentation": "" + } + } + ], + "types": [ + { + "typeName": "ExampleDesc", + "fields": [ + { + "name": "name", + "type": "STRING" + }, + { + "name": "description", + "type": "STRING" + } + ], + "documentation": "" + }, + { + "typeName": "ProofScriptCommandDesc", + "fields": [], + "documentation": "" + }, + { + "typeName": "ProofMacroDesc", + "fields": [ + { + "name": "name", + "type": "STRING" + }, + { + "name": "category", + "type": "STRING" + }, + { + "name": "description", + "type": "STRING" + }, + { + "name": "scriptCommandName", + "type": "STRING" + } + ], + "documentation": "" + }, + { + "typeName": "TraceValue", + "values": [ + "Off", + "Message", + "All" + ], + "documentation": "" + }, + { + "typeName": "SetTraceParams", + "fields": [ + { + "name": "value", + "type": "TraceValue" + } + ], + "documentation": "" + }, + { + "typeName": "EnvironmentId", + "fields": [ + { + "name": "envId", + "type": "STRING" + } + ], + "documentation": "" + }, + { + "typeName": "ProofId", + "fields": [ + { + "name": "env", + "type": "EnvironmentId" + }, + { + "name": "proofId", + "type": "STRING" + } + ], + "documentation": "" + }, + { + "typeName": "TreeNodeDesc", + "fields": [], + "documentation": "" + }, + { + "typeName": "TreeNodeId", + "fields": [ + { + "name": "id", + "type": "STRING" + } + ], + "documentation": "" + }, + { + "typeName": "NodeId", + "fields": [ + { + "name": "proofId", + "type": "ProofId" + }, + { + "name": "nodeId", + "type": "INT" + } + ], + "documentation": "" + }, + { + "typeName": "PrintOptions", + "fields": [ + { + "name": "unicode", + "type": "BOOL" + }, + { + "name": "width", + "type": "INT" + }, + { + "name": "indentation", + "type": "INT" + }, + { + "name": "pure", + "type": "BOOL" + }, + { + "name": "termLabels", + "type": "BOOL" + } + ], + "documentation": "" + }, + { + "typeName": "NodeTextId", + "fields": [ + { + "name": "nodeId", + "type": "NodeId" + }, + { + "name": "nodeTextId", + "type": "INT" + } + ], + "documentation": "" + }, + { + "typeName": "NodeTextDesc", + "fields": [ + { + "name": "id", + "type": "NodeTextId" + }, + { + "name": "result", + "type": "STRING" + } + ], + "documentation": "" + }, + { + "typeName": "TermActionId", + "fields": [ + { + "name": "nodeId", + "type": "NodeId" + }, + { + "name": "pio", + "type": "STRING" + }, + { + "name": "id", + "type": "STRING" + } + ], + "documentation": "" + }, + { + "typeName": "TermActionKind", + "values": [ + "BuiltIn", + "Script", + "Macro", + "Taclet" + ], + "documentation": "" + }, + { + "typeName": "TermActionDesc", + "fields": [ + { + "name": "commandId", + "type": "TermActionId" + }, + { + "name": "displayName", + "type": "STRING" + }, + { + "name": "description", + "type": "STRING" + }, + { + "name": "category", + "type": "STRING" + }, + { + "name": "kind", + "type": "TermActionKind" + } + ], + "documentation": "" + }, + { + "typeName": "List", + "fields": [], + "documentation": "" + }, + { + "typeName": "SortDesc", + "fields": [ + { + "name": "string", + "type": "STRING" + }, + { + "name": "documentation", + "type": "STRING" + }, + { + "name": "extendsSorts", + "type": "List" + }, + { + "name": "anAbstract", + "type": "BOOL" + }, + { + "name": "s", + "type": "STRING" + } + ], + "documentation": "" + }, + { + "typeName": "FunctionDesc", + "fields": [ + { + "name": "name", + "type": "STRING" + }, + { + "name": "sort", + "type": "STRING" + }, + { + "name": "retSort", + "type": "SortDesc" + }, + { + "name": "argSorts", + "type": "List" + }, + { + "name": "rigid", + "type": "BOOL" + }, + { + "name": "unique", + "type": "BOOL" + }, + { + "name": "skolemConstant", + "type": "BOOL" + } + ], + "documentation": "" + }, + { + "typeName": "ContractId", + "fields": [ + { + "name": "envId", + "type": "EnvironmentId" + }, + { + "name": "contractId", + "type": "INT" + } + ], + "documentation": "" + }, + { + "typeName": "ContractDesc", + "fields": [ + { + "name": "contractId", + "type": "ContractId" + }, + { + "name": "name", + "type": "STRING" + }, + { + "name": "displayName", + "type": "STRING" + }, + { + "name": "typeName", + "type": "STRING" + }, + { + "name": "htmlText", + "type": "STRING" + }, + { + "name": "plainText", + "type": "STRING" + } + ], + "documentation": "" + }, + { + "typeName": "LoadParams", + "fields": [ + { + "name": "keyFile", + "type": "STRING" + }, + { + "name": "javaFile", + "type": "STRING" + }, + { + "name": "classPath", + "type": "List" + }, + { + "name": "bootClassPath", + "type": "STRING" + }, + { + "name": "includes", + "type": "List" + } + ], + "documentation": "" + }, + { + "typeName": "ProblemDefinition", + "fields": [ + { + "name": "sorts", + "type": "List" + }, + { + "name": "functions", + "type": "List" + }, + { + "name": "predicates", + "type": "List" + }, + { + "name": "antecTerms", + "type": "List" + }, + { + "name": "succTerms", + "type": "List" + } + ], + "documentation": "" + }, + { + "typeName": "LogTraceParams", + "fields": [ + { + "name": "messag", + "type": "STRING" + }, + { + "name": "verbose", + "type": "STRING" + } + ], + "documentation": "" + }, + { + "typeName": "MessageType", + "values": [ + "Unused", + "Error", + "Warning", + "Info", + "Log", + "Debug" + ], + "documentation": "" + }, + { + "typeName": "ShowMessageParams", + "fields": [ + { + "name": "type", + "type": "MessageType" + }, + { + "name": "message", + "type": "STRING" + } + ], + "documentation": "" + }, + { + "typeName": "MessageActionItem[]", + "fields": [], + "documentation": "" + }, + { + "typeName": "ShowMessageRequestParams", + "fields": [ + { + "name": "type", + "type": "MessageType" + }, + { + "name": "message", + "type": "STRING" + }, + { + "name": "actions", + "type": "MessageActionItem[]" + } + ], + "documentation": "" + }, + { + "typeName": "MessageActionItem", + "fields": [ + { + "name": "title", + "type": "STRING" + } + ], + "documentation": "" + }, + { + "typeName": "Range", + "fields": [ + { + "name": "start", + "type": "INT" + }, + { + "name": "end", + "type": "INT" + } + ], + "documentation": "" + }, + { + "typeName": "ShowDocumentParams", + "fields": [ + { + "name": "uri", + "type": "STRING" + }, + { + "name": "external", + "type": "BOOL" + }, + { + "name": "takeFocus", + "type": "BOOL" + }, + { + "name": "selection", + "type": "Range" + } + ], + "documentation": "" + }, + { + "typeName": "ShowDocumentResult", + "fields": [ + { + "name": "success", + "type": "BOOL" + } + ], + "documentation": "" + }, + { + "typeName": "TaskFinishedInfo", + "fields": [], + "documentation": "" + }, + { + "typeName": "TaskStartedInfo", + "fields": [], + "documentation": "" + } + ] +} \ No newline at end of file diff --git a/api.meta.md b/api.meta.md new file mode 100644 index 00000000000..f53d88619f3 --- /dev/null +++ b/api.meta.md @@ -0,0 +1,480 @@ +## Types +### Type: ContractDesc +``` +type ContractDesc { + contractId : ContractId + displayName : STRING + htmlText : STRING + name : STRING + plainText : STRING + typeName : STRING +} +``` + +### Type: ContractId +``` +type ContractId { + contractId : INT + envId : EnvironmentId +} +``` + +### Type: EnvironmentId +``` +type EnvironmentId { + envId : STRING +} +``` + +### Type: ExampleDesc +``` +type ExampleDesc { + description : STRING + name : STRING +} +``` + +### Type: FunctionDesc +``` +type FunctionDesc { + argSorts : List + name : STRING + retSort : SortDesc + rigid : BOOL + skolemConstant : BOOL + sort : STRING + unique : BOOL +} +``` + +### Type: List +``` +type List { + +} +``` + +### Type: LoadParams +``` +type LoadParams { + bootClassPath : STRING + classPath : List + includes : List + javaFile : STRING + keyFile : STRING +} +``` + +### Type: LogTraceParams +``` +type LogTraceParams { + messag : STRING + verbose : STRING +} +``` + +### Type: MessageActionItem +``` +type MessageActionItem { + title : STRING +} +``` + +### Type: MessageActionItem[] +``` +type MessageActionItem[] { + +} +``` + +### Type: MessageType +``` +enum MessageType { Unused, Error, Warning, Info, Log, Debug } +``` + +### Type: NodeId +``` +type NodeId { + nodeId : INT + proofId : ProofId +} +``` + +### Type: NodeTextDesc +``` +type NodeTextDesc { + id : NodeTextId + result : STRING +} +``` + +### Type: NodeTextId +``` +type NodeTextId { + nodeId : NodeId + nodeTextId : INT +} +``` + +### Type: PrintOptions +``` +type PrintOptions { + indentation : INT + pure : BOOL + termLabels : BOOL + unicode : BOOL + width : INT +} +``` + +### Type: ProblemDefinition +``` +type ProblemDefinition { + antecTerms : List + functions : List + predicates : List + sorts : List + succTerms : List +} +``` + +### Type: ProofId +``` +type ProofId { + env : EnvironmentId + proofId : STRING +} +``` + +### Type: ProofMacroDesc +``` +type ProofMacroDesc { + category : STRING + description : STRING + name : STRING + scriptCommandName : STRING +} +``` + +### Type: ProofScriptCommandDesc +``` +type ProofScriptCommandDesc { + +} +``` + +### Type: Range +``` +type Range { + end : INT + start : INT +} +``` + +### Type: SetTraceParams +``` +type SetTraceParams { + value : TraceValue +} +``` + +### Type: ShowDocumentParams +``` +type ShowDocumentParams { + external : BOOL + selection : Range + takeFocus : BOOL + uri : STRING +} +``` + +### Type: ShowDocumentResult +``` +type ShowDocumentResult { + success : BOOL +} +``` + +### Type: ShowMessageParams +``` +type ShowMessageParams { + message : STRING + type : MessageType +} +``` + +### Type: ShowMessageRequestParams +``` +type ShowMessageRequestParams { + actions : MessageActionItem[] + message : STRING + type : MessageType +} +``` + +### Type: SortDesc +``` +type SortDesc { + anAbstract : BOOL + documentation : STRING + extendsSorts : List + s : STRING + string : STRING +} +``` + +### Type: TaskFinishedInfo +``` +type TaskFinishedInfo { + +} +``` + +### Type: TaskStartedInfo +``` +type TaskStartedInfo { + +} +``` + +### Type: TermActionDesc +``` +type TermActionDesc { + category : STRING + commandId : TermActionId + description : STRING + displayName : STRING + kind : TermActionKind +} +``` + +### Type: TermActionId +``` +type TermActionId { + id : STRING + nodeId : NodeId + pio : STRING +} +``` + +### Type: TermActionKind +``` +enum TermActionKind { BuiltIn, Script, Macro, Taclet } +``` + +### Type: TraceValue +``` +enum TraceValue { Off, Message, All } +``` + +### Type: TreeNodeDesc +``` +type TreeNodeDesc { + +} +``` + +### Type: TreeNodeId +``` +type TreeNodeId { + id : STRING +} +``` + +## Endpoints +### client/logTrace (`server ~~> client`) + +``` +Client.client/logTrace( arg0 : LogTraceParams ) **async** +``` + + +### client/sayHello (`server ~~> client`) + +``` +Client.client/sayHello( arg0 : STRING ) **async** +``` + + +### client/showDocument (`server -> client`) + +``` +Client.client/showDocument( arg0 : ShowDocumentParams ) -> ShowDocumentResult +``` + + +### client/sm (`server ~~> client`) + +``` +Client.client/sm( arg0 : ShowMessageParams ) **async** +``` + + +### client/userResponse (`server -> client`) + +``` +Client.client/userResponse( arg0 : ShowMessageRequestParams ) -> MessageActionItem +``` + + +### env/contracts (`client -> server`) + +``` +Server.env/contracts( arg0 : EnvironmentId ) -> ContractDesc[] +``` + + +### env/functions (`client -> server`) + +``` +Server.env/functions( arg0 : EnvironmentId ) -> FunctionDesc[] +``` + + +### env/openContract (`client -> server`) + +``` +Server.env/openContract( arg0 : ContractId ) -> ProofId +``` + + +### env/sorts (`client -> server`) + +``` +Server.env/sorts( arg0 : EnvironmentId ) -> SortDesc[] +``` + + +### examples/list (`client -> server`) + +``` +Server.examples/list( ) -> ExampleDesc[] +``` + + +### goal/actions (`client -> server`) + +``` +Server.goal/actions( arg0 : NodeTextId, arg1 : INT ) -> TermActionDesc[] +``` + + +### goal/apply_action (`client -> server`) + +``` +Server.goal/apply_action( arg0 : TermActionId ) -> TermActionDesc[] +``` + + +### goal/free (`client ~~> server`) + +``` +Server.goal/free( arg0 : NodeTextId ) **async** +``` + + +### goal/print (`client -> server`) + +``` +Server.goal/print( arg0 : NodeId, arg1 : PrintOptions ) -> NodeTextDesc +``` + + +### loading/load (`client -> server`) + +``` +Server.loading/load( arg0 : LoadParams ) -> either +``` + + +### loading/loadExample (`client -> server`) + +``` +Server.loading/loadExample( arg0 : STRING ) -> ProofId +``` + + +### loading/loadKey (`client -> server`) + +``` +Server.loading/loadKey( arg0 : STRING ) -> ProofId +``` + + +### loading/loadProblem (`client -> server`) + +``` +Server.loading/loadProblem( arg0 : ProblemDefinition ) -> ProofId +``` + + +### loading/loadTerm (`client -> server`) + +``` +Server.loading/loadTerm( arg0 : STRING ) -> ProofId +``` + + +### meta/available_macros (`client -> server`) + +``` +Server.meta/available_macros( ) -> ProofMacroDesc[] +``` + + +### meta/available_script_commands (`client -> server`) + +``` +Server.meta/available_script_commands( ) -> ProofScriptCommandDesc[] +``` + + +### meta/version (`client -> server`) + +``` +Server.meta/version( ) -> STRING +``` + + +### proofTree/children (`client -> server`) + +``` +Server.proofTree/children( arg0 : ProofId, arg1 : TreeNodeId ) -> TreeNodeDesc[] +``` + + +### proofTree/root (`client -> server`) + +``` +Server.proofTree/root( arg0 : ProofId ) -> TreeNodeDesc +``` + + +### proofTree/subtree (`client -> server`) + +``` +Server.proofTree/subtree( arg0 : ProofId, arg1 : TreeNodeId ) -> TreeNodeDesc[] +``` + + +### server/exit (`client ~~> server`) + +``` +Server.server/exit( ) **async** +``` + + +### server/setTrace (`client ~~> server`) + +``` +Server.server/setTrace( arg0 : SetTraceParams ) **async** +``` + + +### server/shutdown (`client -> server`) + +``` +Server.server/shutdown( ) -> BOOL +``` + + diff --git a/api.py b/api.py new file mode 100644 index 00000000000..c83050a763e --- /dev/null +++ b/api.py @@ -0,0 +1,374 @@ +import enum +import abc +import typing +class ExampleDesc: + """""" + + name : str + description : str + +class ProofScriptCommandDesc: + """""" + + +class ProofMacroDesc: + """""" + + name : str + category : str + description : str + scriptCommandName : str + +class TraceValue(enum.Enum): + """""" + + Off = None + Message = None + All = None + +class SetTraceParams: + """""" + + value : TraceValue + +class EnvironmentId: + """""" + + envId : str + +class ProofId: + """""" + + env : EnvironmentId + proofId : str + +class TreeNodeDesc: + """""" + + +class TreeNodeId: + """""" + + id : str + +class NodeId: + """""" + + proofId : ProofId + nodeId : int + +class PrintOptions: + """""" + + unicode : bool + width : int + indentation : int + pure : bool + termLabels : bool + +class NodeTextId: + """""" + + nodeId : NodeId + nodeTextId : int + +class NodeTextDesc: + """""" + + id : NodeTextId + result : str + +class TermActionId: + """""" + + nodeId : NodeId + pio : str + id : str + +class TermActionKind(enum.Enum): + """""" + + BuiltIn = None + Script = None + Macro = None + Taclet = None + +class TermActionDesc: + """""" + + commandId : TermActionId + displayName : str + description : str + category : str + kind : TermActionKind + +class List: + """""" + + +class SortDesc: + """""" + + string : str + documentation : str + extendsSorts : List + anAbstract : bool + s : str + +class FunctionDesc: + """""" + + name : str + sort : str + retSort : SortDesc + argSorts : List + rigid : bool + unique : bool + skolemConstant : bool + +class ContractId: + """""" + + envId : EnvironmentId + contractId : int + +class ContractDesc: + """""" + + contractId : ContractId + name : str + displayName : str + typeName : str + htmlText : str + plainText : str + +class LoadParams: + """""" + + keyFile : str + javaFile : str + classPath : List + bootClassPath : str + includes : List + +class ProblemDefinition: + """""" + + sorts : List + functions : List + predicates : List + antecTerms : List + succTerms : List + +class LogTraceParams: + """""" + + messag : str + verbose : str + +class MessageType(enum.Enum): + """""" + + Unused = None + Error = None + Warning = None + Info = None + Log = None + Debug = None + +class ShowMessageParams: + """""" + + type : MessageType + message : str + +class MessageActionItem[]: + """""" + + +class ShowMessageRequestParams: + """""" + + type : MessageType + message : str + actions : MessageActionItem[] + +class MessageActionItem: + """""" + + title : str + +class Range: + """""" + + start : int + end : int + +class ShowDocumentParams: + """""" + + uri : str + external : bool + takeFocus : bool + selection : Range + +class ShowDocumentResult: + """""" + + success : bool + +class TaskFinishedInfo: + """""" + + +class TaskStartedInfo: + """""" + + +class KeyServer(): + def env_contracts(self, arg0 : EnvironmentId) -> typing.List[ContractDesc]: + """""" + + return self.rpc.call_sync("env/contracts", ) + + def env_functions(self, arg0 : EnvironmentId) -> typing.List[FunctionDesc]: + """""" + + return self.rpc.call_sync("env/functions", ) + + def env_openContract(self, arg0 : ContractId) -> ProofId: + """""" + + return self.rpc.call_sync("env/openContract", ) + + def env_sorts(self, arg0 : EnvironmentId) -> typing.List[SortDesc]: + """""" + + return self.rpc.call_sync("env/sorts", ) + + def examples_list(self, ) -> typing.List[ExampleDesc]: + """""" + + return self.rpc.call_sync("examples/list", ) + + def goal_actions(self, arg0 : NodeTextId, arg1 : int) -> typing.List[TermActionDesc]: + """""" + + return self.rpc.call_sync("goal/actions", ) + + def goal_apply_action(self, arg0 : TermActionId) -> typing.List[TermActionDesc]: + """""" + + return self.rpc.call_sync("goal/apply_action", ) + + def goal_free(self, arg0 : NodeTextId): + """""" + + return self.rpc.call_async("goal/free", ) + + def goal_print(self, arg0 : NodeId, arg1 : PrintOptions) -> NodeTextDesc: + """""" + + return self.rpc.call_sync("goal/print", ) + + def loading_load(self, arg0 : LoadParams) -> typing.Union[EnvironmentId, ProofId]: + """""" + + return self.rpc.call_sync("loading/load", ) + + def loading_loadExample(self, arg0 : str) -> ProofId: + """""" + + return self.rpc.call_sync("loading/loadExample", ) + + def loading_loadKey(self, arg0 : str) -> ProofId: + """""" + + return self.rpc.call_sync("loading/loadKey", ) + + def loading_loadProblem(self, arg0 : ProblemDefinition) -> ProofId: + """""" + + return self.rpc.call_sync("loading/loadProblem", ) + + def loading_loadTerm(self, arg0 : str) -> ProofId: + """""" + + return self.rpc.call_sync("loading/loadTerm", ) + + def meta_available_macros(self, ) -> typing.List[ProofMacroDesc]: + """""" + + return self.rpc.call_sync("meta/available_macros", ) + + def meta_available_script_commands(self, ) -> typing.List[ProofScriptCommandDesc]: + """""" + + return self.rpc.call_sync("meta/available_script_commands", ) + + def meta_version(self, ) -> STRING: + """""" + + return self.rpc.call_sync("meta/version", ) + + def proofTree_children(self, arg0 : ProofId, arg1 : TreeNodeId) -> typing.List[TreeNodeDesc]: + """""" + + return self.rpc.call_sync("proofTree/children", ) + + def proofTree_root(self, arg0 : ProofId) -> TreeNodeDesc: + """""" + + return self.rpc.call_sync("proofTree/root", ) + + def proofTree_subtree(self, arg0 : ProofId, arg1 : TreeNodeId) -> typing.List[TreeNodeDesc]: + """""" + + return self.rpc.call_sync("proofTree/subtree", ) + + def server_exit(self, ): + """""" + + return self.rpc.call_async("server/exit", ) + + def server_setTrace(self, arg0 : SetTraceParams): + """""" + + return self.rpc.call_async("server/setTrace", ) + + def server_shutdown(self, ) -> BOOL: + """""" + + return self.rpc.call_sync("server/shutdown", ) + +class Client(abc.AbcMeta): + @abstractmethod + def client_logTrace(self, arg0 : LogTraceParams): + """""" + + pass + + @abstractmethod + def client_sayHello(self, arg0 : str): + """""" + + pass + + @abstractmethod + def client_showDocument(self, arg0 : ShowDocumentParams) -> ShowDocumentResult: + """""" + + pass + + @abstractmethod + def client_sm(self, arg0 : ShowMessageParams): + """""" + + pass + + @abstractmethod + def client_userResponse(self, arg0 : ShowMessageRequestParams) -> MessageActionItem: + """""" + + pass + diff --git a/keyext.api.doc/build.gradle b/keyext.api.doc/build.gradle index f7aadd6467c..84a1789e01f 100644 --- a/keyext.api.doc/build.gradle +++ b/keyext.api.doc/build.gradle @@ -1,4 +1,5 @@ dependencies { + implementation(project(":keyext.api")) implementation("com.github.javaparser:javaparser-core:3.25.5") implementation("com.github.javaparser:javaparser-symbol-solver-core:3.25.5") } \ No newline at end of file diff --git a/keyext.api.doc/src/main/java/DocGen.java b/keyext.api.doc/src/main/java/DocGen.java new file mode 100644 index 00000000000..8ab51393898 --- /dev/null +++ b/keyext.api.doc/src/main/java/DocGen.java @@ -0,0 +1,190 @@ +/* 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 */ +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Comparator; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +public class DocGen implements Supplier { + private final Metamodel.KeyApi metamodel; + private PrintWriter out; + private final StringWriter target = new StringWriter(); + + public DocGen(Metamodel.KeyApi metamodel) { + this.metamodel = metamodel; + } + + @Override + public String get() { + try (var out = new PrintWriter(target)) { + this.out = out; + printHeader(); + + out.format("## Types%n"); + metamodel.types() + .stream().sorted(Comparator.comparing(Metamodel.Type::name)) + .forEach(this::printType); + + out.format("## Endpoints%n"); + metamodel.endpoints() + .stream().sorted(Comparator.comparing(Metamodel.Endpoint::name)) + .forEach(this::endpoints); + printFooter(); + } + return target.toString(); + } + + private void printFooter() { + + } + + private void printHeader() { + + + } + + private void endpoints(Metamodel.Endpoint endpoint) { + // out.format("## Group: %s%n%n", getJsonSegment(typeDeclaration)); + // out.println(getJavaDoc(typeDeclaration)); + // out.println("\n"); + + var direction = ""; + if (endpoint instanceof Metamodel.ServerRequest sr) + direction = "client -> server"; + else if (endpoint instanceof Metamodel.ClientRequest sr) + direction = "server -> client"; + else if (endpoint instanceof Metamodel.ServerNotification sr) + direction = "client ~~> server"; + else if (endpoint instanceof Metamodel.ClientNotification sr) + direction = "server ~~> client"; + + out.format("### %s (`%s`) %n%n", endpoint.name(), direction); + out.format("```%n"); + var args = endpoint.args(); + final var a = args.stream() + .map(it -> "%s : %s".formatted(it.name(), it.type())) + .collect(Collectors.joining(", ")); + if (endpoint instanceof Metamodel.ServerRequest sr) { + out.format("Server.%s( %s ) -> %s%n", endpoint.name(), a, sr.returnType().name()); + } else if (endpoint instanceof Metamodel.ClientRequest sr) + out.format("Client.%s( %s ) -> %s%n", endpoint.name(), a, sr.returnType().name()); + else if (endpoint instanceof Metamodel.ServerNotification sr) + out.format("Server.%s( %s ) **async**%n", endpoint.name(), a); + else if (endpoint instanceof Metamodel.ClientNotification sr) + out.format("Client.%s( %s ) **async**%n", endpoint.name(), a); + out.format("```%n"); + + out.println(endpoint.documentation()); + out.println(); + } + + private void printType(Metamodel.Type type) { + out.format("### Type: %s%n", type.name()); + if (type instanceof Metamodel.ObjectType ot) { + out.format(""" + ``` + type %s { + %s + } + ``` + """.formatted(type.name(), + ot.fields().stream().sorted(Comparator.comparing(Metamodel.Field::name)) + .map(it -> "\t%s : %s".formatted(it.name(), it.type())) + .collect(Collectors.joining("\n")))); + } + + if (type instanceof Metamodel.EnumType et) { + out.format(""" + ``` + enum %s { %s } + ``` + """.formatted(type.name(), String.join(", ", et.values()))); + out.format(type.documentation()); + } + out.format(type.documentation()); + out.println(); + } + + /* + * private static String getCallName (MethodDeclaration m, TypeDeclaration < ?>td){ + * var annotationExpr = m.getAnnotationByName("JsonNotification").or( + * () -> m.getAnnotationByName("JsonRequest")) + * .orElseThrow(); + * + * var segment = getJsonSegment(td) + "/"; + * String name = ""; + * + * if (annotationExpr.isMarkerAnnotationExpr()) { + * name = m.getNameAsString(); + * } else if (annotationExpr.isSingleMemberAnnotationExpr()) { + * var sma = annotationExpr.asSingleMemberAnnotationExpr(); + * name = sma.getMemberValue().asLiteralStringValueExpr().getValue(); + * } else { + * var ne = annotationExpr.asNormalAnnotationExpr(); + * for (MemberValuePair pair : ne.getPairs()) { + * switch (pair.getName().asString()) { + * case "value": + * name = pair.getValue().asLiteralStringValueExpr().getValue(); + * break; + * case "useSegment": + * if (!pair.getValue().asBooleanLiteralExpr().getValue()) { + * segment = ""; + * } + * } + * } + * } + * return segment + name; + * } + * + * @Nonnull + * private static String getJavaDoc (NodeWithJavadoc < ? > typeDeclaration){ + * if (typeDeclaration.getJavadoc().isPresent()) { + * final var javadoc = typeDeclaration.getJavadoc().get(); + * return javadoc.getDescription().toText() + * + "\n\n" + * + javadoc.getBlockTags().stream().map(it -> "* " + it.toText()) + * .collect(Collectors.joining("\n")); + * } + * return ""; + * } + * + * private static String type (MethodDeclaration method){ + * if (method.getAnnotationByName("JsonNotification").isPresent()) + * return "notification"; + * if (method.getAnnotationByName("JsonRequest").isPresent()) + * return "request"; + * return ""; + * } + * + * private static boolean isExported (MethodDeclaration method){ + * return method.getAnnotationByName("JsonNotification").isPresent() + * || method.getAnnotationByName("JsonRequest").isPresent(); + * } + * + * private void printHeader () { + * out.format("# KeY-API%n%n"); + * } + * + * private void printFooter () { + * } + * + * + * /* + * private static boolean hasJsonSegment(TypeDeclaration it) { + * return it.getAnnotationByName("JsonSegment").isPresent(); + * } + * + * private static String getJsonSegment(TypeDeclaration it) { + * var ae = it.getAnnotationByName("JsonSegment").get(); + * return ae.asSingleMemberAnnotationExpr().getMemberValue() + * .asLiteralStringValueExpr().getValue(); + * } + */ + +} diff --git a/keyext.api.doc/src/main/java/ExtractMetaData.java b/keyext.api.doc/src/main/java/ExtractMetaData.java index 07106c02d9f..16e3ffd2d56 100644 --- a/keyext.api.doc/src/main/java/ExtractMetaData.java +++ b/keyext.api.doc/src/main/java/ExtractMetaData.java @@ -1,27 +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 */ -import java.io.FileWriter; +import java.io.File; import java.io.IOException; import java.io.PrintWriter; +import java.lang.reflect.*; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; +import java.util.*; +import java.util.concurrent.CompletableFuture; -import com.github.javaparser.ParseResult; +import de.uka.ilkd.key.proof.Proof; + +import com.github.javaparser.JavaParser; import com.github.javaparser.ParserConfiguration; -import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.body.TypeDeclaration; -import com.github.javaparser.ast.expr.MemberValuePair; +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.comments.Comment; import com.github.javaparser.ast.nodeTypes.NodeWithJavadoc; -import com.github.javaparser.resolution.SymbolResolver; -import com.github.javaparser.resolution.TypeSolver; -import com.github.javaparser.symbolsolver.JavaSymbolSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.TypeSolverBuilder; -import com.github.javaparser.utils.SourceRoot; +import com.google.gson.*; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; +import org.keyproject.key.api.remoteapi.KeyApi; +import org.keyproject.key.api.remoteclient.ClientApi; +import sun.misc.Unsafe; /** * @author Alexander Weigl @@ -30,138 +34,270 @@ public class ExtractMetaData { private static PrintWriter out; - public static void main(String[] args) throws IOException { - System.out.println("XXX" + Arrays.toString(args)); - ParserConfiguration config = new ParserConfiguration(); - config.setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17_PREVIEW); - /* - * config.setAttributeComments(true); - * config.setLexicalPreservationEnabled(false); - * config.setStoreTokens(false); - * config.setIgnoreAnnotationsWhenAttributingComments(true); - * config.setDoNotAssignCommentsPrecedingEmptyLines(true); - */ - // var root = Paths.get(args[0]); - TypeSolver typeSolver = new TypeSolverBuilder() - .withSourceCode("keyext.api/src/main/java") - .withSourceCode("key.core/src/main/java") - .withSourceCode("key.util/src/main/java") - .withCurrentJRE() - .build(); - SymbolResolver symbolResolver = new JavaSymbolSolver(typeSolver); - config.setSymbolResolver(symbolResolver); - var source = new SourceRoot(Paths.get("keyext.api", "src", "main", "java"), config); - var cu = source.tryToParse(); - var jsonSegment = Comparator.comparing(ExtractMetaData::getJsonSegment); - var segments = cu.stream().filter(ParseResult::isSuccessful) - .filter(it -> it.getResult().get().getPrimaryType().isPresent()) - .map(it -> it.getResult().get().getPrimaryType().get()) - .filter(ExtractMetaData::hasJsonSegment) - .sorted(jsonSegment) - .toList(); + private static final List endpoints = new LinkedList<>(); + private static final List types = new LinkedList<>(); + private static final Metamodel.KeyApi keyApi = new Metamodel.KeyApi(endpoints, types); - try (var out = new PrintWriter(new FileWriter("doc.md"))) { - ExtractMetaData.out = out; - printHeader(segments); - segments.forEach(ExtractMetaData::printDocumentation); - printFooter(segments); + public static void main(String[] args) { + for (Method method : KeyApi.class.getMethods()) { + addServerEndpoint(method); } - // "org.keyproject.key.api.remoteapi.KeyApi"; + for (Method method : ClientApi.class.getMethods()) { + addClientEndpoint(method); + } + + try { + Files.writeString(Paths.get("api.meta.json"), getGson().toJson(keyApi)); + } catch (IOException e) { + e.printStackTrace(); + } + + try { + var n = new DocGen(keyApi); + Files.writeString(Paths.get("api.meta.md"), n.get()); + } catch (IOException e) { + e.printStackTrace(); + } + try { + var n = new PyGen(keyApi); + Files.writeString(Paths.get("api.py"), n.get()); + } catch (IOException e) { + e.printStackTrace(); + } } - private static void printDocumentation(TypeDeclaration typeDeclaration) { - out.format("## Group: %s%n%n", getJsonSegment(typeDeclaration)); - out.println(getJavaDoc(typeDeclaration)); - out.println("\n"); + private static Gson getGson() { + return new GsonBuilder() + .setPrettyPrinting() + .registerTypeAdapter(Type.class, new JsonSerializer() { + @Override + public JsonElement serialize(Metamodel.Type src, Type typeOfSrc, + JsonSerializationContext context) { + JsonObject json = (JsonObject) context.serialize(src); + json.addProperty("kind", src.kind()); + return json; + } + }) + .create(); + } + + private static void addServerEndpoint(Method method) { + var jsonSegment = method.getDeclaringClass().getAnnotation(JsonSegment.class); + if (jsonSegment == null) + return; + var segment = jsonSegment.value(); + + var req = method.getAnnotation(JsonRequest.class); + var resp = method.getAnnotation(JsonNotification.class); - for (MethodDeclaration method : typeDeclaration.getMethods()) { - if (!isExported(method)) - continue; + var args = translate(method.getParameters()); - String callName = getCallName(method, typeDeclaration); + if (req != null) { + var mn = callMethodName(method.getName(), segment, req.value(), req.useSegment()); - out.format("### %s (type: %s) %n%n", callName, type(method)); + if (method.getReturnType() == Void.class) { + System.err.println("Found void as return type for a request! " + method); + return; + } - out.println(getJavaDoc(method)); + var retType = getOrFindType(method.getGenericReturnType()); + Objects.requireNonNull(retType, "No retType found " + method.getGenericReturnType()); + var documentation = findDocumentation(method); + var mm = new Metamodel.ServerRequest(mn, documentation, args, retType); + endpoints.add(mm); + return; + } - out.println(); + if (resp != null) { + var mn = callMethodName(method.getName(), segment, resp.value(), resp.useSegment()); + var documentation = findDocumentation(method); + var mm = new Metamodel.ServerNotification(mn, documentation, args); + endpoints.add(mm); + return; } + + throw new IllegalStateException(); } - private static String getCallName(MethodDeclaration m, TypeDeclaration td) { - var annotationExpr = m.getAnnotationByName("JsonNotification").or( - () -> m.getAnnotationByName("JsonRequest")) - .orElseThrow(); + private static String findDocumentation(Method method) { + // TODO get compilation, get type, find method, getJavaDocComment() + return ""; + } - var segment = getJsonSegment(td) + "/"; - String name = ""; + private static List translate(Parameter[] parameters) { + return Arrays.stream(parameters).map(ExtractMetaData::translate).toList(); + } - if (annotationExpr.isMarkerAnnotationExpr()) { - name = m.getNameAsString(); - } else if (annotationExpr.isSingleMemberAnnotationExpr()) { - var sma = annotationExpr.asSingleMemberAnnotationExpr(); - name = sma.getMemberValue().asLiteralStringValueExpr().getValue(); - } else { - var ne = annotationExpr.asNormalAnnotationExpr(); - for (MemberValuePair pair : ne.getPairs()) { - switch (pair.getName().asString()) { - case "value": - name = pair.getValue().asLiteralStringValueExpr().getValue(); - break; - case "useSegment": - if (!pair.getValue().asBooleanLiteralExpr().getValue()) { - segment = ""; - } - } - } - } - return segment + name; + private static Metamodel.Argument translate(Parameter parameter) { + var type = getOrFindType(parameter.getType()).name(); + return new Metamodel.Argument(parameter.getName(), type); } + private static Metamodel.Type getOrFindType(Class type) { + System.out.println(type); + if (type == CompletableFuture.class) { + return getOrFindType(type.getTypeParameters()[0].getClass()); + } - @Nonnull - private static String getJavaDoc(NodeWithJavadoc typeDeclaration) { - if (typeDeclaration.getJavadoc().isPresent()) { - final var javadoc = typeDeclaration.getJavadoc().get(); - return javadoc.getDescription().toText() - + "\n\n" - + javadoc.getBlockTags().stream().map(it -> "* " + it.toText()) - .collect(Collectors.joining("\n")); + if (type == Unsafe.class || type == Class.class || type == Constructor.class + || type == Proof.class) { + throw new IllegalStateException("Forbidden class reached!"); } - return ""; + + if (type == String.class) + return Metamodel.BuiltinType.STRING; + if (type == Integer.class) + return Metamodel.BuiltinType.INT; + if (type == Double.class) + return Metamodel.BuiltinType.DOUBLE; + if (type == Long.class) + return Metamodel.BuiltinType.LONG; + if (type == Character.class) + return Metamodel.BuiltinType.LONG; + if (type == File.class) + return Metamodel.BuiltinType.STRING; + if (type == Boolean.class) + return Metamodel.BuiltinType.BOOL; + if (type == Boolean.TYPE) + return Metamodel.BuiltinType.BOOL; + + if (type == Integer.TYPE) + return Metamodel.BuiltinType.INT; + if (type == Double.TYPE) + return Metamodel.BuiltinType.DOUBLE; + if (type == Long.TYPE) + return Metamodel.BuiltinType.LONG; + if (type == Character.TYPE) + return Metamodel.BuiltinType.LONG; + + System.out.println(type); + var t = types.stream().filter(it -> it.name().equals(type.getSimpleName())).findFirst(); + if (t.isPresent()) + return t.get(); + var a = createType(type); + types.add(a); + return a; } - private static String type(MethodDeclaration method) { - if (method.getAnnotationByName("JsonNotification").isPresent()) - return "notification"; - if (method.getAnnotationByName("JsonRequest").isPresent()) - return "request"; - return ""; + private static Metamodel.Type createType(Class type) { + final var documentation = findDocumentation(type); + if (type.isEnum()) + return new Metamodel.EnumType(type.getSimpleName(), + Arrays.stream(type.getEnumConstants()).map(Object::toString).toList(), + documentation); + + + var obj = new Metamodel.ObjectType(type.getSimpleName(), new ArrayList<>(), documentation); + final var list = Arrays.stream(type.getDeclaredFields()) + .map(it -> new Metamodel.Field(it.getName(), getOrFindType(it.getType()).name())) + .toList(); + obj.fields().addAll(list); + return obj; } - private static boolean isExported(MethodDeclaration method) { - return method.getAnnotationByName("JsonNotification").isPresent() - || method.getAnnotationByName("JsonRequest").isPresent(); + private static String findDocumentation(Class type) { + var parser = initJavaParser(); + var fileName = findFileForType(type); + + if (Files.exists(fileName)) { + try { + return parser.parse(fileName).getResult().flatMap(CompilationUnit::getPrimaryType) + .flatMap(NodeWithJavadoc::getJavadocComment) + .map(Comment::getContent).orElse(""); + } catch (IOException e) { + e.printStackTrace(); + return ""; + } + } else + return ""; } - private static void printHeader(List> segments) { - out.format("# KeY-API%n%n"); + private static Path findFileForType(Class type) { + final var folderString = type.getPackageName().replaceAll("\\.", "/"); + var folder = Paths.get("keyext.api", "src", "main", "java", folderString); + final Class declaringClass = type.getDeclaringClass(); + var fileName = (declaringClass != null ? declaringClass : type).getSimpleName() + ".java"; + var file = folder.resolve(fileName); + return file; } - private static void printFooter(List> segments) { + private static void addClientEndpoint(Method method) { + var jsonSegment = method.getDeclaringClass().getAnnotation(JsonSegment.class); + var segment = jsonSegment.value(); + var req = method.getAnnotation(JsonRequest.class); + var resp = method.getAnnotation(JsonNotification.class); + + var args = translate(method.getParameters()); + + if (req != null) { + var retType = getOrFindType(method.getGenericReturnType()); + Objects.requireNonNull(retType); + var mn = callMethodName(method.getName(), segment, req.value(), req.useSegment()); + var documentation = findDocumentation(method); + var mm = new Metamodel.ClientRequest(mn, documentation, args, retType); + endpoints.add(mm); + return; + } + + if (resp != null) { + var mn = callMethodName(method.getName(), segment, resp.value(), resp.useSegment()); + var documentation = findDocumentation(method); + var mm = new Metamodel.ClientNotification(mn, documentation, args); + endpoints.add(mm); + } } + private static String callMethodName(String method, String segment, String userValue, + boolean useSegment) { + if (!useSegment) { + if (userValue == null || userValue.isBlank()) { + return method; + } else { + return userValue; + } + } else { + if (userValue == null || userValue.isBlank()) { + return segment + "/" + method; + } else { + return segment + "/" + userValue; + } + } + } + + private static Metamodel.Type getOrFindType(Type genericReturnType) { + if (genericReturnType instanceof Class c) + return getOrFindType(c); + if (genericReturnType instanceof ParameterizedType pt) { + if (Objects.equals(pt.getRawType().getTypeName(), + CompletableFuture.class.getTypeName())) { + return getOrFindType(pt.getActualTypeArguments()[0]); + } + if (Objects.equals(pt.getRawType().getTypeName(), List.class.getTypeName())) { + var base = getOrFindType(pt.getActualTypeArguments()[0]); + return new Metamodel.ListType(base, ""); + } - private static boolean hasJsonSegment(TypeDeclaration it) { - return it.getAnnotationByName("JsonSegment").isPresent(); + if (Objects.equals(pt.getRawType().getTypeName(), Either.class.getTypeName())) { + var base1 = getOrFindType(pt.getActualTypeArguments()[0]); + var base2 = getOrFindType(pt.getActualTypeArguments()[1]); + return new Metamodel.EitherType(base1, base2, ""); + } + } + return null; } - private static String getJsonSegment(TypeDeclaration it) { - var ae = it.getAnnotationByName("JsonSegment").get(); - return ae.asSingleMemberAnnotationExpr().getMemberValue() - .asLiteralStringValueExpr().getValue(); + static JavaParser initJavaParser() { + ParserConfiguration config = new ParserConfiguration(); + config.setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17_PREVIEW); + config.setAttributeComments(true); + config.setLexicalPreservationEnabled(false); + config.setStoreTokens(false); + config.setIgnoreAnnotationsWhenAttributingComments(true); + config.setDoNotAssignCommentsPrecedingEmptyLines(true); + return new JavaParser(config); } + } diff --git a/keyext.api.doc/src/main/java/Metamodel.java b/keyext.api.doc/src/main/java/Metamodel.java new file mode 100644 index 00000000000..6d6ed916c6e --- /dev/null +++ b/keyext.api.doc/src/main/java/Metamodel.java @@ -0,0 +1,100 @@ +/* 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 */ +import java.util.List; + +import org.jspecify.annotations.NullMarked; + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +@NullMarked +public class Metamodel { + public record KeyApi( + List endpoints, + List types) { + } + + sealed interface Endpoint { + String name(); + + String documentation(); + + default String kind() { + return getClass().getSimpleName(); + } + + List args(); + } + + public record Argument(String name, String type) { + } + + record ServerRequest(String name, String documentation, List args, Type returnType) + implements Endpoint { + } + + record ServerNotification(String name, String documentation, List args) + implements Endpoint { + } + + record ClientRequest(String name, String documentation, List args, Type returnType) + implements Endpoint { + } + + record ClientNotification(String name, String documentation, List args) + implements Endpoint { + } + + record Field(String name, /* Type */ String type) { + } + + sealed interface Type { + default String kind() { + return getClass().getSimpleName(); + } + + String documentation(); + + String name(); + } + + enum BuiltinType implements Type { + INT, LONG, STRING, BOOL, DOUBLE; + + @Override + public String documentation() { + return "built-in data type"; + } + } + + record ListType(Type type, String documentation) implements Type { + @Override + public String name() { + return type().name() + "[]"; + } + } + + record ObjectType(String typeName, List fields, String documentation) implements Type { + @Override + public String name() { + return typeName; + } + } + + public record EitherType(Type a, Type b, String documentation) implements Type { + @Override + public String name() { + return "either"; + } + } + + public record EnumType(String typeName, List values, String documentation) + implements Type { + @Override + public String name() { + return typeName; + } + } +} diff --git a/keyext.api.doc/src/main/java/PyGen.java b/keyext.api.doc/src/main/java/PyGen.java new file mode 100644 index 00000000000..02cec8af7a6 --- /dev/null +++ b/keyext.api.doc/src/main/java/PyGen.java @@ -0,0 +1,145 @@ +/* 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 */ +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Comparator; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +public class PyGen implements Supplier { + private final Metamodel.KeyApi metamodel; + private PrintWriter out; + private final StringWriter target = new StringWriter(); + + public PyGen(Metamodel.KeyApi metamodel) { + this.metamodel = metamodel; + } + + @Override + public String get() { + try (var out = new PrintWriter(target)) { + this.out = out; + + out.format(""" + import enum + import abc + import typing + from abc import abstractmethod + """); + + metamodel.types().forEach(this::printType); + server( + metamodel.endpoints() + .stream() + .filter(it -> it instanceof Metamodel.ServerRequest + || it instanceof Metamodel.ServerNotification) + .sorted(Comparator.comparing(Metamodel.Endpoint::name))); + + client( + metamodel.endpoints() + .stream() + .filter(it -> it instanceof Metamodel.ClientRequest + || it instanceof Metamodel.ClientNotification) + .sorted(Comparator.comparing(Metamodel.Endpoint::name))); + + } + return target.toString(); + } + + private void client(Stream sorted) { + out.format("class Client(abc.AbcMeta):%n"); + sorted.forEach(this::clientEndpoint); + } + + private void clientEndpoint(Metamodel.Endpoint endpoint) { + var args = endpoint.args().stream() + .map(it -> "%s : %s".formatted(it.name(), asPython(it.type()))) + .collect(Collectors.joining(", ")); + out.format(" @abstractmethod%n"); + if (endpoint instanceof Metamodel.ClientRequest sr) { + out.format(" def %s(self, %s) -> %s:%n", endpoint.name().replace("/", "_"), args, + asPython(sr.returnType())); + } else { + out.format(" def %s(self, %s):%n", endpoint.name().replace("/", "_"), args); + } + out.format(" \"\"\"%s\"\"\"%n%n", endpoint.documentation()); + out.format(" pass".formatted(endpoint.name(), "")); + out.println(); + out.println(); + } + + private void server(Stream sorted) { + out.format("class KeyServer():%n"); + sorted.forEach(this::serverEndpoint); + } + + private void serverEndpoint(Metamodel.Endpoint endpoint) { + var args = endpoint.args().stream() + .map(it -> "%s : %s".formatted(it.name(), asPython(it.type()))) + .collect(Collectors.joining(", ")); + if (endpoint instanceof Metamodel.ServerRequest sr) { + out.format(" def %s(self, %s) -> %s:%n", endpoint.name().replace("/", "_"), args, + asPython(sr.returnType())); + out.format(" \"\"\"%s\"\"\"%n%n", sr.documentation()); + out.format( + " return self.rpc.call_sync(\"%s\", %s)".formatted(endpoint.name(), "")); + } else { + out.format(" def %s(self, %s):%n", endpoint.name().replace("/", "_"), args); + out.format(" \"\"\"%s\"\"\"%n%n", endpoint.documentation()); + out.format( + " return self.rpc.call_async(\"%s\", %s)".formatted(endpoint.name(), "")); + } + out.println(); + out.println(); + } + + private void printType(Metamodel.Type type) { + if (type instanceof Metamodel.ObjectType ot) { + out.format("class %s:%n".formatted(type.name())); + out.format(" \"\"\"%s\"\"\"%n%n", type.documentation()); + ot.fields().forEach( + it -> out.format(" %s : %s%n".formatted(it.name(), asPython(it.type())))); + } else if (type instanceof Metamodel.EnumType et) { + out.format("class %s(enum.Enum):%n".formatted(type.name())); + out.format(" \"\"\"%s\"\"\"%n%n", type.documentation()); + et.values().forEach(it -> out.format(" %s = None%n".formatted(it))); + } + out.println(); + } + + private String asPython(String typeName) { + return switch (typeName) { + case "INT" -> "int"; + case "LONG" -> "int"; + case "STRING" -> "str"; + case "BOOL" -> "bool"; + case "DOUBLE" -> "float"; + default -> { + var t = findType(typeName); + yield asPython(t); + } + }; + } + + private String asPython(Metamodel.Type t) { + if (t instanceof Metamodel.ListType lt) { + return "typing.List[" + asPython(lt.type()) + "]"; + } + + if (t instanceof Metamodel.EitherType lt) { + return "typing.Union[" + asPython(lt.a()) + ", " + asPython(lt.b()) + "]"; + } + return t.name(); + } + + private Metamodel.Type findType(String typeName) { + return this.metamodel.types().stream().filter(it -> it.name().equals(typeName)).findFirst() + .orElseThrow(() -> new RuntimeException("Could not find type: " + typeName)); + } +} diff --git a/keyext.api/build.gradle b/keyext.api/build.gradle index 0ddb34926e0..d2fdca4e9be 100644 --- a/keyext.api/build.gradle +++ b/keyext.api/build.gradle @@ -10,10 +10,10 @@ plugins { description "Verification server interface via JSON-RPC" dependencies { - implementation(project(":key.core")) - implementation(project(":key.ui")) + api(project(":key.core")) + api(project(":key.ui")) - implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.21.1") + api("org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.21.1") implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.websocket.jakarta:0.21.1") implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.websocket.jakarta:0.21.1") implementation("org.eclipse.jetty.websocket:websocket-javax-server:10.0.16") diff --git a/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java b/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java index c94607260fa..fc165efa767 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java @@ -16,7 +16,6 @@ import de.uka.ilkd.key.control.AbstractUserInterfaceControl; import de.uka.ilkd.key.control.DefaultUserInterfaceControl; import de.uka.ilkd.key.control.KeYEnvironment; -import de.uka.ilkd.key.gui.Example; import de.uka.ilkd.key.gui.ExampleChooser; import de.uka.ilkd.key.macros.ProofMacroFacade; import de.uka.ilkd.key.macros.ProofMacroFinishedInfo; @@ -47,6 +46,7 @@ import org.keyproject.key.api.data.*; import org.keyproject.key.api.data.KeyIdentifications.*; import org.keyproject.key.api.internal.NodeText; +import org.keyproject.key.api.remoteapi.ExampleDesc; import org.keyproject.key.api.remoteapi.KeyApi; import org.keyproject.key.api.remoteapi.PrintOptions; import org.keyproject.key.api.remoteclient.ClientApi; @@ -78,14 +78,15 @@ public KeyApiImpl() { @Override @JsonRequest - public CompletableFuture> examples() { + public CompletableFuture> examples() { return CompletableFutures - .computeAsync((c) -> ExampleChooser.listExamples(ExampleChooser.lookForExamples())); + .computeAsync((c) -> ExampleChooser.listExamples(ExampleChooser.lookForExamples()) + .stream().map(it -> ExampleDesc.from(it)).toList()); } @Override - public CompletableFuture shutdown() { - return CompletableFuture.completedFuture(null); + public CompletableFuture shutdown() { + return CompletableFuture.completedFuture(true); } @Override @@ -215,10 +216,12 @@ public CompletableFuture tree(ProofId proofId) { } private NodeDesc asNodeDescRecursive(ProofId proofId, Node root) { + final List list = + root.childrenStream().map(it -> asNodeDescRecursive(proofId, it)).toList(); return new NodeDesc(new NodeId(proofId, root.serialNr()), root.getNodeInfo().getBranchLabel(), root.getNodeInfo().getScriptRuleApplication(), - root.childrenStream().map(it -> asNodeDescRecursive(proofId, it)).toList()); + list); } @Override diff --git a/keyext.api/src/main/java/org/keyproject/key/api/NodeTextId.java b/keyext.api/src/main/java/org/keyproject/key/api/NodeTextId.java index 7e956689c08..2f948413e72 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/NodeTextId.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/NodeTextId.java @@ -2,12 +2,3 @@ * KeY is licensed under the GNU General Public License Version 2 * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api; - -import org.keyproject.key.api.data.KeyIdentifications.NodeId; - -/** - * @author Alexander Weigl - * @version 1 (29.10.23) - */ -public record NodeTextId(NodeId nodeId, int nodeTextId) { -} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java b/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java index 3ac97bdc3d5..fe0e9f2f182 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java @@ -21,7 +21,9 @@ import org.key_project.util.collection.ImmutableSLList; import org.checkerframework.checker.nullness.qual.NonNull; +import org.jspecify.annotations.NonNull; import org.keyproject.key.api.data.KeyIdentifications; +import org.keyproject.key.api.data.KeyIdentifications.NodeTextId; import org.keyproject.key.api.data.TermActionDesc; import org.keyproject.key.api.data.TermActionKind; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/KeyIdentifications.java b/keyext.api/src/main/java/org/keyproject/key/api/data/KeyIdentifications.java index 83fb93bd517..c474050a00a 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/KeyIdentifications.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/KeyIdentifications.java @@ -13,6 +13,7 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import org.checkerframework.checker.nullness.qual.NonNull; +import org.jspecify.annotations.NonNull; import org.keyproject.key.api.NodeTextId; import org.keyproject.key.api.internal.NodeText; @@ -118,11 +119,12 @@ public record ProofId(EnvironmentId env, String proofId) { /** * @author Alexander Weigl - * @version 1 (13.10.23) + * @version 1 (29.10.23) */ - public record PrintId(String id) { + public record NodeTextId(NodeId nodeId, int nodeTextId) { } + /** * @author Alexander Weigl * @version 1 (13.10.23) diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java index 44941cb8dfd..bba069cc757 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java @@ -6,12 +6,14 @@ import java.util.List; import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * @author Alexander Weigl * @version 1 (13.10.23) */ -public record NodeDesc(KeyIdentifications.NodeId nodeid, String branchLabel, +public record NodeDesc(KeyIdentifications.NodeId nodeid, + String branchLabel, boolean scriptRuleApplication, @Nullable List children) { public NodeDesc(KeyIdentifications.ProofId proofId, int serialNr, String branchLabel, diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/NodeTextDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/NodeTextDesc.java new file mode 100644 index 00000000000..4bd7d186a58 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/NodeTextDesc.java @@ -0,0 +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 org.keyproject.key.api.data; + + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +public record NodeTextDesc(KeyIdentifications.NodeTextId id, String result) { +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/ProblemDefinition.java b/keyext.api/src/main/java/org/keyproject/key/api/data/ProblemDefinition.java index 427e4751646..a6b6fb2765b 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/ProblemDefinition.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/ProblemDefinition.java @@ -3,9 +3,11 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.data; + import java.util.List; import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * @author Alexander Weigl diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ExampleApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ExampleApi.java index 6da9c2f556b..5f19f881e9d 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ExampleApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ExampleApi.java @@ -6,13 +6,11 @@ import java.util.List; import java.util.concurrent.CompletableFuture; -import de.uka.ilkd.key.gui.Example; - import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; @JsonSegment("examples") public interface ExampleApi { @JsonRequest("list") - CompletableFuture> examples(); + CompletableFuture> examples(); } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ExampleDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ExampleDesc.java new file mode 100644 index 00000000000..e2f00758a72 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ExampleDesc.java @@ -0,0 +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 org.keyproject.key.api.remoteapi; + +import de.uka.ilkd.key.gui.Example; + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +public record ExampleDesc(String name, String description) { + public static ExampleDesc from(Example example) { + return new ExampleDesc(example.getName(), example.getDescription()); + } +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java index e072f01cf82..be120b488de 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java @@ -6,10 +6,9 @@ import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; -import org.keyproject.key.api.NodeTextDesc; -import org.keyproject.key.api.NodeTextId; import org.keyproject.key.api.data.*; import org.keyproject.key.api.data.KeyIdentifications.*; +import org.keyproject.key.api.data.NodeTextDesc; /** * @author Alexander Weigl diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ServerManagement.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ServerManagement.java index b593d0ed66b..bcbff9a04ff 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ServerManagement.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ServerManagement.java @@ -38,7 +38,7 @@ public interface ServerManagement { * error: code and message set in case an exception happens during shutdown request. */ @JsonRequest - CompletableFuture shutdown(); + CompletableFuture shutdown(); /** * Exit Notification (:arrow_right:) diff --git a/keyext.api/src/main/python/keyapi/rpc.py b/keyext.api/src/main/python/keyapi/rpc.py deleted file mode 100644 index 89dc6f3e041..00000000000 --- a/keyext.api/src/main/python/keyapi/rpc.py +++ /dev/null @@ -1,76 +0,0 @@ -import abc -import json -from abc import abstractmethod -from multiprocessing import Process, SimpleQueue, Lock, Value - - -class Client(abc.ABC): - @abstractmethod - def handle(self, response): - pass - - -class JsonRPCHandler: - client: Client - - def __init__(self, in_stream, out_stream): - self.input = in_stream - self.out = out_stream - self.__id = 0 - self.client: Client - - self._events = dict() - self._responses = dict() - - # t: Process = Process(target=self.__read) - # t.start() - - def read_message(self): - length = 0 - for clength in self.input.readlines(): - if clength.startswith("Content-Length:"): - length = int(clength[14:]) - break - - payload = self.input.read(2 + length) - r = json.loads(payload) - if "id" in r: # JSON response for request - rid = r["id"] - # self._responses[rid] = r - # if rid in self._events: - # self._events[rid].set() - else: # JSON notification - self.client.handle(r) - return r - - def __read(self): - while True: - self.read_message() - - # def __create_event(self, number): - # self._events[number] = Event() - - def _send(self, method, params): - self.__id += 1 - id = self.__id - # self.__create_event(self.__id) - req = {"jsonrpc": "2.0", "method": method, "params": params, "id": self.__id} - - self._write(json.dumps(req)) - # self._wait_for(self.__id) - - r = dict() - while "id" in r and str(r[id]) != str(id): - r = self.read_message() - return r - - def _write(self, msg): - length = len(msg) - self.out.write(f"Content-Length: {length}\r\n") - self.out.write("\r\n") - self.out.write(msg) - self.out.write("\r\n") - self.out.flush() - - # def _wait_for(self, rid): - # self._events[rid].wait() diff --git a/keyext.api/src/test/java/org/keyproject/key/api/TestRpc.java b/keyext.api/src/test/java/org/keyproject/key/api/TestRpc.java index 5ebb562d474..9e7869bdfaa 100644 --- a/keyext.api/src/test/java/org/keyproject/key/api/TestRpc.java +++ b/keyext.api/src/test/java/org/keyproject/key/api/TestRpc.java @@ -10,7 +10,6 @@ import java.util.Collections; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; @@ -19,7 +18,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.keyproject.key.api.adapters.KeyAdapter; import org.keyproject.key.api.remoteapi.KeyApi; import org.keyproject.key.api.remoteclient.ClientApi; @@ -37,7 +35,7 @@ void setup() throws IOException { inClient.connect(outServer); outClient.connect(inServer); - KeyApiImpl impl = new KeyApiImpl(new KeyAdapter(null)); + KeyApiImpl impl = new KeyApiImpl(); Launcher serverLauncher = StartServer.launch(outServer, inServer, impl); impl.setClientApi(serverLauncher.getRemoteProxy()); @@ -61,7 +59,7 @@ void setup() throws IOException { } @AfterEach - void teardown() throws ExecutionException, InterruptedException, TimeoutException { + void teardown() { serverListening.cancel(true); clientListening.cancel(true); } diff --git a/keyext.api/src/main/python/keyapi/__init__.py b/keyext.client.python/keyapi/__init__.py similarity index 54% rename from keyext.api/src/main/python/keyapi/__init__.py rename to keyext.client.python/keyapi/__init__.py index 8523f3801f5..4a913b39b2e 100644 --- a/keyext.api/src/main/python/keyapi/__init__.py +++ b/keyext.client.python/keyapi/__init__.py @@ -1,13 +1,4 @@ -from keyapi.rpc import JsonRPCHandler, Client - - -class KeyClient(Client): - def __int__(self): - pass - - def handle(self, res): - print(res) - +from keyapi.rpc import * class KeyStub(JsonRPCHandler): def __init__(self, input, output): diff --git a/keyext.client.python/keyapi/rpc.py b/keyext.client.python/keyapi/rpc.py new file mode 100644 index 00000000000..02ba2be474b --- /dev/null +++ b/keyext.client.python/keyapi/rpc.py @@ -0,0 +1,200 @@ +import enum +import json +import sys +import threading +from io import TextIOWrapper +from typing import Dict + +JSON_RPC_REQ_FORMAT = "Content-Length: {json_string_len}\r\n\r\n{json_string}" +LEN_HEADER = "Content-Length: " +TYPE_HEADER = "Content-Type: " + + +class MyEncoder(json.JSONEncoder): + """ + Encodes an object in JSON + """ + + def default(self, o): # pylint: disable=E0202 + return o.__dict__ + + +class ResponseError(Exception): + def __init__(self, error_code, message): + super().__init__(message) + self.error_code = error_code + + +class ErrorCodes(enum.Enum): + ParseError = 1 + + +class JsonRpcEndpoint(object): + ''' + Thread safe JSON RPC endpoint implementation. Responsible to recieve and send JSON RPC messages, as described in the + protocol. More information can be found: https://www.jsonrpc.org/ + ''' + + def __init__(self, stdin, stdout): + self.stdin = stdin + self.stdout = stdout + self.read_lock = threading.Lock() + self.write_lock = threading.Lock() + + @staticmethod + def __add_header(json_string): + ''' + Adds a header for the given json string + + :param str json_string: The string + :return: the string with the header + ''' + return JSON_RPC_REQ_FORMAT.format(json_string_len=len(json_string), json_string=json_string) + + def send_request(self, message): + ''' + Sends the given message. + + :param dict message: The message to send. + ''' + json_string = json.dumps(message, cls=MyEncoder) + jsonrpc_req = self.__add_header(json_string) + with self.write_lock: + self.stdin.write(jsonrpc_req.encode()) + self.stdin.flush() + + def recv_response(self): + ''' + Recives a message. + + :return: a message + ''' + with self.read_lock: + message_size = None + while True: + # read header + line = self.stdout.readline() + if not line: + # server quit + return None + line = line.decode("utf-8") + if not line.endswith("\r\n"): + raise ResponseError(ErrorCodes.ParseError, "Bad header: missing newline") + # remove the "\r\n" + line = line[:-2] + if line == "": + # done with the headers + break + elif line.startswith(LEN_HEADER): + line = line[len(LEN_HEADER):] + if not line.isdigit(): + raise ResponseError(ErrorCodes.ParseError, + "Bad header: size is not int") + message_size = int(line) + elif line.startswith(TYPE_HEADER): + # nothing todo with type for now. + pass + else: + raise ResponseError(ErrorCodes.ParseError, "Bad header: unkown header") + if not message_size: + raise ResponseError(ErrorCodes.ParseError, "Bad header: missing size") + + jsonrpc_res = self.stdout.read(message_size).decode("utf-8") + return json.loads(jsonrpc_res) + + +class LspEndpoint(threading.Thread): + def __init__(self, json_rpc_endpoint: JsonRpcEndpoint, method_callbacks=None, notify_callbacks=None, timeout=2): + super().__init__() + self.json_rpc_endpoint: JsonRpcEndpoint = json_rpc_endpoint + self.notify_callbacks: Dict = notify_callbacks or {} + self.method_callbacks: Dict = method_callbacks or {} + self.event_dict = {} + self.response_dict = {} + self.next_id = 0 + self._timeout = timeout + self.shutdown_flag = False + + def handle_result(self, rpc_id, result, error): + self.response_dict[rpc_id] = (result, error) + cond = self.event_dict[rpc_id] + cond.acquire() + cond.notify() + cond.release() + + def stop(self): + self.shutdown_flag = True + + def run(self): + while not self.shutdown_flag: + try: + jsonrpc_message = self.json_rpc_endpoint.recv_response() + if jsonrpc_message is None: + print("server quit") + break + method = jsonrpc_message.get("method") + result = jsonrpc_message.get("result") + error = jsonrpc_message.get("error") + rpc_id = jsonrpc_message.get("id") + params = jsonrpc_message.get("params") + + if method: + if rpc_id: + # a call for method + if method not in self.method_callbacks: + raise ResponseError(ErrorCodes.MethodNotFound, + "Method not found: {method}".format(method=method)) + result = self.method_callbacks[method](params) + self.send_response(rpc_id, result, None) + else: + # a call for notify + if method not in self.notify_callbacks: + # Have nothing to do with this. + print("Notify method not found: {method}.".format(method=method)) + else: + self.notify_callbacks[method](params) + else: + self.handle_result(rpc_id, result, error) + except ResponseError as e: + self.send_response(rpc_id, None, e) + + def send_response(self, id, result, error): + message_dict = {"jsonrpc": "2.0", "id": id} + if result: + message_dict["result"] = result + if error: + message_dict["error"] = error + self.json_rpc_endpoint.send_request(message_dict) + + def send_message(self, method_name, params, id=None): + message_dict = {} + message_dict["jsonrpc"] = "2.0" + if id is not None: + message_dict["id"] = id + message_dict["method"] = method_name + message_dict["params"] = params + self.json_rpc_endpoint.send_request(message_dict) + + def call_method(self, method_name, **kwargs): + current_id = self.next_id + self.next_id += 1 + cond = threading.Condition() + self.event_dict[current_id] = cond + + cond.acquire() + self.send_message(method_name, kwargs, current_id) + if self.shutdown_flag: + return None + + if not cond.wait(timeout=self._timeout): + raise TimeoutError() + cond.release() + + self.event_dict.pop(current_id) + result, error = self.response_dict.pop(current_id) + if error: + raise ResponseError(error.get("code"), error.get("message"), error.get("data")) + return result + + def send_notification(self, method_name, **kwargs): + self.send_message(method_name, kwargs) diff --git a/keyext.api/src/main/python/main.py b/keyext.client.python/main.py similarity index 100% rename from keyext.api/src/main/python/main.py rename to keyext.client.python/main.py diff --git a/keyext.client.python/rwtest.py b/keyext.client.python/rwtest.py new file mode 100644 index 00000000000..312532192dc --- /dev/null +++ b/keyext.client.python/rwtest.py @@ -0,0 +1,402 @@ +import enum +import abc +import typing +from abc import abstractmethod + + +class ExampleDesc: + """""" + + name: str + description: str + + +class ProofScriptCommandDesc: + """""" + + +class ProofMacroDesc: + """""" + + name: str + category: str + description: str + scriptCommandName: str + + +class TraceValue(enum.Enum): + """""" + + Off = None + Message = None + All = None + + +class SetTraceParams: + """""" + + value: TraceValue + + +class EnvironmentId: + """""" + + envId: str + + +class ProofId: + """""" + + env: EnvironmentId + proofId: str + + +class TreeNodeDesc: + """""" + + +class TreeNodeId: + """""" + + id: str + + +class NodeId: + """""" + + proofId: ProofId + nodeId: int + + +class PrintOptions: + """""" + + unicode: bool + width: int + indentation: int + pure: bool + termLabels: bool + + +class NodeTextId: + """""" + + nodeId: NodeId + nodeTextId: int + + +class NodeTextDesc: + """""" + + id: NodeTextId + result: str + + +class TermActionId: + """""" + + nodeId: NodeId + pio: str + id: str + + +class TermActionKind(enum.Enum): + """""" + + BuiltIn = None + Script = None + Macro = None + Taclet = None + + +class TermActionDesc: + """""" + + commandId: TermActionId + displayName: str + description: str + category: str + kind: TermActionKind + + +class List: + """""" + + +class SortDesc: + """""" + + string: str + documentation: str + extendsSorts: List + anAbstract: bool + s: str + + +class FunctionDesc: + """""" + + name: str + sort: str + retSort: SortDesc + argSorts: List + rigid: bool + unique: bool + skolemConstant: bool + + +class ContractId: + """""" + + envId: EnvironmentId + contractId: int + + +class ContractDesc: + """""" + + contractId: ContractId + name: str + displayName: str + typeName: str + htmlText: str + plainText: str + + +class LoadParams: + """""" + + keyFile: str + javaFile: str + classPath: List + bootClassPath: str + includes: List + + +class ProblemDefinition: + """""" + + sorts: List + functions: List + predicates: List + antecTerms: List + succTerms: List + + +class LogTraceParams: + """""" + + messag: str + verbose: str + + +class MessageType(enum.Enum): + """""" + + Unused = None + Error = None + Warning = None + Info = None + Log = None + Debug = None + + +class ShowMessageParams: + """""" + + type: MessageType + message: str + + + +class ShowMessageRequestParams: + """""" + + type: MessageType + message: str + actions: typing.List[MessageActionItem] + + +class MessageActionItem: + """""" + + title: str + + +class Range: + """""" + + start: int + end: int + + +class ShowDocumentParams: + """""" + + uri: str + external: bool + takeFocus: bool + selection: Range + + +class ShowDocumentResult: + """""" + + success: bool + + +class TaskFinishedInfo: + """""" + + +class TaskStartedInfo: + """""" + + +class KeyServer(): + def env_contracts(self, arg0: EnvironmentId) -> typing.List[ContractDesc]: + """""" + + return self.rpc.call_sync("env/contracts", ) + + def env_functions(self, arg0: EnvironmentId) -> typing.List[FunctionDesc]: + """""" + + return self.rpc.call_sync("env/functions", ) + + def env_openContract(self, arg0: ContractId) -> ProofId: + """""" + + return self.rpc.call_sync("env/openContract", ) + + def env_sorts(self, arg0: EnvironmentId) -> typing.List[SortDesc]: + """""" + + return self.rpc.call_sync("env/sorts", ) + + def examples_list(self, ) -> typing.List[ExampleDesc]: + """""" + + return self.rpc.call_sync("examples/list", ) + + def goal_actions(self, arg0: NodeTextId, arg1: int) -> typing.List[TermActionDesc]: + """""" + + return self.rpc.call_sync("goal/actions", ) + + def goal_apply_action(self, arg0: TermActionId) -> typing.List[TermActionDesc]: + """""" + + return self.rpc.call_sync("goal/apply_action", ) + + def goal_free(self, arg0: NodeTextId): + """""" + + return self.rpc.call_async("goal/free", ) + + def goal_print(self, arg0: NodeId, arg1: PrintOptions) -> NodeTextDesc: + """""" + + return self.rpc.call_sync("goal/print", ) + + def loading_load(self, arg0: LoadParams) -> typing.Union[EnvironmentId, ProofId]: + """""" + + return self.rpc.call_sync("loading/load", ) + + def loading_loadExample(self, arg0: str) -> ProofId: + """""" + + return self.rpc.call_sync("loading/loadExample", ) + + def loading_loadKey(self, arg0: str) -> ProofId: + """""" + + return self.rpc.call_sync("loading/loadKey", ) + + def loading_loadProblem(self, arg0: ProblemDefinition) -> ProofId: + """""" + + return self.rpc.call_sync("loading/loadProblem", ) + + def loading_loadTerm(self, arg0: str) -> ProofId: + """""" + + return self.rpc.call_sync("loading/loadTerm", ) + + def meta_available_macros(self, ) -> typing.List[ProofMacroDesc]: + """""" + + return self.rpc.call_sync("meta/available_macros", ) + + def meta_available_script_commands(self, ) -> typing.List[ProofScriptCommandDesc]: + """""" + + return self.rpc.call_sync("meta/available_script_commands", ) + + def meta_version(self, ) -> str: + """""" + + return self.rpc.call_sync("meta/version", ) + + def proofTree_children(self, arg0: ProofId, arg1: TreeNodeId) -> typing.List[TreeNodeDesc]: + """""" + + return self.rpc.call_sync("proofTree/children", ) + + def proofTree_root(self, arg0: ProofId) -> TreeNodeDesc: + """""" + + return self.rpc.call_sync("proofTree/root", ) + + def proofTree_subtree(self, arg0: ProofId, arg1: TreeNodeId) -> typing.List[TreeNodeDesc]: + """""" + + return self.rpc.call_sync("proofTree/subtree", ) + + def server_exit(self, ): + """""" + + return self.rpc.call_async("server/exit", ) + + def server_setTrace(self, arg0: SetTraceParams): + """""" + + return self.rpc.call_async("server/setTrace", ) + + def server_shutdown(self, ) -> bool: + """""" + + return self.rpc.call_sync("server/shutdown", ) + + +class Client(abc.ABCMeta): + @abstractmethod + def client_logTrace(self, arg0: LogTraceParams): + """""" + + pass + + @abstractmethod + def client_sayHello(self, arg0: str): + """""" + + pass + + @abstractmethod + def client_showDocument(self, arg0: ShowDocumentParams) -> ShowDocumentResult: + """""" + + pass + + @abstractmethod + def client_sm(self, arg0: ShowMessageParams): + """""" + + pass + + @abstractmethod + def client_userResponse(self, arg0: ShowMessageRequestParams) -> MessageActionItem: + """""" + + pass From 51aeccbd7d951d675a659667eecb48abb8b6d773 Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Wed, 1 Nov 2023 15:52:40 +0100 Subject: [PATCH 07/35] running for primitive data types, somethings wrong in de-/serialization --- api.meta.json | 445 +++++++++------ api.meta.md | 53 +- api.py | 374 ------------ keydata.py | 493 ++++++++++++++++ .../src/main/java/ExtractMetaData.java | 24 +- keyext.api.doc/src/main/java/Metamodel.java | 7 +- .../src/main/java/PythionGenerator.java | 230 ++++++++ keyext.api/build.gradle | 1 + .../keyproject/key/api/GenericSerializer.java | 61 ++ .../org/keyproject/key/api/KeyApiImpl.java | 2 +- .../org/keyproject/key/api/StartServer.java | 6 +- .../key/api/adapters/KeyAdapter.java | 2 +- .../ShowMessageRequestParams.java | 4 +- keyext.client.python/keyapi/__init__.py | 12 +- keyext.client.python/keyapi/keydata.py | 533 ++++++++++++++++++ keyext.client.python/keyapi/rpc.py | 58 +- keyext.client.python/keyapi/server.py | 161 ++++++ keyext.client.python/main.py | 22 +- keyext.client.python/rwtest.py | 402 ------------- server.py | 159 ++++++ 20 files changed, 2033 insertions(+), 1016 deletions(-) delete mode 100644 api.py create mode 100644 keydata.py create mode 100644 keyext.api.doc/src/main/java/PythionGenerator.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/GenericSerializer.java create mode 100644 keyext.client.python/keyapi/keydata.py create mode 100644 keyext.client.python/keyapi/server.py delete mode 100644 keyext.client.python/rwtest.py create mode 100644 server.py diff --git a/api.meta.json b/api.meta.json index 180b16a3019..953b76c6088 100644 --- a/api.meta.json +++ b/api.meta.json @@ -10,11 +10,13 @@ "fields": [ { "name": "name", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "description", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -51,19 +53,23 @@ "fields": [ { "name": "name", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "category", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "description", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "scriptCommandName", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -87,7 +93,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "params", "type": "SetTraceParams" } ] @@ -97,7 +103,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "id", "type": "ProofId" } ], @@ -112,11 +118,11 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "proof", "type": "ProofId" }, { - "name": "arg1", + "name": "nodeId", "type": "TreeNodeId" } ], @@ -134,11 +140,11 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "proof", "type": "ProofId" }, { - "name": "arg1", + "name": "nodeId", "type": "TreeNodeId" } ], @@ -156,11 +162,11 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "id", "type": "NodeId" }, { - "name": "arg1", + "name": "options", "type": "PrintOptions" } ], @@ -169,11 +175,13 @@ "fields": [ { "name": "id", - "type": "NodeTextId" + "type": "NodeTextId", + "documentation": "" }, { "name": "result", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -184,11 +192,11 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "id", "type": "NodeTextId" }, { - "name": "arg1", + "name": "pos", "type": "INT" } ], @@ -198,23 +206,28 @@ "fields": [ { "name": "commandId", - "type": "TermActionId" + "type": "TermActionId", + "documentation": "" }, { "name": "displayName", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "description", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "category", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "kind", - "type": "TermActionKind" + "type": "TermActionKind", + "documentation": "" } ], "documentation": "" @@ -227,7 +240,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "id", "type": "TermActionId" } ], @@ -237,23 +250,28 @@ "fields": [ { "name": "commandId", - "type": "TermActionId" + "type": "TermActionId", + "documentation": "" }, { "name": "displayName", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "description", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "category", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "kind", - "type": "TermActionKind" + "type": "TermActionKind", + "documentation": "" } ], "documentation": "" @@ -266,7 +284,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "id", "type": "NodeTextId" } ] @@ -276,7 +294,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "env", "type": "EnvironmentId" } ], @@ -286,31 +304,38 @@ "fields": [ { "name": "name", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "sort", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "retSort", - "type": "SortDesc" + "type": "SortDesc", + "documentation": "" }, { "name": "argSorts", - "type": "List" + "type": "List", + "documentation": "" }, { "name": "rigid", - "type": "BOOL" + "type": "BOOL", + "documentation": "" }, { "name": "unique", - "type": "BOOL" + "type": "BOOL", + "documentation": "" }, { "name": "skolemConstant", - "type": "BOOL" + "type": "BOOL", + "documentation": "" } ], "documentation": "" @@ -323,7 +348,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "env", "type": "EnvironmentId" } ], @@ -333,23 +358,28 @@ "fields": [ { "name": "string", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "documentation", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "extendsSorts", - "type": "List" + "type": "List", + "documentation": "" }, { "name": "anAbstract", - "type": "BOOL" + "type": "BOOL", + "documentation": "" }, { "name": "s", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -362,7 +392,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "env", "type": "EnvironmentId" } ], @@ -372,27 +402,33 @@ "fields": [ { "name": "contractId", - "type": "ContractId" + "type": "ContractId", + "documentation": "" }, { "name": "name", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "displayName", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "typeName", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "htmlText", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "plainText", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -405,7 +441,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "contractId", "type": "ContractId" } ], @@ -414,11 +450,13 @@ "fields": [ { "name": "env", - "type": "EnvironmentId" + "type": "EnvironmentId", + "documentation": "" }, { "name": "proofId", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -429,7 +467,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "params", "type": "LoadParams" } ], @@ -439,7 +477,8 @@ "fields": [ { "name": "envId", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -449,11 +488,13 @@ "fields": [ { "name": "env", - "type": "EnvironmentId" + "type": "EnvironmentId", + "documentation": "" }, { "name": "proofId", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -462,11 +503,11 @@ } }, { - "name": "loading/loadKey", + "name": "loading/loadExample", "documentation": "", "args": [ { - "name": "arg0", + "name": "id", "type": "STRING" } ], @@ -475,23 +516,25 @@ "fields": [ { "name": "env", - "type": "EnvironmentId" + "type": "EnvironmentId", + "documentation": "" }, { "name": "proofId", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" } }, { - "name": "loading/loadExample", + "name": "loading/loadProblem", "documentation": "", "args": [ { - "name": "arg0", - "type": "STRING" + "name": "problem", + "type": "ProblemDefinition" } ], "returnType": { @@ -499,23 +542,25 @@ "fields": [ { "name": "env", - "type": "EnvironmentId" + "type": "EnvironmentId", + "documentation": "" }, { "name": "proofId", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" } }, { - "name": "loading/loadProblem", + "name": "loading/loadKey", "documentation": "", "args": [ { - "name": "arg0", - "type": "ProblemDefinition" + "name": "content", + "type": "STRING" } ], "returnType": { @@ -523,11 +568,13 @@ "fields": [ { "name": "env", - "type": "EnvironmentId" + "type": "EnvironmentId", + "documentation": "" }, { "name": "proofId", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -538,7 +585,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "term", "type": "STRING" } ], @@ -547,11 +594,13 @@ "fields": [ { "name": "env", - "type": "EnvironmentId" + "type": "EnvironmentId", + "documentation": "" }, { "name": "proofId", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -562,7 +611,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "e", "type": "STRING" } ] @@ -572,7 +621,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "params", "type": "LogTraceParams" } ] @@ -582,7 +631,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "params", "type": "ShowMessageParams" } ] @@ -592,7 +641,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "params", "type": "ShowMessageRequestParams" } ], @@ -601,7 +650,8 @@ "fields": [ { "name": "title", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -612,7 +662,7 @@ "documentation": "", "args": [ { - "name": "arg0", + "name": "params", "type": "ShowDocumentParams" } ], @@ -621,7 +671,8 @@ "fields": [ { "name": "success", - "type": "BOOL" + "type": "BOOL", + "documentation": "" } ], "documentation": "" @@ -634,11 +685,13 @@ "fields": [ { "name": "name", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "description", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -653,19 +706,23 @@ "fields": [ { "name": "name", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "category", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "description", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "scriptCommandName", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -684,7 +741,8 @@ "fields": [ { "name": "value", - "type": "TraceValue" + "type": "TraceValue", + "documentation": "" } ], "documentation": "" @@ -694,7 +752,8 @@ "fields": [ { "name": "envId", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -704,11 +763,13 @@ "fields": [ { "name": "env", - "type": "EnvironmentId" + "type": "EnvironmentId", + "documentation": "" }, { "name": "proofId", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -723,7 +784,8 @@ "fields": [ { "name": "id", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -733,11 +795,13 @@ "fields": [ { "name": "proofId", - "type": "ProofId" + "type": "ProofId", + "documentation": "" }, { "name": "nodeId", - "type": "INT" + "type": "INT", + "documentation": "" } ], "documentation": "" @@ -747,23 +811,28 @@ "fields": [ { "name": "unicode", - "type": "BOOL" + "type": "BOOL", + "documentation": "" }, { "name": "width", - "type": "INT" + "type": "INT", + "documentation": "" }, { "name": "indentation", - "type": "INT" + "type": "INT", + "documentation": "" }, { "name": "pure", - "type": "BOOL" + "type": "BOOL", + "documentation": "" }, { "name": "termLabels", - "type": "BOOL" + "type": "BOOL", + "documentation": "" } ], "documentation": "" @@ -773,11 +842,13 @@ "fields": [ { "name": "nodeId", - "type": "NodeId" + "type": "NodeId", + "documentation": "" }, { "name": "nodeTextId", - "type": "INT" + "type": "INT", + "documentation": "" } ], "documentation": "" @@ -787,11 +858,13 @@ "fields": [ { "name": "id", - "type": "NodeTextId" + "type": "NodeTextId", + "documentation": "" }, { "name": "result", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -801,15 +874,18 @@ "fields": [ { "name": "nodeId", - "type": "NodeId" + "type": "NodeId", + "documentation": "" }, { "name": "pio", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "id", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -829,23 +905,28 @@ "fields": [ { "name": "commandId", - "type": "TermActionId" + "type": "TermActionId", + "documentation": "" }, { "name": "displayName", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "description", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "category", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "kind", - "type": "TermActionKind" + "type": "TermActionKind", + "documentation": "" } ], "documentation": "" @@ -860,23 +941,28 @@ "fields": [ { "name": "string", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "documentation", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "extendsSorts", - "type": "List" + "type": "List", + "documentation": "" }, { "name": "anAbstract", - "type": "BOOL" + "type": "BOOL", + "documentation": "" }, { "name": "s", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -886,31 +972,38 @@ "fields": [ { "name": "name", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "sort", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "retSort", - "type": "SortDesc" + "type": "SortDesc", + "documentation": "" }, { "name": "argSorts", - "type": "List" + "type": "List", + "documentation": "" }, { "name": "rigid", - "type": "BOOL" + "type": "BOOL", + "documentation": "" }, { "name": "unique", - "type": "BOOL" + "type": "BOOL", + "documentation": "" }, { "name": "skolemConstant", - "type": "BOOL" + "type": "BOOL", + "documentation": "" } ], "documentation": "" @@ -920,11 +1013,13 @@ "fields": [ { "name": "envId", - "type": "EnvironmentId" + "type": "EnvironmentId", + "documentation": "" }, { "name": "contractId", - "type": "INT" + "type": "INT", + "documentation": "" } ], "documentation": "" @@ -934,27 +1029,33 @@ "fields": [ { "name": "contractId", - "type": "ContractId" + "type": "ContractId", + "documentation": "" }, { "name": "name", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "displayName", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "typeName", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "htmlText", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "plainText", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -964,23 +1065,28 @@ "fields": [ { "name": "keyFile", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "javaFile", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "classPath", - "type": "List" + "type": "List", + "documentation": "" }, { "name": "bootClassPath", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "includes", - "type": "List" + "type": "List", + "documentation": "" } ], "documentation": "" @@ -990,23 +1096,28 @@ "fields": [ { "name": "sorts", - "type": "List" + "type": "List", + "documentation": "" }, { "name": "functions", - "type": "List" + "type": "List", + "documentation": "" }, { "name": "predicates", - "type": "List" + "type": "List", + "documentation": "" }, { "name": "antecTerms", - "type": "List" + "type": "List", + "documentation": "" }, { "name": "succTerms", - "type": "List" + "type": "List", + "documentation": "" } ], "documentation": "" @@ -1016,11 +1127,13 @@ "fields": [ { "name": "messag", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "verbose", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -1042,34 +1155,34 @@ "fields": [ { "name": "type", - "type": "MessageType" + "type": "MessageType", + "documentation": "" }, { "name": "message", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" }, - { - "typeName": "MessageActionItem[]", - "fields": [], - "documentation": "" - }, { "typeName": "ShowMessageRequestParams", "fields": [ { "name": "type", - "type": "MessageType" + "type": "MessageType", + "documentation": "" }, { "name": "message", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "actions", - "type": "MessageActionItem[]" + "type": "List", + "documentation": "" } ], "documentation": "" @@ -1079,7 +1192,8 @@ "fields": [ { "name": "title", - "type": "STRING" + "type": "STRING", + "documentation": "" } ], "documentation": "" @@ -1089,11 +1203,13 @@ "fields": [ { "name": "start", - "type": "INT" + "type": "INT", + "documentation": "" }, { "name": "end", - "type": "INT" + "type": "INT", + "documentation": "" } ], "documentation": "" @@ -1103,19 +1219,23 @@ "fields": [ { "name": "uri", - "type": "STRING" + "type": "STRING", + "documentation": "" }, { "name": "external", - "type": "BOOL" + "type": "BOOL", + "documentation": "" }, { "name": "takeFocus", - "type": "BOOL" + "type": "BOOL", + "documentation": "" }, { "name": "selection", - "type": "Range" + "type": "Range", + "documentation": "" } ], "documentation": "" @@ -1125,7 +1245,8 @@ "fields": [ { "name": "success", - "type": "BOOL" + "type": "BOOL", + "documentation": "" } ], "documentation": "" diff --git a/api.meta.md b/api.meta.md index f53d88619f3..f6d7a29c764 100644 --- a/api.meta.md +++ b/api.meta.md @@ -80,13 +80,6 @@ type MessageActionItem { } ``` -### Type: MessageActionItem[] -``` -type MessageActionItem[] { - -} -``` - ### Type: MessageType ``` enum MessageType { Unused, Error, Warning, Info, Log, Debug } @@ -206,7 +199,7 @@ type ShowMessageParams { ### Type: ShowMessageRequestParams ``` type ShowMessageRequestParams { - actions : MessageActionItem[] + actions : List message : STRING type : MessageType } @@ -285,63 +278,63 @@ type TreeNodeId { ### client/logTrace (`server ~~> client`) ``` -Client.client/logTrace( arg0 : LogTraceParams ) **async** +Client.client/logTrace( params : LogTraceParams ) **async** ``` ### client/sayHello (`server ~~> client`) ``` -Client.client/sayHello( arg0 : STRING ) **async** +Client.client/sayHello( e : STRING ) **async** ``` ### client/showDocument (`server -> client`) ``` -Client.client/showDocument( arg0 : ShowDocumentParams ) -> ShowDocumentResult +Client.client/showDocument( params : ShowDocumentParams ) -> ShowDocumentResult ``` ### client/sm (`server ~~> client`) ``` -Client.client/sm( arg0 : ShowMessageParams ) **async** +Client.client/sm( params : ShowMessageParams ) **async** ``` ### client/userResponse (`server -> client`) ``` -Client.client/userResponse( arg0 : ShowMessageRequestParams ) -> MessageActionItem +Client.client/userResponse( params : ShowMessageRequestParams ) -> MessageActionItem ``` ### env/contracts (`client -> server`) ``` -Server.env/contracts( arg0 : EnvironmentId ) -> ContractDesc[] +Server.env/contracts( env : EnvironmentId ) -> ContractDesc[] ``` ### env/functions (`client -> server`) ``` -Server.env/functions( arg0 : EnvironmentId ) -> FunctionDesc[] +Server.env/functions( env : EnvironmentId ) -> FunctionDesc[] ``` ### env/openContract (`client -> server`) ``` -Server.env/openContract( arg0 : ContractId ) -> ProofId +Server.env/openContract( contractId : ContractId ) -> ProofId ``` ### env/sorts (`client -> server`) ``` -Server.env/sorts( arg0 : EnvironmentId ) -> SortDesc[] +Server.env/sorts( env : EnvironmentId ) -> SortDesc[] ``` @@ -355,63 +348,63 @@ Server.examples/list( ) -> ExampleDesc[] ### goal/actions (`client -> server`) ``` -Server.goal/actions( arg0 : NodeTextId, arg1 : INT ) -> TermActionDesc[] +Server.goal/actions( id : NodeTextId, pos : INT ) -> TermActionDesc[] ``` ### goal/apply_action (`client -> server`) ``` -Server.goal/apply_action( arg0 : TermActionId ) -> TermActionDesc[] +Server.goal/apply_action( id : TermActionId ) -> TermActionDesc[] ``` ### goal/free (`client ~~> server`) ``` -Server.goal/free( arg0 : NodeTextId ) **async** +Server.goal/free( id : NodeTextId ) **async** ``` ### goal/print (`client -> server`) ``` -Server.goal/print( arg0 : NodeId, arg1 : PrintOptions ) -> NodeTextDesc +Server.goal/print( id : NodeId, options : PrintOptions ) -> NodeTextDesc ``` ### loading/load (`client -> server`) ``` -Server.loading/load( arg0 : LoadParams ) -> either +Server.loading/load( params : LoadParams ) -> either ``` ### loading/loadExample (`client -> server`) ``` -Server.loading/loadExample( arg0 : STRING ) -> ProofId +Server.loading/loadExample( id : STRING ) -> ProofId ``` ### loading/loadKey (`client -> server`) ``` -Server.loading/loadKey( arg0 : STRING ) -> ProofId +Server.loading/loadKey( content : STRING ) -> ProofId ``` ### loading/loadProblem (`client -> server`) ``` -Server.loading/loadProblem( arg0 : ProblemDefinition ) -> ProofId +Server.loading/loadProblem( problem : ProblemDefinition ) -> ProofId ``` ### loading/loadTerm (`client -> server`) ``` -Server.loading/loadTerm( arg0 : STRING ) -> ProofId +Server.loading/loadTerm( term : STRING ) -> ProofId ``` @@ -439,21 +432,21 @@ Server.meta/version( ) -> STRING ### proofTree/children (`client -> server`) ``` -Server.proofTree/children( arg0 : ProofId, arg1 : TreeNodeId ) -> TreeNodeDesc[] +Server.proofTree/children( proof : ProofId, nodeId : TreeNodeId ) -> TreeNodeDesc[] ``` ### proofTree/root (`client -> server`) ``` -Server.proofTree/root( arg0 : ProofId ) -> TreeNodeDesc +Server.proofTree/root( id : ProofId ) -> TreeNodeDesc ``` ### proofTree/subtree (`client -> server`) ``` -Server.proofTree/subtree( arg0 : ProofId, arg1 : TreeNodeId ) -> TreeNodeDesc[] +Server.proofTree/subtree( proof : ProofId, nodeId : TreeNodeId ) -> TreeNodeDesc[] ``` @@ -467,7 +460,7 @@ Server.server/exit( ) **async** ### server/setTrace (`client ~~> server`) ``` -Server.server/setTrace( arg0 : SetTraceParams ) **async** +Server.server/setTrace( params : SetTraceParams ) **async** ``` diff --git a/api.py b/api.py deleted file mode 100644 index c83050a763e..00000000000 --- a/api.py +++ /dev/null @@ -1,374 +0,0 @@ -import enum -import abc -import typing -class ExampleDesc: - """""" - - name : str - description : str - -class ProofScriptCommandDesc: - """""" - - -class ProofMacroDesc: - """""" - - name : str - category : str - description : str - scriptCommandName : str - -class TraceValue(enum.Enum): - """""" - - Off = None - Message = None - All = None - -class SetTraceParams: - """""" - - value : TraceValue - -class EnvironmentId: - """""" - - envId : str - -class ProofId: - """""" - - env : EnvironmentId - proofId : str - -class TreeNodeDesc: - """""" - - -class TreeNodeId: - """""" - - id : str - -class NodeId: - """""" - - proofId : ProofId - nodeId : int - -class PrintOptions: - """""" - - unicode : bool - width : int - indentation : int - pure : bool - termLabels : bool - -class NodeTextId: - """""" - - nodeId : NodeId - nodeTextId : int - -class NodeTextDesc: - """""" - - id : NodeTextId - result : str - -class TermActionId: - """""" - - nodeId : NodeId - pio : str - id : str - -class TermActionKind(enum.Enum): - """""" - - BuiltIn = None - Script = None - Macro = None - Taclet = None - -class TermActionDesc: - """""" - - commandId : TermActionId - displayName : str - description : str - category : str - kind : TermActionKind - -class List: - """""" - - -class SortDesc: - """""" - - string : str - documentation : str - extendsSorts : List - anAbstract : bool - s : str - -class FunctionDesc: - """""" - - name : str - sort : str - retSort : SortDesc - argSorts : List - rigid : bool - unique : bool - skolemConstant : bool - -class ContractId: - """""" - - envId : EnvironmentId - contractId : int - -class ContractDesc: - """""" - - contractId : ContractId - name : str - displayName : str - typeName : str - htmlText : str - plainText : str - -class LoadParams: - """""" - - keyFile : str - javaFile : str - classPath : List - bootClassPath : str - includes : List - -class ProblemDefinition: - """""" - - sorts : List - functions : List - predicates : List - antecTerms : List - succTerms : List - -class LogTraceParams: - """""" - - messag : str - verbose : str - -class MessageType(enum.Enum): - """""" - - Unused = None - Error = None - Warning = None - Info = None - Log = None - Debug = None - -class ShowMessageParams: - """""" - - type : MessageType - message : str - -class MessageActionItem[]: - """""" - - -class ShowMessageRequestParams: - """""" - - type : MessageType - message : str - actions : MessageActionItem[] - -class MessageActionItem: - """""" - - title : str - -class Range: - """""" - - start : int - end : int - -class ShowDocumentParams: - """""" - - uri : str - external : bool - takeFocus : bool - selection : Range - -class ShowDocumentResult: - """""" - - success : bool - -class TaskFinishedInfo: - """""" - - -class TaskStartedInfo: - """""" - - -class KeyServer(): - def env_contracts(self, arg0 : EnvironmentId) -> typing.List[ContractDesc]: - """""" - - return self.rpc.call_sync("env/contracts", ) - - def env_functions(self, arg0 : EnvironmentId) -> typing.List[FunctionDesc]: - """""" - - return self.rpc.call_sync("env/functions", ) - - def env_openContract(self, arg0 : ContractId) -> ProofId: - """""" - - return self.rpc.call_sync("env/openContract", ) - - def env_sorts(self, arg0 : EnvironmentId) -> typing.List[SortDesc]: - """""" - - return self.rpc.call_sync("env/sorts", ) - - def examples_list(self, ) -> typing.List[ExampleDesc]: - """""" - - return self.rpc.call_sync("examples/list", ) - - def goal_actions(self, arg0 : NodeTextId, arg1 : int) -> typing.List[TermActionDesc]: - """""" - - return self.rpc.call_sync("goal/actions", ) - - def goal_apply_action(self, arg0 : TermActionId) -> typing.List[TermActionDesc]: - """""" - - return self.rpc.call_sync("goal/apply_action", ) - - def goal_free(self, arg0 : NodeTextId): - """""" - - return self.rpc.call_async("goal/free", ) - - def goal_print(self, arg0 : NodeId, arg1 : PrintOptions) -> NodeTextDesc: - """""" - - return self.rpc.call_sync("goal/print", ) - - def loading_load(self, arg0 : LoadParams) -> typing.Union[EnvironmentId, ProofId]: - """""" - - return self.rpc.call_sync("loading/load", ) - - def loading_loadExample(self, arg0 : str) -> ProofId: - """""" - - return self.rpc.call_sync("loading/loadExample", ) - - def loading_loadKey(self, arg0 : str) -> ProofId: - """""" - - return self.rpc.call_sync("loading/loadKey", ) - - def loading_loadProblem(self, arg0 : ProblemDefinition) -> ProofId: - """""" - - return self.rpc.call_sync("loading/loadProblem", ) - - def loading_loadTerm(self, arg0 : str) -> ProofId: - """""" - - return self.rpc.call_sync("loading/loadTerm", ) - - def meta_available_macros(self, ) -> typing.List[ProofMacroDesc]: - """""" - - return self.rpc.call_sync("meta/available_macros", ) - - def meta_available_script_commands(self, ) -> typing.List[ProofScriptCommandDesc]: - """""" - - return self.rpc.call_sync("meta/available_script_commands", ) - - def meta_version(self, ) -> STRING: - """""" - - return self.rpc.call_sync("meta/version", ) - - def proofTree_children(self, arg0 : ProofId, arg1 : TreeNodeId) -> typing.List[TreeNodeDesc]: - """""" - - return self.rpc.call_sync("proofTree/children", ) - - def proofTree_root(self, arg0 : ProofId) -> TreeNodeDesc: - """""" - - return self.rpc.call_sync("proofTree/root", ) - - def proofTree_subtree(self, arg0 : ProofId, arg1 : TreeNodeId) -> typing.List[TreeNodeDesc]: - """""" - - return self.rpc.call_sync("proofTree/subtree", ) - - def server_exit(self, ): - """""" - - return self.rpc.call_async("server/exit", ) - - def server_setTrace(self, arg0 : SetTraceParams): - """""" - - return self.rpc.call_async("server/setTrace", ) - - def server_shutdown(self, ) -> BOOL: - """""" - - return self.rpc.call_sync("server/shutdown", ) - -class Client(abc.AbcMeta): - @abstractmethod - def client_logTrace(self, arg0 : LogTraceParams): - """""" - - pass - - @abstractmethod - def client_sayHello(self, arg0 : str): - """""" - - pass - - @abstractmethod - def client_showDocument(self, arg0 : ShowDocumentParams) -> ShowDocumentResult: - """""" - - pass - - @abstractmethod - def client_sm(self, arg0 : ShowMessageParams): - """""" - - pass - - @abstractmethod - def client_userResponse(self, arg0 : ShowMessageRequestParams) -> MessageActionItem: - """""" - - pass - diff --git a/keydata.py b/keydata.py new file mode 100644 index 00000000000..59511c2a7a8 --- /dev/null +++ b/keydata.py @@ -0,0 +1,493 @@ +from __future__ import annotations +import enum +import abc +import typing +from abc import abstractmethod, ABCMeta + +class ExampleDesc: + """""" + + name : str + """""" + + description : str + """""" + + def __init__(self, name, description): + self.name = name + self.description = description + +class ProofScriptCommandDesc: + """""" + + def __init__(self, ): + pass + + +class ProofMacroDesc: + """""" + + name : str + """""" + + category : str + """""" + + description : str + """""" + + scriptCommandName : str + """""" + + def __init__(self, name, category, description, scriptCommandName): + self.name = name + self.category = category + self.description = description + self.scriptCommandName = scriptCommandName + +class TraceValue(enum.Enum): + """""" + + Off = None + Message = None + All = None + +class SetTraceParams: + """""" + + value : TraceValue + """""" + + def __init__(self, value): + self.value = value + +class EnvironmentId: + """""" + + envId : str + """""" + + def __init__(self, envId): + self.envId = envId + +class ProofId: + """""" + + env : EnvironmentId + """""" + + proofId : str + """""" + + def __init__(self, env, proofId): + self.env = env + self.proofId = proofId + +class TreeNodeDesc: + """""" + + def __init__(self, ): + pass + + +class TreeNodeId: + """""" + + id : str + """""" + + def __init__(self, id): + self.id = id + +class NodeId: + """""" + + proofId : ProofId + """""" + + nodeId : int + """""" + + def __init__(self, proofId, nodeId): + self.proofId = proofId + self.nodeId = nodeId + +class PrintOptions: + """""" + + unicode : bool + """""" + + width : int + """""" + + indentation : int + """""" + + pure : bool + """""" + + termLabels : bool + """""" + + def __init__(self, unicode, width, indentation, pure, termLabels): + self.unicode = unicode + self.width = width + self.indentation = indentation + self.pure = pure + self.termLabels = termLabels + +class NodeTextId: + """""" + + nodeId : NodeId + """""" + + nodeTextId : int + """""" + + def __init__(self, nodeId, nodeTextId): + self.nodeId = nodeId + self.nodeTextId = nodeTextId + +class NodeTextDesc: + """""" + + id : NodeTextId + """""" + + result : str + """""" + + def __init__(self, id, result): + self.id = id + self.result = result + +class TermActionId: + """""" + + nodeId : NodeId + """""" + + pio : str + """""" + + id : str + """""" + + def __init__(self, nodeId, pio, id): + self.nodeId = nodeId + self.pio = pio + self.id = id + +class TermActionKind(enum.Enum): + """""" + + BuiltIn = None + Script = None + Macro = None + Taclet = None + +class TermActionDesc: + """""" + + commandId : TermActionId + """""" + + displayName : str + """""" + + description : str + """""" + + category : str + """""" + + kind : TermActionKind + """""" + + def __init__(self, commandId, displayName, description, category, kind): + self.commandId = commandId + self.displayName = displayName + self.description = description + self.category = category + self.kind = kind + +class List: + """""" + + def __init__(self, ): + pass + + +class SortDesc: + """""" + + string : str + """""" + + documentation : str + """""" + + extendsSorts : List + """""" + + anAbstract : bool + """""" + + s : str + """""" + + def __init__(self, string, documentation, extendsSorts, anAbstract, s): + self.string = string + self.documentation = documentation + self.extendsSorts = extendsSorts + self.anAbstract = anAbstract + self.s = s + +class FunctionDesc: + """""" + + name : str + """""" + + sort : str + """""" + + retSort : SortDesc + """""" + + argSorts : List + """""" + + rigid : bool + """""" + + unique : bool + """""" + + skolemConstant : bool + """""" + + def __init__(self, name, sort, retSort, argSorts, rigid, unique, skolemConstant): + self.name = name + self.sort = sort + self.retSort = retSort + self.argSorts = argSorts + self.rigid = rigid + self.unique = unique + self.skolemConstant = skolemConstant + +class ContractId: + """""" + + envId : EnvironmentId + """""" + + contractId : int + """""" + + def __init__(self, envId, contractId): + self.envId = envId + self.contractId = contractId + +class ContractDesc: + """""" + + contractId : ContractId + """""" + + name : str + """""" + + displayName : str + """""" + + typeName : str + """""" + + htmlText : str + """""" + + plainText : str + """""" + + def __init__(self, contractId, name, displayName, typeName, htmlText, plainText): + self.contractId = contractId + self.name = name + self.displayName = displayName + self.typeName = typeName + self.htmlText = htmlText + self.plainText = plainText + +class LoadParams: + """""" + + keyFile : str + """""" + + javaFile : str + """""" + + classPath : List + """""" + + bootClassPath : str + """""" + + includes : List + """""" + + def __init__(self, keyFile, javaFile, classPath, bootClassPath, includes): + self.keyFile = keyFile + self.javaFile = javaFile + self.classPath = classPath + self.bootClassPath = bootClassPath + self.includes = includes + +class ProblemDefinition: + """""" + + sorts : List + """""" + + functions : List + """""" + + predicates : List + """""" + + antecTerms : List + """""" + + succTerms : List + """""" + + def __init__(self, sorts, functions, predicates, antecTerms, succTerms): + self.sorts = sorts + self.functions = functions + self.predicates = predicates + self.antecTerms = antecTerms + self.succTerms = succTerms + +class LogTraceParams: + """""" + + messag : str + """""" + + verbose : str + """""" + + def __init__(self, messag, verbose): + self.messag = messag + self.verbose = verbose + +class MessageType(enum.Enum): + """""" + + Unused = None + Error = None + Warning = None + Info = None + Log = None + Debug = None + +class ShowMessageParams: + """""" + + type : MessageType + """""" + + message : str + """""" + + def __init__(self, type, message): + self.type = type + self.message = message + +class ShowMessageRequestParams: + """""" + + type : MessageType + """""" + + message : str + """""" + + actions : List + """""" + + def __init__(self, type, message, actions): + self.type = type + self.message = message + self.actions = actions + +class MessageActionItem: + """""" + + title : str + """""" + + def __init__(self, title): + self.title = title + +class Range: + """""" + + start : int + """""" + + end : int + """""" + + def __init__(self, start, end): + self.start = start + self.end = end + +class ShowDocumentParams: + """""" + + uri : str + """""" + + external : bool + """""" + + takeFocus : bool + """""" + + selection : Range + """""" + + def __init__(self, uri, external, takeFocus, selection): + self.uri = uri + self.external = external + self.takeFocus = takeFocus + self.selection = selection + +class ShowDocumentResult: + """""" + + success : bool + """""" + + def __init__(self, success): + self.success = success + +class TaskFinishedInfo: + """""" + + def __init__(self, ): + pass + + +class TaskStartedInfo: + """""" + + def __init__(self, ): + pass + + +KEY_DATA_CLASSES = { "ExampleDesc": ExampleDesc,"ProofScriptCommandDesc": ProofScriptCommandDesc,"ProofMacroDesc": ProofMacroDesc,"TraceValue": TraceValue,"SetTraceParams": SetTraceParams,"EnvironmentId": EnvironmentId,"ProofId": ProofId,"TreeNodeDesc": TreeNodeDesc,"TreeNodeId": TreeNodeId,"NodeId": NodeId,"PrintOptions": PrintOptions,"NodeTextId": NodeTextId,"NodeTextDesc": NodeTextDesc,"TermActionId": TermActionId,"TermActionKind": TermActionKind,"TermActionDesc": TermActionDesc,"List": List,"SortDesc": SortDesc,"FunctionDesc": FunctionDesc,"ContractId": ContractId,"ContractDesc": ContractDesc,"LoadParams": LoadParams,"ProblemDefinition": ProblemDefinition,"LogTraceParams": LogTraceParams,"MessageType": MessageType,"ShowMessageParams": ShowMessageParams,"ShowMessageRequestParams": ShowMessageRequestParams,"MessageActionItem": MessageActionItem,"Range": Range,"ShowDocumentParams": ShowDocumentParams,"ShowDocumentResult": ShowDocumentResult,"TaskFinishedInfo": TaskFinishedInfo,"TaskStartedInfo": TaskStartedInfo } + diff --git a/keyext.api.doc/src/main/java/ExtractMetaData.java b/keyext.api.doc/src/main/java/ExtractMetaData.java index 16e3ffd2d56..d053b9268d3 100644 --- a/keyext.api.doc/src/main/java/ExtractMetaData.java +++ b/keyext.api.doc/src/main/java/ExtractMetaData.java @@ -10,6 +10,8 @@ import java.nio.file.Paths; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.function.Supplier; import de.uka.ilkd.key.proof.Proof; @@ -47,21 +49,17 @@ public static void main(String[] args) { addClientEndpoint(method); } - try { - Files.writeString(Paths.get("api.meta.json"), getGson().toJson(keyApi)); - } catch (IOException e) { - e.printStackTrace(); - } + runGenerator("api.meta.json", (a) -> () -> getGson().toJson(a)); + runGenerator("api.meta.md", DocGen::new); + runGenerator("keydata.py", PythionGenerator.PyDataGen::new); + runGenerator("server.py", PythionGenerator.PyApiGen::new); + } + private static void runGenerator(String target, + Function> api) { try { - var n = new DocGen(keyApi); - Files.writeString(Paths.get("api.meta.md"), n.get()); - } catch (IOException e) { - e.printStackTrace(); - } - try { - var n = new PyGen(keyApi); - Files.writeString(Paths.get("api.py"), n.get()); + var n = api.apply(keyApi); + Files.writeString(Paths.get(target), n.get()); } catch (IOException e) { e.printStackTrace(); } diff --git a/keyext.api.doc/src/main/java/Metamodel.java b/keyext.api.doc/src/main/java/Metamodel.java index 6d6ed916c6e..db639174e71 100644 --- a/keyext.api.doc/src/main/java/Metamodel.java +++ b/keyext.api.doc/src/main/java/Metamodel.java @@ -47,10 +47,13 @@ record ClientNotification(String name, String documentation, List args implements Endpoint { } - record Field(String name, /* Type */ String type) { + record Field(String name, /* Type */ String type, String documentation) { + Field(String name, String type) { + this(name, type, ""); + } } - sealed interface Type { + public sealed interface Type { default String kind() { return getClass().getSimpleName(); } diff --git a/keyext.api.doc/src/main/java/PythionGenerator.java b/keyext.api.doc/src/main/java/PythionGenerator.java new file mode 100644 index 00000000000..47faabfc131 --- /dev/null +++ b/keyext.api.doc/src/main/java/PythionGenerator.java @@ -0,0 +1,230 @@ +/* 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 */ +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Comparator; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +public abstract class PythionGenerator implements Supplier { + protected final Metamodel.KeyApi metamodel; + protected PrintWriter out; + protected final StringWriter target = new StringWriter(); + + public PythionGenerator(Metamodel.KeyApi metamodel) { + this.metamodel = metamodel; + } + + @Override + public String get() { + try (var out = new PrintWriter(target)) { + this.out = out; + run(); + } + return target.toString(); + } + + protected abstract void run(); + + protected String asPython(String typeName) { + return switch (typeName) { + case "INT" -> "int"; + case "LONG" -> "int"; + case "STRING" -> "str"; + case "BOOL" -> "bool"; + case "DOUBLE" -> "float"; + default -> { + var t = findType(typeName); + yield asPython(t); + } + }; + } + + protected String asPython(Metamodel.Type t) { + if (t instanceof Metamodel.ListType lt) { + return "typing.List[" + asPython(lt.type()) + "]"; + } + + if (t instanceof Metamodel.EitherType lt) { + return "typing.Union[" + asPython(lt.a()) + ", " + asPython(lt.b()) + "]"; + } + + if (t instanceof Metamodel.BuiltinType bt) { + return switch (bt) { + case INT -> "int"; + case LONG -> "int"; + case STRING -> "str"; + case BOOL -> "bool"; + case DOUBLE -> "float"; + }; + } + return t.name(); + } + + protected Metamodel.Type findType(String typeName) { + return this.metamodel.types().stream().filter(it -> it.name().equals(typeName)).findFirst() + .orElseThrow(() -> new RuntimeException("Could not find type: " + typeName)); + } + + + public static class PyApiGen extends PythionGenerator { + public PyApiGen(Metamodel.KeyApi metamodel) { + super(metamodel); + } + + @Override + protected void run() { + out.format(""" + from __future__ import annotations + from .keydata import * + from .rpc import ServerBase, LspEndpoint + + import enum + import abc + import typing + from abc import abstractmethod + """); + server( + metamodel.endpoints() + .stream() + .filter(it -> it instanceof Metamodel.ServerRequest + || it instanceof Metamodel.ServerNotification) + .sorted(Comparator.comparing(Metamodel.Endpoint::name))); + + client( + metamodel.endpoints() + .stream() + .filter(it -> it instanceof Metamodel.ClientRequest + || it instanceof Metamodel.ClientNotification) + .sorted(Comparator.comparing(Metamodel.Endpoint::name))); + } + + + private void client(Stream sorted) { + out.format("class Client(abc.ABCMeta):%n"); + sorted.forEach(this::clientEndpoint); + } + + private void clientEndpoint(Metamodel.Endpoint endpoint) { + var args = endpoint.args().stream() + .map(it -> "%s : %s".formatted(it.name(), asPython(it.type()))) + .collect(Collectors.joining(", ")); + out.format(" @abstractmethod%n"); + if (endpoint instanceof Metamodel.ClientRequest sr) { + out.format(" def %s(self, %s) -> %s:%n", endpoint.name().replace("/", "_"), args, + asPython(sr.returnType())); + } else { + out.format(" def %s(self, %s):%n", endpoint.name().replace("/", "_"), args); + } + out.format(" \"\"\"%s\"\"\"%n%n", endpoint.documentation()); + out.format(" pass".formatted(endpoint.name(), "")); + out.println(); + out.println(); + } + + private void server(Stream sorted) { + out.format(""" + class KeyServer(ServerBase):%n + def __init__(self, endpoint : LspEndpoint): + super().__init__(endpoint) + + """); + sorted.forEach(this::serverEndpoint); + } + + private void serverEndpoint(Metamodel.Endpoint endpoint) { + var args = endpoint.args().stream() + .map(it -> "%s : %s".formatted(it.name(), asPython(it.type()))) + .collect(Collectors.joining(", ")); + + var params = "[]"; + if (!endpoint.args().isEmpty()) { + params = endpoint.args().stream() + .map(Metamodel.Argument::name) + .collect(Collectors.joining(" , ", "[", "]")); + } + + if (endpoint instanceof Metamodel.ServerRequest sr) { + out.format(" def %s(self, %s) -> %s:%n", endpoint.name().replace("/", "_"), args, + asPython(sr.returnType())); + out.format(" \"\"\"%s\"\"\"%n%n", sr.documentation()); + out.format( + " return self._call_sync(\"%s\", %s)".formatted(endpoint.name(), params)); + } else { + out.format(" def %s(self, %s):%n", endpoint.name().replace("/", "_"), args); + out.format(" \"\"\"%s\"\"\"%n%n", endpoint.documentation()); + out.format(" return self._call_async(\"%s\", %s)".formatted(endpoint.name(), + params)); + } + out.println(); + out.println(); + } + + } + + + public static class PyDataGen extends PythionGenerator { + public PyDataGen(Metamodel.KeyApi metamodel) { + super(metamodel); + } + + @Override + public String get() { + try (var out = new PrintWriter(target)) { + this.out = out; + run(); + } + return target.toString(); + } + + protected void run() { + out.format(""" + from __future__ import annotations + import enum + import abc + import typing + from abc import abstractmethod, ABCMeta + + """); + metamodel.types().forEach(this::printType); + + var names = + metamodel.types().stream().map(it -> "\"%s\": %s".formatted(it.name(), it.name())) + .collect(Collectors.joining(",")); + out.format("KEY_DATA_CLASSES = { %s }%n%n", names); + } + + private void printType(Metamodel.Type type) { + if (type instanceof Metamodel.ObjectType ot) { + out.format("class %s:%n".formatted(type.name())); + out.format(" \"\"\"%s\"\"\"%n", type.documentation()); + ot.fields().forEach(it -> out.format("%n %s : %s%n \"\"\"%s\"\"\"%n" + .formatted(it.name(), asPython(it.type()), it.documentation()))); + + out.format("\n def __init__(self%s):%n".formatted( + ot.fields().stream() + .map(Metamodel.Field::name) + .collect(Collectors.joining(", ", ", ", "")))); + + if (ot.fields().isEmpty()) + out.format(" pass%n%n"); + + for (Metamodel.Field field : ot.fields()) { + out.format(" self.%s = %s%n", field.name(), field.name()); + } + + } else if (type instanceof Metamodel.EnumType et) { + out.format("class %s(enum.Enum):%n".formatted(type.name())); + out.format(" \"\"\"%s\"\"\"%n%n", type.documentation()); + et.values().forEach(it -> out.format(" %s = None%n".formatted(it))); + } + out.println(); + } + } +} diff --git a/keyext.api/build.gradle b/keyext.api/build.gradle index d2fdca4e9be..bc997708b51 100644 --- a/keyext.api/build.gradle +++ b/keyext.api/build.gradle @@ -27,5 +27,6 @@ dependencies { compileJava { // for GraalVM options.compilerArgs += ["-Aproject=${project.group}/${project.name}"] + options.compilerArgs += ["-parameters"] // for having parameter name in reflection } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/GenericSerializer.java b/keyext.api/src/main/java/org/keyproject/key/api/GenericSerializer.java new file mode 100644 index 00000000000..9082242ee37 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/GenericSerializer.java @@ -0,0 +1,61 @@ +/* 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.keyproject.key.api; + +import java.lang.reflect.Type; + +import com.google.gson.*; + +/** + * Stackoverflow + * post + */ +public class GenericSerializer implements JsonSerializer /* , JsonDeserializer */ { + + private static final String CLASS_PROPERTY_NAME = "$type"; + private final Gson gson; + + public GenericSerializer() { + gson = new Gson(); + } + + public GenericSerializer(Gson gson) { + this.gson = gson; + } + + /* + * @Override + * public Object deserialize(JsonElement json, Type typeOfT, + * JsonDeserializationContext context) throws JsonParseException { + * + * Class actualClass; + * if (json.isJsonObject()) { + * JsonObject jsonObject = json.getAsJsonObject(); + * String className = jsonObject.get(CLASS_PROPERTY_NAME).getAsString(); + * try { + * actualClass = Class.forName(className); + * } catch (ClassNotFoundException e) { + * e.printStackTrace(); + * throw new JsonParseException(e.getMessage()); + * } + * } else { + * actualClass = typeOfT.getClass(); + * } + * + * return gson.fromJson(json, actualClass); + * } + */ + + @Override + public JsonElement serialize(Object src, Type typeOfSrc, + JsonSerializationContext context) { + JsonElement retValue = gson.toJsonTree(src); + if (retValue.isJsonObject()) { + retValue.getAsJsonObject().addProperty(CLASS_PROPERTY_NAME, src.getClass().getName()); + } + return retValue; + } + +} diff --git a/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java b/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java index fc165efa767..f271fd4e614 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java @@ -81,7 +81,7 @@ public KeyApiImpl() { public CompletableFuture> examples() { return CompletableFutures .computeAsync((c) -> ExampleChooser.listExamples(ExampleChooser.lookForExamples()) - .stream().map(it -> ExampleDesc.from(it)).toList()); + .stream().map(ExampleDesc::from).toList()); } @Override diff --git a/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java b/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java index eefa000ac8c..9cbfeb7ac70 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java @@ -29,8 +29,6 @@ public class StartServer implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(StartServer.class); - private static KeyAdapter adapter; - // region CLI arguments @Option(names = "--std", description = "use stdout and stdin for communication") boolean stdStreams; @@ -151,7 +149,9 @@ public void run() { public static void configureJson(GsonBuilder gsonBuilder) { - adapter = new KeyAdapter(gsonBuilder); + gsonBuilder.registerTypeHierarchyAdapter(Object.class, new GenericSerializer()); + gsonBuilder.registerTypeAdapter(File.class, new KeyAdapter.FileTypeAdapter()); + } public static Launcher launch(OutputStream out, InputStream in, KeyApiImpl keyApi) { diff --git a/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java b/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java index 77ab08d832a..ecd1e93371b 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java @@ -53,7 +53,7 @@ public JsonElement serialize(ProofMacro src, Type typeOfSrc, } } - static class FileTypeAdapter extends TypeAdapter { + public static class FileTypeAdapter extends TypeAdapter { @Override public void write(JsonWriter out, File value) throws IOException { out.value(value.toString()); diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowMessageRequestParams.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowMessageRequestParams.java index 1e893bbe067..3341b146701 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowMessageRequestParams.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ShowMessageRequestParams.java @@ -3,6 +3,8 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.remoteclient; +import java.util.List; + public record ShowMessageRequestParams( /** * The message type. See {@link MessageType} @@ -18,5 +20,5 @@ public record ShowMessageRequestParams( * The message action items to present. * */ - MessageActionItem[] actions) { + List actions) { } diff --git a/keyext.client.python/keyapi/__init__.py b/keyext.client.python/keyapi/__init__.py index 4a913b39b2e..4807ec5ee59 100644 --- a/keyext.client.python/keyapi/__init__.py +++ b/keyext.client.python/keyapi/__init__.py @@ -1,8 +1,8 @@ -from keyapi.rpc import * +import abc +from abc import abstractmethod, ABCMeta + +from keyapi.keydata import * +from keyapi.rpc import LspEndpoint + -class KeyStub(JsonRPCHandler): - def __init__(self, input, output): - super().__init__(input, output) - def list_examples(self): - return self._send("examples/list", []) diff --git a/keyext.client.python/keyapi/keydata.py b/keyext.client.python/keyapi/keydata.py new file mode 100644 index 00000000000..dfeaf76f7c7 --- /dev/null +++ b/keyext.client.python/keyapi/keydata.py @@ -0,0 +1,533 @@ +from __future__ import annotations +import enum +import abc +import typing +from abc import abstractmethod, ABCMeta + + +class ExampleDesc: + """""" + + name: str + """""" + + description: str + """""" + + def __init__(self, name, description): + self.name = name + self.description = description + + +class ProofScriptCommandDesc: + """""" + + def __init__(self, ): + pass + + +class ProofMacroDesc: + """""" + + name: str + """""" + + category: str + """""" + + description: str + """""" + + scriptCommandName: str + """""" + + def __init__(self, name, category, description, scriptCommandName): + self.name = name + self.category = category + self.description = description + self.scriptCommandName = scriptCommandName + + +class TraceValue(enum.Enum): + """""" + + Off = None + Message = None + All = None + + +class SetTraceParams: + """""" + + value: TraceValue + """""" + + def __init__(self, value): + self.value = value + + +class EnvironmentId: + """""" + + envId: str + """""" + + def __init__(self, envId): + self.envId = envId + + +class ProofId: + """""" + + env: EnvironmentId + """""" + + proofId: str + """""" + + def __init__(self, env, proofId): + self.env = env + self.proofId = proofId + + +class TreeNodeDesc: + """""" + + def __init__(self, ): + pass + + +class TreeNodeId: + """""" + + id: str + """""" + + def __init__(self, id): + self.id = id + + +class NodeId: + """""" + + proofId: ProofId + """""" + + nodeId: int + """""" + + def __init__(self, proofId, nodeId): + self.proofId = proofId + self.nodeId = nodeId + + +class PrintOptions: + """""" + + unicode: bool + """""" + + width: int + """""" + + indentation: int + """""" + + pure: bool + """""" + + termLabels: bool + """""" + + def __init__(self, unicode, width, indentation, pure, termLabels): + self.unicode = unicode + self.width = width + self.indentation = indentation + self.pure = pure + self.termLabels = termLabels + + +class NodeTextId: + """""" + + nodeId: NodeId + """""" + + nodeTextId: int + """""" + + def __init__(self, nodeId, nodeTextId): + self.nodeId = nodeId + self.nodeTextId = nodeTextId + + +class NodeTextDesc: + """""" + + id: NodeTextId + """""" + + result: str + """""" + + def __init__(self, id, result): + self.id = id + self.result = result + + +class TermActionId: + """""" + + nodeId: NodeId + """""" + + pio: str + """""" + + id: str + """""" + + def __init__(self, nodeId, pio, id): + self.nodeId = nodeId + self.pio = pio + self.id = id + + +class TermActionKind(enum.Enum): + """""" + + BuiltIn = None + Script = None + Macro = None + Taclet = None + + +class TermActionDesc: + """""" + + commandId: TermActionId + """""" + + displayName: str + """""" + + description: str + """""" + + category: str + """""" + + kind: TermActionKind + """""" + + def __init__(self, commandId, displayName, description, category, kind): + self.commandId = commandId + self.displayName = displayName + self.description = description + self.category = category + self.kind = kind + + +class List: + """""" + + def __init__(self, ): + pass + + +class SortDesc: + """""" + + string: str + """""" + + documentation: str + """""" + + extendsSorts: List + """""" + + anAbstract: bool + """""" + + s: str + """""" + + def __init__(self, string, documentation, extendsSorts, anAbstract, s): + self.string = string + self.documentation = documentation + self.extendsSorts = extendsSorts + self.anAbstract = anAbstract + self.s = s + + +class FunctionDesc: + """""" + + name: str + """""" + + sort: str + """""" + + retSort: SortDesc + """""" + + argSorts: List + """""" + + rigid: bool + """""" + + unique: bool + """""" + + skolemConstant: bool + """""" + + def __init__(self, name, sort, retSort, argSorts, rigid, unique, skolemConstant): + self.name = name + self.sort = sort + self.retSort = retSort + self.argSorts = argSorts + self.rigid = rigid + self.unique = unique + self.skolemConstant = skolemConstant + + +class ContractId: + """""" + + envId: EnvironmentId + """""" + + contractId: int + """""" + + def __init__(self, envId, contractId): + self.envId = envId + self.contractId = contractId + + +class ContractDesc: + """""" + + contractId: ContractId + """""" + + name: str + """""" + + displayName: str + """""" + + typeName: str + """""" + + htmlText: str + """""" + + plainText: str + """""" + + def __init__(self, contractId, name, displayName, typeName, htmlText, plainText): + self.contractId = contractId + self.name = name + self.displayName = displayName + self.typeName = typeName + self.htmlText = htmlText + self.plainText = plainText + + +class LoadParams: + """""" + + keyFile: str + """""" + + javaFile: str + """""" + + classPath: List + """""" + + bootClassPath: str + """""" + + includes: List + """""" + + def __init__(self, keyFile, javaFile, classPath, bootClassPath, includes): + self.keyFile = keyFile + self.javaFile = javaFile + self.classPath = classPath + self.bootClassPath = bootClassPath + self.includes = includes + + + +class ProblemDefinition: + """""" + + sorts: List + """""" + + functions: List + """""" + + predicates: List + """""" + + antecTerms: List + """""" + + succTerms: List + """""" + + def __init__(self, sorts, functions, predicates, antecTerms, succTerms): + self.sorts = sorts + self.functions = functions + self.predicates = predicates + self.antecTerms = antecTerms + self.succTerms = succTerms + + +class LogTraceParams: + """""" + + messag: str + """""" + + verbose: str + """""" + + def __init__(self, messag, verbose): + self.messag = messag + self.verbose = verbose + + +class MessageType(enum.Enum): + """""" + + Unused = None + Error = None + Warning = None + Info = None + Log = None + Debug = None + + +class ShowMessageParams: + """""" + + type: MessageType + """""" + + message: str + """""" + + def __init__(self, type, message): + self.type = type + self.message = message + + +class ShowMessageRequestParams: + """""" + + type: MessageType + """""" + + message: str + """""" + + actions: List + """""" + + def __init__(self, type, message, actions): + self.type = type + self.message = message + self.actions = actions + + +class MessageActionItem: + """""" + + title: str + """""" + + def __init__(self, title): + self.title = title + + +class Range: + """""" + + start: int + """""" + + end: int + """""" + + def __init__(self, start, end): + self.start = start + self.end = end + + +class ShowDocumentParams: + """""" + + uri: str + """""" + + external: bool + """""" + + takeFocus: bool + """""" + + selection: Range + """""" + + def __init__(self, uri, external, takeFocus, selection): + self.uri = uri + self.external = external + self.takeFocus = takeFocus + self.selection = selection + + +class ShowDocumentResult: + """""" + + success: bool + """""" + + def __init__(self, success): + self.success = success + + +class TaskFinishedInfo: + """""" + + def __init__(self, ): + pass + + +class TaskStartedInfo: + """""" + + def __init__(self, ): + pass + + +KEY_DATA_CLASSES = {"ExampleDesc": ExampleDesc, "ProofScriptCommandDesc": ProofScriptCommandDesc, + "ProofMacroDesc": ProofMacroDesc, "TraceValue": TraceValue, "SetTraceParams": SetTraceParams, + "EnvironmentId": EnvironmentId, "ProofId": ProofId, "TreeNodeDesc": TreeNodeDesc, + "TreeNodeId": TreeNodeId, "NodeId": NodeId, "PrintOptions": PrintOptions, "NodeTextId": NodeTextId, + "NodeTextDesc": NodeTextDesc, "TermActionId": TermActionId, "TermActionKind": TermActionKind, + "TermActionDesc": TermActionDesc, "List": List, "SortDesc": SortDesc, "FunctionDesc": FunctionDesc, + "ContractId": ContractId, "ContractDesc": ContractDesc, "LoadParams": LoadParams, + "ProblemDefinition": ProblemDefinition, "LogTraceParams": LogTraceParams, + "MessageType": MessageType, "ShowMessageParams": ShowMessageParams, + "ShowMessageRequestParams": ShowMessageRequestParams, "MessageActionItem": MessageActionItem, + "Range": Range, "ShowDocumentParams": ShowDocumentParams, "ShowDocumentResult": ShowDocumentResult, + "TaskFinishedInfo": TaskFinishedInfo, "TaskStartedInfo": TaskStartedInfo} diff --git a/keyext.client.python/keyapi/rpc.py b/keyext.client.python/keyapi/rpc.py index 02ba2be474b..c112055e006 100644 --- a/keyext.client.python/keyapi/rpc.py +++ b/keyext.client.python/keyapi/rpc.py @@ -1,10 +1,11 @@ import enum import json -import sys import threading -from io import TextIOWrapper +import typing from typing import Dict +from keyapi import KEY_DATA_CLASSES + JSON_RPC_REQ_FORMAT = "Content-Length: {json_string_len}\r\n\r\n{json_string}" LEN_HEADER = "Content-Length: " TYPE_HEADER = "Content-Type: " @@ -16,16 +17,20 @@ class MyEncoder(json.JSONEncoder): """ def default(self, o): # pylint: disable=E0202 - return o.__dict__ + d = dict(o.__dict__) + d['$type'] = type(o).__name__ + return d class ResponseError(Exception): - def __init__(self, error_code, message): + def __init__(self, error_code, message, data=None): super().__init__(message) self.error_code = error_code + self.data = data class ErrorCodes(enum.Enum): + MethodNotFound = None ParseError = 1 @@ -57,27 +62,27 @@ def send_request(self, message): :param dict message: The message to send. ''' - json_string = json.dumps(message, cls=MyEncoder) + json_string = json.dumps(message , cls=MyEncoder) jsonrpc_req = self.__add_header(json_string) with self.write_lock: - self.stdin.write(jsonrpc_req.encode()) - self.stdin.flush() + self.stdout.write(jsonrpc_req) + self.stdout.flush() - def recv_response(self): + def recv_response(self) -> object: ''' Recives a message. :return: a message ''' - with self.read_lock: + with (self.read_lock): message_size = None while True: # read header - line = self.stdout.readline() + line = self.stdin.readline() if not line: # server quit return None - line = line.decode("utf-8") + # line = line.decode("utf-8") if not line.endswith("\r\n"): raise ResponseError(ErrorCodes.ParseError, "Bad header: missing newline") # remove the "\r\n" @@ -99,12 +104,18 @@ def recv_response(self): if not message_size: raise ResponseError(ErrorCodes.ParseError, "Bad header: missing size") - jsonrpc_res = self.stdout.read(message_size).decode("utf-8") - return json.loads(jsonrpc_res) + jsonrpc_res = self.stdin.read(message_size) # .decode("utf-8") + return json.loads(jsonrpc_res, object_hook=object_decoder) + + +def object_decoder(obj): + if '$type' in obj: + return KEY_DATA_CLASSES[obj["$type"]](**obj) + return obj class LspEndpoint(threading.Thread): - def __init__(self, json_rpc_endpoint: JsonRpcEndpoint, method_callbacks=None, notify_callbacks=None, timeout=2): + def __init__(self, json_rpc_endpoint: JsonRpcEndpoint, method_callbacks=None, notify_callbacks=None, timeout=2000): super().__init__() self.json_rpc_endpoint: JsonRpcEndpoint = json_rpc_endpoint self.notify_callbacks: Dict = notify_callbacks or {} @@ -126,6 +137,7 @@ def stop(self): self.shutdown_flag = True def run(self): + rpc_id = None while not self.shutdown_flag: try: jsonrpc_message = self.json_rpc_endpoint.recv_response() @@ -175,14 +187,14 @@ def send_message(self, method_name, params, id=None): message_dict["params"] = params self.json_rpc_endpoint.send_request(message_dict) - def call_method(self, method_name, **kwargs): + def call_method(self, method_name, args): current_id = self.next_id self.next_id += 1 cond = threading.Condition() self.event_dict[current_id] = cond cond.acquire() - self.send_message(method_name, kwargs, current_id) + self.send_message(method_name, args, current_id) if self.shutdown_flag: return None @@ -196,5 +208,17 @@ def call_method(self, method_name, **kwargs): raise ResponseError(error.get("code"), error.get("message"), error.get("data")) return result - def send_notification(self, method_name, **kwargs): + def send_notification(self, method_name, kwargs): self.send_message(method_name, kwargs) + + +class ServerBase: + def __init__(self, endpoint: LspEndpoint): + self.endpoint = endpoint + + def _call_sync(self, method_name: str, param: typing.List[object]) -> object: + resp = self.endpoint.call_method(method_name, param) + return resp + + def _call_async(self, method_name: str, param: typing.List[object]): + self.endpoint.send_notification(method_name, param) diff --git a/keyext.client.python/keyapi/server.py b/keyext.client.python/keyapi/server.py new file mode 100644 index 00000000000..2940363e1c2 --- /dev/null +++ b/keyext.client.python/keyapi/server.py @@ -0,0 +1,161 @@ +from __future__ import annotations + +import abc +from abc import abstractmethod + +from .keydata import * +from .rpc import ServerBase, LspEndpoint + + +# noinspection PyTypeChecker +class KeyServer(ServerBase): + + def __init__(self, endpoint: LspEndpoint): + super().__init__(endpoint) + + def env_contracts(self, env: EnvironmentId) -> typing.List[ContractDesc]: + """""" + + return self._call_sync("env/contracts", [env]) + + def env_functions(self, env: EnvironmentId) -> typing.List[FunctionDesc]: + """""" + + return self._call_sync("env/functions", [env]) + + def env_openContract(self, contractId: ContractId) -> ProofId: + """""" + + return self._call_sync("env/openContract", [contractId]) + + def env_sorts(self, env: EnvironmentId) -> typing.List[SortDesc]: + """""" + + return self._call_sync("env/sorts", [env]) + + def examples_list(self, ) -> typing.List[ExampleDesc]: + """""" + + return self._call_sync("examples/list", []) + + def goal_actions(self, id: NodeTextId, pos: int) -> typing.List[TermActionDesc]: + """""" + + return self._call_sync("goal/actions", [id, pos]) + + def goal_apply_action(self, id: TermActionId) -> typing.List[TermActionDesc]: + """""" + + return self._call_sync("goal/apply_action", [id]) + + def goal_free(self, id: NodeTextId): + """""" + + return self._call_async("goal/free", [id]) + + def goal_print(self, id: NodeId, options: PrintOptions) -> NodeTextDesc: + """""" + + return self._call_sync("goal/print", [id, options]) + + def loading_load(self, params: LoadParams) -> typing.Union[EnvironmentId, ProofId]: + """""" + + return self._call_sync("loading/load", [params]) + + def loading_loadExample(self, id: str) -> ProofId: + """""" + + return self._call_sync("loading/loadExample", [id]) + + def loading_loadKey(self, content: str) -> ProofId: + """""" + + return self._call_sync("loading/loadKey", [content]) + + def loading_loadProblem(self, problem: ProblemDefinition) -> ProofId: + """""" + + return self._call_sync("loading/loadProblem", [problem]) + + def loading_loadTerm(self, term: str) -> ProofId: + """""" + + return self._call_sync("loading/loadTerm", [term]) + + def meta_available_macros(self, ) -> typing.List[ProofMacroDesc]: + """""" + + return self._call_sync("meta/available_macros", []) + + def meta_available_script_commands(self, ) -> typing.List[ProofScriptCommandDesc]: + """""" + + return self._call_sync("meta/available_script_commands", []) + + def meta_version(self, ) -> str: + """""" + + return self._call_sync("meta/version", []) + + def proofTree_children(self, proof: ProofId, nodeId: TreeNodeId) -> typing.List[TreeNodeDesc]: + """""" + + return self._call_sync("proofTree/children", [proof, nodeId]) + + def proofTree_root(self, id: ProofId) -> TreeNodeDesc: + """""" + + return self._call_sync("proofTree/root", [id]) + + def proofTree_subtree(self, proof: ProofId, nodeId: TreeNodeId) -> typing.List[TreeNodeDesc]: + """""" + + return self._call_sync("proofTree/subtree", [proof, nodeId]) + + def server_exit(self, ): + """""" + + return self._call_async("server/exit", []) + + def server_setTrace(self, params: SetTraceParams): + """""" + + return self._call_async("server/setTrace", [params]) + + def server_shutdown(self, ) -> bool: + """""" + + return self._call_sync("server/shutdown", []) + + +class Client(abc.ABCMeta): + @abstractmethod + def client_logTrace(self, params: LogTraceParams): + """""" + + pass + + @abstractmethod + def client_sayHello(self, e: str): + """""" + + pass + + @abstractmethod + def client_showDocument(self, params: ShowDocumentParams) -> ShowDocumentResult: + """""" + + pass + + @abstractmethod + def client_sm(self, params: ShowMessageParams): + """""" + + pass + + @abstractmethod + def client_userResponse(self, params: ShowMessageRequestParams) -> MessageActionItem: + """""" + + pass diff --git a/keyext.client.python/main.py b/keyext.client.python/main.py index a5cb45321d8..d12fadea42a 100644 --- a/keyext.client.python/main.py +++ b/keyext.client.python/main.py @@ -1,5 +1,7 @@ import socket -from keyapi import KeyStub, KeyClient +from keyapi import LspEndpoint, LoadParams +from keyapi.server import KeyServer +from keyapi.rpc import JsonRpcEndpoint if __name__ == "__main__": target = ("localhost", 5151) @@ -8,6 +10,18 @@ s.connect(target) input = s.makefile("r", newline="\r\n") output = s.makefile("w", newline="\r\n") - stub = KeyStub(input, output) - stub.client = KeyClient() - print(stub.list_examples()) + + rpc_endpoint = JsonRpcEndpoint(input, output) + endpoint = LspEndpoint(rpc_endpoint) + endpoint.start() + + key = KeyServer(endpoint) + print(key.meta_version()) + ex = key.examples_list() + print(ex) + + proofHandle = key.loading_load( + LoadParams("/home/weigl/work/key/key.ui/examples/standard_key/prop_log/contraposition.key", + None, None, None, None)) + + print(proofHandle) diff --git a/keyext.client.python/rwtest.py b/keyext.client.python/rwtest.py deleted file mode 100644 index 312532192dc..00000000000 --- a/keyext.client.python/rwtest.py +++ /dev/null @@ -1,402 +0,0 @@ -import enum -import abc -import typing -from abc import abstractmethod - - -class ExampleDesc: - """""" - - name: str - description: str - - -class ProofScriptCommandDesc: - """""" - - -class ProofMacroDesc: - """""" - - name: str - category: str - description: str - scriptCommandName: str - - -class TraceValue(enum.Enum): - """""" - - Off = None - Message = None - All = None - - -class SetTraceParams: - """""" - - value: TraceValue - - -class EnvironmentId: - """""" - - envId: str - - -class ProofId: - """""" - - env: EnvironmentId - proofId: str - - -class TreeNodeDesc: - """""" - - -class TreeNodeId: - """""" - - id: str - - -class NodeId: - """""" - - proofId: ProofId - nodeId: int - - -class PrintOptions: - """""" - - unicode: bool - width: int - indentation: int - pure: bool - termLabels: bool - - -class NodeTextId: - """""" - - nodeId: NodeId - nodeTextId: int - - -class NodeTextDesc: - """""" - - id: NodeTextId - result: str - - -class TermActionId: - """""" - - nodeId: NodeId - pio: str - id: str - - -class TermActionKind(enum.Enum): - """""" - - BuiltIn = None - Script = None - Macro = None - Taclet = None - - -class TermActionDesc: - """""" - - commandId: TermActionId - displayName: str - description: str - category: str - kind: TermActionKind - - -class List: - """""" - - -class SortDesc: - """""" - - string: str - documentation: str - extendsSorts: List - anAbstract: bool - s: str - - -class FunctionDesc: - """""" - - name: str - sort: str - retSort: SortDesc - argSorts: List - rigid: bool - unique: bool - skolemConstant: bool - - -class ContractId: - """""" - - envId: EnvironmentId - contractId: int - - -class ContractDesc: - """""" - - contractId: ContractId - name: str - displayName: str - typeName: str - htmlText: str - plainText: str - - -class LoadParams: - """""" - - keyFile: str - javaFile: str - classPath: List - bootClassPath: str - includes: List - - -class ProblemDefinition: - """""" - - sorts: List - functions: List - predicates: List - antecTerms: List - succTerms: List - - -class LogTraceParams: - """""" - - messag: str - verbose: str - - -class MessageType(enum.Enum): - """""" - - Unused = None - Error = None - Warning = None - Info = None - Log = None - Debug = None - - -class ShowMessageParams: - """""" - - type: MessageType - message: str - - - -class ShowMessageRequestParams: - """""" - - type: MessageType - message: str - actions: typing.List[MessageActionItem] - - -class MessageActionItem: - """""" - - title: str - - -class Range: - """""" - - start: int - end: int - - -class ShowDocumentParams: - """""" - - uri: str - external: bool - takeFocus: bool - selection: Range - - -class ShowDocumentResult: - """""" - - success: bool - - -class TaskFinishedInfo: - """""" - - -class TaskStartedInfo: - """""" - - -class KeyServer(): - def env_contracts(self, arg0: EnvironmentId) -> typing.List[ContractDesc]: - """""" - - return self.rpc.call_sync("env/contracts", ) - - def env_functions(self, arg0: EnvironmentId) -> typing.List[FunctionDesc]: - """""" - - return self.rpc.call_sync("env/functions", ) - - def env_openContract(self, arg0: ContractId) -> ProofId: - """""" - - return self.rpc.call_sync("env/openContract", ) - - def env_sorts(self, arg0: EnvironmentId) -> typing.List[SortDesc]: - """""" - - return self.rpc.call_sync("env/sorts", ) - - def examples_list(self, ) -> typing.List[ExampleDesc]: - """""" - - return self.rpc.call_sync("examples/list", ) - - def goal_actions(self, arg0: NodeTextId, arg1: int) -> typing.List[TermActionDesc]: - """""" - - return self.rpc.call_sync("goal/actions", ) - - def goal_apply_action(self, arg0: TermActionId) -> typing.List[TermActionDesc]: - """""" - - return self.rpc.call_sync("goal/apply_action", ) - - def goal_free(self, arg0: NodeTextId): - """""" - - return self.rpc.call_async("goal/free", ) - - def goal_print(self, arg0: NodeId, arg1: PrintOptions) -> NodeTextDesc: - """""" - - return self.rpc.call_sync("goal/print", ) - - def loading_load(self, arg0: LoadParams) -> typing.Union[EnvironmentId, ProofId]: - """""" - - return self.rpc.call_sync("loading/load", ) - - def loading_loadExample(self, arg0: str) -> ProofId: - """""" - - return self.rpc.call_sync("loading/loadExample", ) - - def loading_loadKey(self, arg0: str) -> ProofId: - """""" - - return self.rpc.call_sync("loading/loadKey", ) - - def loading_loadProblem(self, arg0: ProblemDefinition) -> ProofId: - """""" - - return self.rpc.call_sync("loading/loadProblem", ) - - def loading_loadTerm(self, arg0: str) -> ProofId: - """""" - - return self.rpc.call_sync("loading/loadTerm", ) - - def meta_available_macros(self, ) -> typing.List[ProofMacroDesc]: - """""" - - return self.rpc.call_sync("meta/available_macros", ) - - def meta_available_script_commands(self, ) -> typing.List[ProofScriptCommandDesc]: - """""" - - return self.rpc.call_sync("meta/available_script_commands", ) - - def meta_version(self, ) -> str: - """""" - - return self.rpc.call_sync("meta/version", ) - - def proofTree_children(self, arg0: ProofId, arg1: TreeNodeId) -> typing.List[TreeNodeDesc]: - """""" - - return self.rpc.call_sync("proofTree/children", ) - - def proofTree_root(self, arg0: ProofId) -> TreeNodeDesc: - """""" - - return self.rpc.call_sync("proofTree/root", ) - - def proofTree_subtree(self, arg0: ProofId, arg1: TreeNodeId) -> typing.List[TreeNodeDesc]: - """""" - - return self.rpc.call_sync("proofTree/subtree", ) - - def server_exit(self, ): - """""" - - return self.rpc.call_async("server/exit", ) - - def server_setTrace(self, arg0: SetTraceParams): - """""" - - return self.rpc.call_async("server/setTrace", ) - - def server_shutdown(self, ) -> bool: - """""" - - return self.rpc.call_sync("server/shutdown", ) - - -class Client(abc.ABCMeta): - @abstractmethod - def client_logTrace(self, arg0: LogTraceParams): - """""" - - pass - - @abstractmethod - def client_sayHello(self, arg0: str): - """""" - - pass - - @abstractmethod - def client_showDocument(self, arg0: ShowDocumentParams) -> ShowDocumentResult: - """""" - - pass - - @abstractmethod - def client_sm(self, arg0: ShowMessageParams): - """""" - - pass - - @abstractmethod - def client_userResponse(self, arg0: ShowMessageRequestParams) -> MessageActionItem: - """""" - - pass diff --git a/server.py b/server.py new file mode 100644 index 00000000000..7ad103297fe --- /dev/null +++ b/server.py @@ -0,0 +1,159 @@ +from __future__ import annotations +from .keydata import * +from .rpc import ServerBase + +import enum +import abc +import typing +from abc import abstractmethod +class KeyServer(ServerBase): + + def __init__(self, endpoint : LspEndpoint): + super().__init__(endpoint) + + def env_contracts(self, env : EnvironmentId) -> typing.List[ContractDesc]: + """""" + + return self._call_sync("env/contracts", [env]) + + def env_functions(self, env : EnvironmentId) -> typing.List[FunctionDesc]: + """""" + + return self._call_sync("env/functions", [env]) + + def env_openContract(self, contractId : ContractId) -> ProofId: + """""" + + return self._call_sync("env/openContract", [contractId]) + + def env_sorts(self, env : EnvironmentId) -> typing.List[SortDesc]: + """""" + + return self._call_sync("env/sorts", [env]) + + def examples_list(self, ) -> typing.List[ExampleDesc]: + """""" + + return self._call_sync("examples/list", []) + + def goal_actions(self, id : NodeTextId, pos : int) -> typing.List[TermActionDesc]: + """""" + + return self._call_sync("goal/actions", [id , pos]) + + def goal_apply_action(self, id : TermActionId) -> typing.List[TermActionDesc]: + """""" + + return self._call_sync("goal/apply_action", [id]) + + def goal_free(self, id : NodeTextId): + """""" + + return self._call_async("goal/free", [id]) + + def goal_print(self, id : NodeId, options : PrintOptions) -> NodeTextDesc: + """""" + + return self._call_sync("goal/print", [id , options]) + + def loading_load(self, params : LoadParams) -> typing.Union[EnvironmentId, ProofId]: + """""" + + return self._call_sync("loading/load", [params]) + + def loading_loadExample(self, id : str) -> ProofId: + """""" + + return self._call_sync("loading/loadExample", [id]) + + def loading_loadKey(self, content : str) -> ProofId: + """""" + + return self._call_sync("loading/loadKey", [content]) + + def loading_loadProblem(self, problem : ProblemDefinition) -> ProofId: + """""" + + return self._call_sync("loading/loadProblem", [problem]) + + def loading_loadTerm(self, term : str) -> ProofId: + """""" + + return self._call_sync("loading/loadTerm", [term]) + + def meta_available_macros(self, ) -> typing.List[ProofMacroDesc]: + """""" + + return self._call_sync("meta/available_macros", []) + + def meta_available_script_commands(self, ) -> typing.List[ProofScriptCommandDesc]: + """""" + + return self._call_sync("meta/available_script_commands", []) + + def meta_version(self, ) -> str: + """""" + + return self._call_sync("meta/version", []) + + def proofTree_children(self, proof : ProofId, nodeId : TreeNodeId) -> typing.List[TreeNodeDesc]: + """""" + + return self._call_sync("proofTree/children", [proof , nodeId]) + + def proofTree_root(self, id : ProofId) -> TreeNodeDesc: + """""" + + return self._call_sync("proofTree/root", [id]) + + def proofTree_subtree(self, proof : ProofId, nodeId : TreeNodeId) -> typing.List[TreeNodeDesc]: + """""" + + return self._call_sync("proofTree/subtree", [proof , nodeId]) + + def server_exit(self, ): + """""" + + return self._call_async("server/exit", []) + + def server_setTrace(self, params : SetTraceParams): + """""" + + return self._call_async("server/setTrace", [params]) + + def server_shutdown(self, ) -> bool: + """""" + + return self._call_sync("server/shutdown", []) + +class Client(abc.ABCMeta): + @abstractmethod + def client_logTrace(self, params : LogTraceParams): + """""" + + pass + + @abstractmethod + def client_sayHello(self, e : str): + """""" + + pass + + @abstractmethod + def client_showDocument(self, params : ShowDocumentParams) -> ShowDocumentResult: + """""" + + pass + + @abstractmethod + def client_sm(self, params : ShowMessageParams): + """""" + + pass + + @abstractmethod + def client_userResponse(self, params : ShowMessageRequestParams) -> MessageActionItem: + """""" + + pass + From e89f9eb7ff4b78a9142a96bbd2979b6b4d221239 Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Fri, 3 Nov 2023 17:58:48 +0100 Subject: [PATCH 08/35] add throwable adapter --- .../org/keyproject/key/api/StartServer.java | 2 +- .../key/api/adapters/KeyAdapter.java | 20 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java b/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java index 9cbfeb7ac70..53293b35680 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java @@ -151,7 +151,7 @@ public void run() { public static void configureJson(GsonBuilder gsonBuilder) { gsonBuilder.registerTypeHierarchyAdapter(Object.class, new GenericSerializer()); gsonBuilder.registerTypeAdapter(File.class, new KeyAdapter.FileTypeAdapter()); - + gsonBuilder.registerTypeAdapter(Throwable.class, new KeyAdapter.ThrowableAdapter()); } public static Launcher launch(OutputStream out, InputStream in, KeyApiImpl keyApi) { diff --git a/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java b/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java index ecd1e93371b..53cd4cec09c 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java @@ -80,19 +80,31 @@ public JsonElement serialize(Function src, Type typeOfSrc, } } + public static class ThrowableAdapter implements JsonSerializer { + @Override + public JsonElement serialize(Throwable src, Type typeOfSrc, + JsonSerializationContext context) { + var obj = new JsonObject(); + obj.add("$class", context.serialize(src.getClass().getSimpleName())); + obj.add("message", context.serialize(src.getMessage())); + obj.add("cause", context.serialize(src.getCause())); + return obj; + } + } + /* * class IdentifiableTypeAdapter implements JsonSerializer, * JsonDeserializer { * * @Override - * public Identifiable deserialize(JsonElement json, Type typeOfT, - * JsonDeserializationContext context) throws JsonParseException { + * public Identifiable deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext + * context) throws JsonParseException { * return (Identifiable) find(json.getAsString()); * } * * @Override - * public JsonElement serialize(Identifiable src, Type typeOfSrc, - * JsonSerializationContext context) { + * public JsonElement serialize(Identifiable src, Type typeOfSrc, JsonSerializationContext + * context) { * insert(src); * return context.serialize(src.identification()); * } From 47f6efeec3b8db47379c7ce74f814ffdd7cd7d4a Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Sat, 18 Nov 2023 19:43:59 +0100 Subject: [PATCH 09/35] spotless and merge errors --- keyext.api.doc/src/main/java/Metamodel.java | 19 +++++++++++++++++-- .../keyproject/key/api/TermActionUtil.java | 1 - .../key/api/data/KeyIdentifications.java | 2 -- .../key/api/data/MacroStatistic.java | 7 +++---- .../org/keyproject/key/api/data/NodeDesc.java | 1 - .../key/api/data/ProblemDefinition.java | 1 - .../keyproject/key/api/remoteapi/EnvApi.java | 3 +++ .../keyproject/key/api/remoteapi/GoalApi.java | 3 +++ .../key/api/remoteapi/ProofApi.java | 3 +++ .../key/api/remoteapi/ProofTreeApi.java | 3 +++ .../key/api/remoteclient/ClientApi.java | 3 +++ .../ilkd/key/gui/testgen/TGInfoDialog.java | 9 ++++++--- 12 files changed, 41 insertions(+), 14 deletions(-) diff --git a/keyext.api.doc/src/main/java/Metamodel.java b/keyext.api.doc/src/main/java/Metamodel.java index db639174e71..f4afddc14e6 100644 --- a/keyext.api.doc/src/main/java/Metamodel.java +++ b/keyext.api.doc/src/main/java/Metamodel.java @@ -16,7 +16,9 @@ public record KeyApi( List types) { } - sealed interface Endpoint { + sealed + + interface Endpoint { String name(); String documentation(); @@ -48,12 +50,16 @@ record ClientNotification(String name, String documentation, List args } record Field(String name, /* Type */ String type, String documentation) { + Field(String name, String type) { this(name, type, ""); } } - public sealed interface Type { + public sealed + + + interface Type { default String kind() { return getClass().getSimpleName(); } @@ -63,6 +69,7 @@ default String kind() { String name(); } + enum BuiltinType implements Type { INT, LONG, STRING, BOOL, DOUBLE; @@ -70,31 +77,39 @@ enum BuiltinType implements Type { public String documentation() { return "built-in data type"; } + } record ListType(Type type, String documentation) implements Type { + @Override public String name() { return type().name() + "[]"; } + } record ObjectType(String typeName, List fields, String documentation) implements Type { + @Override public String name() { return typeName; } + } public record EitherType(Type a, Type b, String documentation) implements Type { + @Override public String name() { return "either"; } + } public record EnumType(String typeName, List values, String documentation) implements Type { + @Override public String name() { return typeName; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java b/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java index fe0e9f2f182..c65a93d677a 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java @@ -20,7 +20,6 @@ import org.key_project.util.collection.ImmutableList; import org.key_project.util.collection.ImmutableSLList; -import org.checkerframework.checker.nullness.qual.NonNull; import org.jspecify.annotations.NonNull; import org.keyproject.key.api.data.KeyIdentifications; import org.keyproject.key.api.data.KeyIdentifications.NodeTextId; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/KeyIdentifications.java b/keyext.api/src/main/java/org/keyproject/key/api/data/KeyIdentifications.java index c474050a00a..cbb5e21f12d 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/KeyIdentifications.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/KeyIdentifications.java @@ -12,9 +12,7 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; -import org.checkerframework.checker.nullness.qual.NonNull; import org.jspecify.annotations.NonNull; -import org.keyproject.key.api.NodeTextId; import org.keyproject.key.api.internal.NodeText; /** diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/MacroStatistic.java b/keyext.api/src/main/java/org/keyproject/key/api/data/MacroStatistic.java index 50cd735bd82..06aa618634d 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/MacroStatistic.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/MacroStatistic.java @@ -9,12 +9,11 @@ * @author Alexander Weigl * @version 1 (13.10.23) */ -public record MacroStatistic(KeyIdentifications.ProofId proofId, String macroId, boolean cancelled, - int appliedRules, +public record MacroStatistic(KeyIdentifications.ProofId proofId, String macroId, int appliedRules, int closedGoals) { public static MacroStatistic from(KeyIdentifications.ProofId proofId, ProofMacroFinishedInfo info) { - return new MacroStatistic(proofId, info.getMacro().getName(), info.isCancelled(), - info.getAppliedRules(), info.getClosedGoals()); + return new MacroStatistic(proofId, info.getMacro().getName(), info.getAppliedRules(), + info.getClosedGoals()); } } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java index bba069cc757..5c72cfec2c9 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/NodeDesc.java @@ -5,7 +5,6 @@ import java.util.List; -import org.checkerframework.checker.nullness.qual.Nullable; import org.jspecify.annotations.Nullable; /** diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/ProblemDefinition.java b/keyext.api/src/main/java/org/keyproject/key/api/data/ProblemDefinition.java index a6b6fb2765b..0eafbefcf89 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/ProblemDefinition.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/ProblemDefinition.java @@ -6,7 +6,6 @@ import java.util.List; -import org.checkerframework.checker.nullness.qual.Nullable; import org.jspecify.annotations.Nullable; /** diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvApi.java index c70fe8a15d2..dc8fb6362c6 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/EnvApi.java @@ -3,6 +3,9 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.remoteapi; +import java.util.List; +import java.util.concurrent.CompletableFuture; + import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; import org.keyproject.key.api.data.ContractDesc; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java index be120b488de..68a9e263708 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/GoalApi.java @@ -3,6 +3,9 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.remoteapi; +import java.util.List; +import java.util.concurrent.CompletableFuture; + import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofApi.java index 80131fa143c..8d0fc27154d 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofApi.java @@ -3,6 +3,9 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.remoteapi; +import java.util.List; +import java.util.concurrent.CompletableFuture; + import de.uka.ilkd.key.proof.Statistics; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofTreeApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofTreeApi.java index 459adfce637..cd4aa88b146 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofTreeApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/ProofTreeApi.java @@ -3,6 +3,9 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.remoteapi; +import java.util.List; +import java.util.concurrent.CompletableFuture; + import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; import org.keyproject.key.api.data.KeyIdentifications.ProofId; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java index 9d5159cb901..fe34e79927e 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/ClientApi.java @@ -3,6 +3,9 @@ * SPDX-License-Identifier: GPL-2.0-only */ package org.keyproject.key.api.remoteclient; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nullable; + import de.uka.ilkd.key.prover.TaskFinishedInfo; import de.uka.ilkd.key.prover.TaskStartedInfo; diff --git a/keyext.ui.testgen/src/main/java/de/uka/ilkd/key/gui/testgen/TGInfoDialog.java b/keyext.ui.testgen/src/main/java/de/uka/ilkd/key/gui/testgen/TGInfoDialog.java index af40c2d8c20..6fce99da565 100644 --- a/keyext.ui.testgen/src/main/java/de/uka/ilkd/key/gui/testgen/TGInfoDialog.java +++ b/keyext.ui.testgen/src/main/java/de/uka/ilkd/key/gui/testgen/TGInfoDialog.java @@ -37,9 +37,12 @@ public void actionPerformed(ActionEvent e) { // This method delegates the request only to the UserInterfaceControl // which implements the functionality. No functionality is allowed in this method body! new Thread(() -> { - MainWindow.getInstance().getMediator().getUI().getProofControl() - .stopAndWaitAutoMode(); - ThreadUtilities.invokeOnEventQueue(() -> exitButton.setEnabled(true)); + try { + MainWindow.getInstance().getMediator().getUI().getProofControl() + .stopAndWaitAutoMode(); + ThreadUtilities.invokeOnEventQueue(() -> exitButton.setEnabled(true)); + } catch (InterruptedException ignore) { + } }).start(); } }; From f37f26b288ac0a3cb5a146d602a5f7ac31956754 Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Wed, 26 Jun 2024 16:11:29 +0200 Subject: [PATCH 10/35] fix after refactoring + spotless --- .../src/main/java/org/keyproject/key/api/TermActionUtil.java | 2 +- .../java/org/keyproject/key/api/adapters/KeyAdapter.java | 3 ++- .../main/java/org/keyproject/key/api/data/FunctionDesc.java | 5 +++-- .../src/main/java/org/keyproject/key/api/data/SortDesc.java | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java b/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java index c65a93d677a..4e121061d84 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/TermActionUtil.java @@ -9,7 +9,6 @@ import de.uka.ilkd.key.control.KeYEnvironment; import de.uka.ilkd.key.control.ProofControl; -import de.uka.ilkd.key.logic.Name; import de.uka.ilkd.key.logic.PosInOccurrence; import de.uka.ilkd.key.macros.ProofMacro; import de.uka.ilkd.key.macros.ProofMacroFacade; @@ -17,6 +16,7 @@ import de.uka.ilkd.key.proof.Goal; import de.uka.ilkd.key.rule.*; +import org.key_project.logic.Name; import org.key_project.util.collection.ImmutableList; import org.key_project.util.collection.ImmutableSLList; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java b/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java index 53cd4cec09c..bd423049595 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java @@ -7,9 +7,10 @@ import java.io.IOException; import java.lang.reflect.Type; -import de.uka.ilkd.key.logic.op.Function; import de.uka.ilkd.key.macros.ProofMacro; +import org.key_project.logic.op.Function; + import com.google.gson.*; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/FunctionDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/FunctionDesc.java index df3e8f285ef..c693a179524 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/FunctionDesc.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/FunctionDesc.java @@ -5,7 +5,8 @@ import java.util.List; -import de.uka.ilkd.key.logic.op.Function; +import org.key_project.logic.op.Function; + /** * @author Alexander Weigl @@ -15,7 +16,7 @@ public record FunctionDesc(String name, String sort, SortDesc retSort, List Date: Sun, 20 Oct 2024 16:12:52 +0200 Subject: [PATCH 11/35] wip --- keyext.api.doc/build.gradle | 4 +- keyext.api.doc/src/main/java/DocGen.java | 190 ------------------ .../org/key_project/key/api/doc/DocGen.java | 117 +++++++++++ .../key/api/doc}/ExtractMetaData.java | 166 ++++++++------- .../key_project/key/api/doc}/Metamodel.java | 7 + .../key/api/doc/PythonGenerator.java} | 25 ++- .../org/keyproject/key/api/KeyApiImpl.java | 14 +- .../key/api/data/TermActionDesc.java | 22 +- .../keyproject/key/api/data/package-info.java | 8 + .../org/keyproject/key/api/package-info.java | 8 + .../key/api/remoteapi/package-info.java | 8 + .../key/api/remoteclient/package-info.java | 8 + 12 files changed, 292 insertions(+), 285 deletions(-) delete mode 100644 keyext.api.doc/src/main/java/DocGen.java create mode 100644 keyext.api.doc/src/main/java/org/key_project/key/api/doc/DocGen.java rename keyext.api.doc/src/main/java/{ => org/key_project/key/api/doc}/ExtractMetaData.java (63%) rename keyext.api.doc/src/main/java/{ => org/key_project/key/api/doc}/Metamodel.java (86%) rename keyext.api.doc/src/main/java/{PythionGenerator.java => org/key_project/key/api/doc/PythonGenerator.java} (91%) create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/data/package-info.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/package-info.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteapi/package-info.java create mode 100644 keyext.api/src/main/java/org/keyproject/key/api/remoteclient/package-info.java diff --git a/keyext.api.doc/build.gradle b/keyext.api.doc/build.gradle index 84a1789e01f..0c275f1d19e 100644 --- a/keyext.api.doc/build.gradle +++ b/keyext.api.doc/build.gradle @@ -1,5 +1,5 @@ dependencies { implementation(project(":keyext.api")) - implementation("com.github.javaparser:javaparser-core:3.25.5") - implementation("com.github.javaparser:javaparser-symbol-solver-core:3.25.5") + implementation("com.github.javaparser:javaparser-symbol-solver-core:3.26.1") + implementation("info.picocli:picocli:4.7.6") } \ No newline at end of file diff --git a/keyext.api.doc/src/main/java/DocGen.java b/keyext.api.doc/src/main/java/DocGen.java deleted file mode 100644 index 8ab51393898..00000000000 --- a/keyext.api.doc/src/main/java/DocGen.java +++ /dev/null @@ -1,190 +0,0 @@ -/* 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 */ -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.Comparator; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -/** - * @author Alexander Weigl - * @version 1 (29.10.23) - */ -public class DocGen implements Supplier { - private final Metamodel.KeyApi metamodel; - private PrintWriter out; - private final StringWriter target = new StringWriter(); - - public DocGen(Metamodel.KeyApi metamodel) { - this.metamodel = metamodel; - } - - @Override - public String get() { - try (var out = new PrintWriter(target)) { - this.out = out; - printHeader(); - - out.format("## Types%n"); - metamodel.types() - .stream().sorted(Comparator.comparing(Metamodel.Type::name)) - .forEach(this::printType); - - out.format("## Endpoints%n"); - metamodel.endpoints() - .stream().sorted(Comparator.comparing(Metamodel.Endpoint::name)) - .forEach(this::endpoints); - printFooter(); - } - return target.toString(); - } - - private void printFooter() { - - } - - private void printHeader() { - - - } - - private void endpoints(Metamodel.Endpoint endpoint) { - // out.format("## Group: %s%n%n", getJsonSegment(typeDeclaration)); - // out.println(getJavaDoc(typeDeclaration)); - // out.println("\n"); - - var direction = ""; - if (endpoint instanceof Metamodel.ServerRequest sr) - direction = "client -> server"; - else if (endpoint instanceof Metamodel.ClientRequest sr) - direction = "server -> client"; - else if (endpoint instanceof Metamodel.ServerNotification sr) - direction = "client ~~> server"; - else if (endpoint instanceof Metamodel.ClientNotification sr) - direction = "server ~~> client"; - - out.format("### %s (`%s`) %n%n", endpoint.name(), direction); - out.format("```%n"); - var args = endpoint.args(); - final var a = args.stream() - .map(it -> "%s : %s".formatted(it.name(), it.type())) - .collect(Collectors.joining(", ")); - if (endpoint instanceof Metamodel.ServerRequest sr) { - out.format("Server.%s( %s ) -> %s%n", endpoint.name(), a, sr.returnType().name()); - } else if (endpoint instanceof Metamodel.ClientRequest sr) - out.format("Client.%s( %s ) -> %s%n", endpoint.name(), a, sr.returnType().name()); - else if (endpoint instanceof Metamodel.ServerNotification sr) - out.format("Server.%s( %s ) **async**%n", endpoint.name(), a); - else if (endpoint instanceof Metamodel.ClientNotification sr) - out.format("Client.%s( %s ) **async**%n", endpoint.name(), a); - out.format("```%n"); - - out.println(endpoint.documentation()); - out.println(); - } - - private void printType(Metamodel.Type type) { - out.format("### Type: %s%n", type.name()); - if (type instanceof Metamodel.ObjectType ot) { - out.format(""" - ``` - type %s { - %s - } - ``` - """.formatted(type.name(), - ot.fields().stream().sorted(Comparator.comparing(Metamodel.Field::name)) - .map(it -> "\t%s : %s".formatted(it.name(), it.type())) - .collect(Collectors.joining("\n")))); - } - - if (type instanceof Metamodel.EnumType et) { - out.format(""" - ``` - enum %s { %s } - ``` - """.formatted(type.name(), String.join(", ", et.values()))); - out.format(type.documentation()); - } - out.format(type.documentation()); - out.println(); - } - - /* - * private static String getCallName (MethodDeclaration m, TypeDeclaration < ?>td){ - * var annotationExpr = m.getAnnotationByName("JsonNotification").or( - * () -> m.getAnnotationByName("JsonRequest")) - * .orElseThrow(); - * - * var segment = getJsonSegment(td) + "/"; - * String name = ""; - * - * if (annotationExpr.isMarkerAnnotationExpr()) { - * name = m.getNameAsString(); - * } else if (annotationExpr.isSingleMemberAnnotationExpr()) { - * var sma = annotationExpr.asSingleMemberAnnotationExpr(); - * name = sma.getMemberValue().asLiteralStringValueExpr().getValue(); - * } else { - * var ne = annotationExpr.asNormalAnnotationExpr(); - * for (MemberValuePair pair : ne.getPairs()) { - * switch (pair.getName().asString()) { - * case "value": - * name = pair.getValue().asLiteralStringValueExpr().getValue(); - * break; - * case "useSegment": - * if (!pair.getValue().asBooleanLiteralExpr().getValue()) { - * segment = ""; - * } - * } - * } - * } - * return segment + name; - * } - * - * @Nonnull - * private static String getJavaDoc (NodeWithJavadoc < ? > typeDeclaration){ - * if (typeDeclaration.getJavadoc().isPresent()) { - * final var javadoc = typeDeclaration.getJavadoc().get(); - * return javadoc.getDescription().toText() - * + "\n\n" - * + javadoc.getBlockTags().stream().map(it -> "* " + it.toText()) - * .collect(Collectors.joining("\n")); - * } - * return ""; - * } - * - * private static String type (MethodDeclaration method){ - * if (method.getAnnotationByName("JsonNotification").isPresent()) - * return "notification"; - * if (method.getAnnotationByName("JsonRequest").isPresent()) - * return "request"; - * return ""; - * } - * - * private static boolean isExported (MethodDeclaration method){ - * return method.getAnnotationByName("JsonNotification").isPresent() - * || method.getAnnotationByName("JsonRequest").isPresent(); - * } - * - * private void printHeader () { - * out.format("# KeY-API%n%n"); - * } - * - * private void printFooter () { - * } - * - * - * /* - * private static boolean hasJsonSegment(TypeDeclaration it) { - * return it.getAnnotationByName("JsonSegment").isPresent(); - * } - * - * private static String getJsonSegment(TypeDeclaration it) { - * var ae = it.getAnnotationByName("JsonSegment").get(); - * return ae.asSingleMemberAnnotationExpr().getMemberValue() - * .asLiteralStringValueExpr().getValue(); - * } - */ - -} diff --git a/keyext.api.doc/src/main/java/org/key_project/key/api/doc/DocGen.java b/keyext.api.doc/src/main/java/org/key_project/key/api/doc/DocGen.java new file mode 100644 index 00000000000..d793e0fa08a --- /dev/null +++ b/keyext.api.doc/src/main/java/org/key_project/key/api/doc/DocGen.java @@ -0,0 +1,117 @@ +/* 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.key.api.doc;/* + * 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 + */ + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Comparator; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * @author Alexander Weigl + * @version 1 (29.10.23) + */ +public class DocGen implements Supplier { + private final Metamodel.KeyApi metamodel; + private PrintWriter out; + + public DocGen(Metamodel.KeyApi metamodel) { + this.metamodel = metamodel; + } + + @Override + public String get() { + final StringWriter target = new StringWriter(); + try (var out = new PrintWriter(target)) { + this.out = out; + printHeader(); + + out.format("## Types%n"); + metamodel.types() + .stream().sorted(Comparator.comparing(Metamodel.Type::name)) + .forEach(this::printType); + + out.format("## Endpoints%n"); + metamodel.endpoints() + .stream().sorted(Comparator.comparing(Metamodel.Endpoint::name)) + .forEach(this::endpoints); + printFooter(); + } + return target.toString(); + } + + private void printFooter() { + + } + + private void printHeader() { + + + } + + private void endpoints(Metamodel.Endpoint endpoint) { + var direction = switch (endpoint) { + case Metamodel.ServerRequest sr -> "client -> server"; + case Metamodel.ClientRequest sr -> "server -> client"; + case Metamodel.ServerNotification sr -> "client ~~> server"; + case Metamodel.ClientNotification sr -> "server ~~> client"; + }; + + out.format("### %s (`%s`) %n%n", endpoint.name(), direction); + out.format("```%n"); + var args = endpoint.args(); + final var a = args.stream() + .map(it -> "%s : %s".formatted(it.name(), it.type())) + .collect(Collectors.joining(", ")); + switch (endpoint) { + case Metamodel.ServerRequest sr -> + out.format("Server.%s( %s ) -> %s%n", endpoint.name(), a, sr.returnType().name()); + case Metamodel.ClientRequest sr -> + out.format("Client.%s( %s ) -> %s%n", endpoint.name(), a, sr.returnType().name()); + case Metamodel.ServerNotification _ -> + out.format("Server.%s( %s ) **async**%n", endpoint.name(), a); + case Metamodel.ClientNotification _ -> + out.format("Client.%s( %s ) **async**%n", endpoint.name(), a); + default -> { + } + } + out.format("```%n"); + + out.println(endpoint.documentation()); + out.println(); + } + + private void printType(Metamodel.Type type) { + out.format("### Type: %s%n", type.name()); + if (type instanceof Metamodel.ObjectType ot) { + out.format(""" + ``` + type %s { + %s + } + ``` + """.formatted(type.name(), + ot.fields().stream().sorted(Comparator.comparing(Metamodel.Field::name)) + .map(it -> "\t%s : %s".formatted(it.name(), it.type())) + .collect(Collectors.joining("\n")))); + } + + if (type instanceof Metamodel.EnumType et) { + out.format(""" + ``` + enum %s { %s } + ``` + """.formatted(type.name(), String.join(", ", et.values()))); + out.format(type.documentation()); + } + out.format(type.documentation()); + out.println(); + } +} diff --git a/keyext.api.doc/src/main/java/ExtractMetaData.java b/keyext.api.doc/src/main/java/org/key_project/key/api/doc/ExtractMetaData.java similarity index 63% rename from keyext.api.doc/src/main/java/ExtractMetaData.java rename to keyext.api.doc/src/main/java/org/key_project/key/api/doc/ExtractMetaData.java index d053b9268d3..86a9bb7e945 100644 --- a/keyext.api.doc/src/main/java/ExtractMetaData.java +++ b/keyext.api.doc/src/main/java/org/key_project/key/api/doc/ExtractMetaData.java @@ -1,46 +1,90 @@ /* 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.key.api.doc;/* + * 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 + */ + import java.io.File; import java.io.IOException; -import java.io.PrintWriter; import java.lang.reflect.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; +import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.function.Supplier; import de.uka.ilkd.key.proof.Proof; -import com.github.javaparser.JavaParser; +import com.github.javaparser.ParseProblemException; import com.github.javaparser.ParserConfiguration; -import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.body.TypeDeclaration; import com.github.javaparser.ast.comments.Comment; import com.github.javaparser.ast.nodeTypes.NodeWithJavadoc; -import com.google.gson.*; +import com.github.javaparser.javadoc.Javadoc; +import com.github.javaparser.javadoc.description.JavadocDescription; +import com.github.javaparser.utils.SourceRoot; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializer; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.keyproject.key.api.remoteapi.KeyApi; import org.keyproject.key.api.remoteclient.ClientApi; -import sun.misc.Unsafe; +import picocli.CommandLine; +import picocli.CommandLine.Option; /** * @author Alexander Weigl * @version 1 (14.10.23) */ -public class ExtractMetaData { - private static PrintWriter out; +@NullMarked +@CommandLine.Command(name = "gendoc", + mixinStandardHelpOptions = true, + version = "gendoc 1.0", + description = "Generates the documentation for key.api") +public class ExtractMetaData implements Callable { + private final List endpoints = new LinkedList<>(); + private final List types = new LinkedList<>(); + private final Metamodel.KeyApi keyApi = new Metamodel.KeyApi(endpoints, types); + private SourceRoot sourceRoot = new SourceRoot(Paths.get(".")); + - private static final List endpoints = new LinkedList<>(); - private static final List types = new LinkedList<>(); - private static final Metamodel.KeyApi keyApi = new Metamodel.KeyApi(endpoints, types); + @Option(names = { "-s", "--source" }, description = "Source folder for getting JavaDoc") + private @Nullable Path source = Paths.get("keyext.api", "src", "main", "java"); + + @Option(names = { "-o", "--output" }, description = "Output folder") + private Path output = Paths.get("out"); public static void main(String[] args) { + int exitCode = new CommandLine(new ExtractMetaData()).execute(args); + System.exit(exitCode); + } + + @Override + public Integer call() throws IOException { + if (source != null) { + ParserConfiguration config = new ParserConfiguration(); + config.setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_21); + config.setAttributeComments(true); + config.setLexicalPreservationEnabled(false); + config.setStoreTokens(false); + config.setIgnoreAnnotationsWhenAttributingComments(true); + config.setDoNotAssignCommentsPrecedingEmptyLines(true); + sourceRoot = new SourceRoot(source, config); + } + for (Method method : KeyApi.class.getMethods()) { addServerEndpoint(method); } @@ -49,17 +93,20 @@ public static void main(String[] args) { addClientEndpoint(method); } + Files.createDirectories(output); + runGenerator("api.meta.json", (a) -> () -> getGson().toJson(a)); runGenerator("api.meta.md", DocGen::new); - runGenerator("keydata.py", PythionGenerator.PyDataGen::new); - runGenerator("server.py", PythionGenerator.PyApiGen::new); + runGenerator("keydata.py", PythonGenerator.PyDataGen::new); + runGenerator("server.py", PythonGenerator.PyApiGen::new); + + return 0; } - private static void runGenerator(String target, - Function> api) { + private void runGenerator(String target, Function> api) { try { var n = api.apply(keyApi); - Files.writeString(Paths.get(target), n.get()); + Files.writeString(output.resolve(target), n.get()); } catch (IOException e) { e.printStackTrace(); } @@ -68,19 +115,16 @@ private static void runGenerator(String target, private static Gson getGson() { return new GsonBuilder() .setPrettyPrinting() - .registerTypeAdapter(Type.class, new JsonSerializer() { - @Override - public JsonElement serialize(Metamodel.Type src, Type typeOfSrc, - JsonSerializationContext context) { + .registerTypeAdapter(Type.class, + (JsonSerializer) (src, typeOfSrc, context) -> { JsonObject json = (JsonObject) context.serialize(src); json.addProperty("kind", src.kind()); return json; - } - }) + }) .create(); } - private static void addServerEndpoint(Method method) { + private void addServerEndpoint(Method method) { var jsonSegment = method.getDeclaringClass().getAnnotation(JsonSegment.class); if (jsonSegment == null) return; @@ -118,28 +162,32 @@ private static void addServerEndpoint(Method method) { throw new IllegalStateException(); } - private static String findDocumentation(Method method) { - // TODO get compilation, get type, find method, getJavaDocComment() - return ""; + private String findDocumentation(Method method) { + return findCompilationUnit(method.getDeclaringClass()) + .map(it -> it.getMethodsByName(method.getName())) + .map(List::getFirst) + .flatMap(NodeWithJavadoc::getJavadoc) + .map(Javadoc::getDescription) + .map(JavadocDescription::toText) + .orElse(""); } - private static List translate(Parameter[] parameters) { - return Arrays.stream(parameters).map(ExtractMetaData::translate).toList(); + private List translate(Parameter[] parameters) { + return Arrays.stream(parameters).map(this::translate).toList(); } - private static Metamodel.Argument translate(Parameter parameter) { + private Metamodel.Argument translate(Parameter parameter) { var type = getOrFindType(parameter.getType()).name(); return new Metamodel.Argument(parameter.getName(), type); } - private static Metamodel.Type getOrFindType(Class type) { + private Metamodel.Type getOrFindType(Class type) { System.out.println(type); if (type == CompletableFuture.class) { return getOrFindType(type.getTypeParameters()[0].getClass()); } - if (type == Unsafe.class || type == Class.class || type == Constructor.class - || type == Proof.class) { + if (type == Class.class || type == Constructor.class || type == Proof.class) { throw new IllegalStateException("Forbidden class reached!"); } @@ -178,7 +226,7 @@ private static Metamodel.Type getOrFindType(Class type) { return a; } - private static Metamodel.Type createType(Class type) { + private Metamodel.Type createType(Class type) { final var documentation = findDocumentation(type); if (type.isEnum()) return new Metamodel.EnumType(type.getSimpleName(), @@ -194,34 +242,24 @@ private static Metamodel.Type createType(Class type) { return obj; } - private static String findDocumentation(Class type) { - var parser = initJavaParser(); - var fileName = findFileForType(type); - - if (Files.exists(fileName)) { - try { - return parser.parse(fileName).getResult().flatMap(CompilationUnit::getPrimaryType) - .flatMap(NodeWithJavadoc::getJavadocComment) - .map(Comment::getContent).orElse(""); - } catch (IOException e) { - e.printStackTrace(); - return ""; - } - } else - return ""; + private String findDocumentation(Class type) { + return findCompilationUnit(type) + .flatMap(NodeWithJavadoc::getJavadocComment) + .map(Comment::getContent) + .orElse(""); } - private static Path findFileForType(Class type) { - final var folderString = type.getPackageName().replaceAll("\\.", "/"); - var folder = Paths.get("keyext.api", "src", "main", "java", folderString); - final Class declaringClass = type.getDeclaringClass(); - var fileName = (declaringClass != null ? declaringClass : type).getSimpleName() + ".java"; - var file = folder.resolve(fileName); - return file; + private Optional> findCompilationUnit(Class type) { + try { + return sourceRoot.parse(type.getPackageName(), type.getSimpleName() + ".java") + .getPrimaryType(); + } catch (ParseProblemException e) { + return Optional.empty(); + } } - private static void addClientEndpoint(Method method) { + private void addClientEndpoint(Method method) { var jsonSegment = method.getDeclaringClass().getAnnotation(JsonSegment.class); var segment = jsonSegment.value(); @@ -248,7 +286,7 @@ private static void addClientEndpoint(Method method) { } } - private static String callMethodName(String method, String segment, String userValue, + private static String callMethodName(String method, String segment, @Nullable String userValue, boolean useSegment) { if (!useSegment) { if (userValue == null || userValue.isBlank()) { @@ -265,8 +303,8 @@ private static String callMethodName(String method, String segment, String userV } } - private static Metamodel.Type getOrFindType(Type genericReturnType) { - if (genericReturnType instanceof Class c) + private Metamodel.@Nullable Type getOrFindType(Type genericReturnType) { + if (genericReturnType instanceof Class c) return getOrFindType(c); if (genericReturnType instanceof ParameterizedType pt) { if (Objects.equals(pt.getRawType().getTypeName(), @@ -286,16 +324,4 @@ private static Metamodel.Type getOrFindType(Type genericReturnType) { } return null; } - - static JavaParser initJavaParser() { - ParserConfiguration config = new ParserConfiguration(); - config.setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17_PREVIEW); - config.setAttributeComments(true); - config.setLexicalPreservationEnabled(false); - config.setStoreTokens(false); - config.setIgnoreAnnotationsWhenAttributingComments(true); - config.setDoNotAssignCommentsPrecedingEmptyLines(true); - return new JavaParser(config); - } - } diff --git a/keyext.api.doc/src/main/java/Metamodel.java b/keyext.api.doc/src/main/java/org/key_project/key/api/doc/Metamodel.java similarity index 86% rename from keyext.api.doc/src/main/java/Metamodel.java rename to keyext.api.doc/src/main/java/org/key_project/key/api/doc/Metamodel.java index f4afddc14e6..9762ee1f107 100644 --- a/keyext.api.doc/src/main/java/Metamodel.java +++ b/keyext.api.doc/src/main/java/org/key_project/key/api/doc/Metamodel.java @@ -1,6 +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 org.key_project.key.api.doc;/* + * 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 + */ + import java.util.List; import org.jspecify.annotations.NullMarked; diff --git a/keyext.api.doc/src/main/java/PythionGenerator.java b/keyext.api.doc/src/main/java/org/key_project/key/api/doc/PythonGenerator.java similarity index 91% rename from keyext.api.doc/src/main/java/PythionGenerator.java rename to keyext.api.doc/src/main/java/org/key_project/key/api/doc/PythonGenerator.java index 47faabfc131..3b7c6447377 100644 --- a/keyext.api.doc/src/main/java/PythionGenerator.java +++ b/keyext.api.doc/src/main/java/org/key_project/key/api/doc/PythonGenerator.java @@ -1,6 +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 org.key_project.key.api.doc;/* + * 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 + */ + import java.io.PrintWriter; import java.io.StringWriter; import java.util.Comparator; @@ -12,12 +19,12 @@ * @author Alexander Weigl * @version 1 (29.10.23) */ -public abstract class PythionGenerator implements Supplier { +public abstract class PythonGenerator implements Supplier { protected final Metamodel.KeyApi metamodel; protected PrintWriter out; protected final StringWriter target = new StringWriter(); - public PythionGenerator(Metamodel.KeyApi metamodel) { + public PythonGenerator(Metamodel.KeyApi metamodel) { this.metamodel = metamodel; } @@ -34,8 +41,7 @@ public String get() { protected String asPython(String typeName) { return switch (typeName) { - case "INT" -> "int"; - case "LONG" -> "int"; + case "INT", "LONG" -> "int"; case "STRING" -> "str"; case "BOOL" -> "bool"; case "DOUBLE" -> "float"; @@ -57,8 +63,7 @@ protected String asPython(Metamodel.Type t) { if (t instanceof Metamodel.BuiltinType bt) { return switch (bt) { - case INT -> "int"; - case LONG -> "int"; + case INT, LONG -> "int"; case STRING -> "str"; case BOOL -> "bool"; case DOUBLE -> "float"; @@ -73,7 +78,7 @@ protected Metamodel.Type findType(String typeName) { } - public static class PyApiGen extends PythionGenerator { + public static class PyApiGen extends PythonGenerator { public PyApiGen(Metamodel.KeyApi metamodel) { super(metamodel); } @@ -123,7 +128,7 @@ private void clientEndpoint(Metamodel.Endpoint endpoint) { out.format(" def %s(self, %s):%n", endpoint.name().replace("/", "_"), args); } out.format(" \"\"\"%s\"\"\"%n%n", endpoint.documentation()); - out.format(" pass".formatted(endpoint.name(), "")); + out.format(" pass"); out.println(); out.println(); } @@ -134,7 +139,7 @@ class KeyServer(ServerBase):%n def __init__(self, endpoint : LspEndpoint): super().__init__(endpoint) - """); + """); sorted.forEach(this::serverEndpoint); } @@ -169,7 +174,7 @@ private void serverEndpoint(Metamodel.Endpoint endpoint) { } - public static class PyDataGen extends PythionGenerator { + public static class PyDataGen extends PythonGenerator { public PyDataGen(Metamodel.KeyApi metamodel) { super(metamodel); } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java b/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java index f271fd4e614..8de35f0197f 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/KeyApiImpl.java @@ -389,8 +389,6 @@ public CompletableFuture loadExample(String id) { var proofId = new ProofId(envId, proof.name().toString()); return data.register(proofId, proof); } catch (ProblemLoaderException e) { - if (proof != null) - proof.dispose(); if (env != null) env.dispose(); throw new RuntimeException(e); @@ -442,8 +440,6 @@ public CompletableFuture loadKey(String content) { var proofId = new ProofId(envId, proof.name().toString()); return data.register(proofId, proof); } catch (ProblemLoaderException | IOException e) { - if (proof != null) - proof.dispose(); if (env != null) env.dispose(); throw new RuntimeException(e); @@ -459,8 +455,8 @@ public CompletableFuture loadTerm(String term) { @Override public CompletableFuture> load(LoadParams params) { return CompletableFutures.computeAsync((c) -> { - Proof proof = null; - KeYEnvironment env = null; + Proof proof; + KeYEnvironment env; try { var loader = control.load(JavaProfile.getDefaultProfile(), params.keyFile(), @@ -482,10 +478,6 @@ public CompletableFuture> load(LoadParams params) return Either.forLeft(envId); } } catch (ProblemLoaderException e) { - if (proof != null) - proof.dispose(); - if (env != null) - env.dispose(); throw new RuntimeException(e); } }); @@ -579,6 +571,4 @@ public void showIssueDialog(Collection issues) { super.showIssueDialog(issues); } } - - } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionDesc.java b/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionDesc.java index e3953a6c8bd..1b4184e58dd 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionDesc.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/TermActionDesc.java @@ -6,10 +6,30 @@ import org.keyproject.key.api.data.KeyIdentifications.TermActionId; /** + * This class represents an action that can be executed on a term. + * * @author Alexander Weigl * @version 1 (13.10.23) */ -public record TermActionDesc(TermActionId commandId, String displayName, String description, +public record TermActionDesc( + /** + * Unique identification + */ + TermActionId commandId, + /** + * A string to display to the user. + */ + String displayName, + /** + * Long description of the action for the user. + */ + String description, + /** + * A string identifying a category + */ String category, + /** + * Kind of the action + */ TermActionKind kind) { } diff --git a/keyext.api/src/main/java/org/keyproject/key/api/data/package-info.java b/keyext.api/src/main/java/org/keyproject/key/api/data/package-info.java new file mode 100644 index 00000000000..39ecb9745a4 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/data/package-info.java @@ -0,0 +1,8 @@ +/** + * @author Alexander Weigl + * @version 1 (20.10.24) + */ +@NullMarked +package org.keyproject.key.api.data; + +import org.jspecify.annotations.NullMarked; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/package-info.java b/keyext.api/src/main/java/org/keyproject/key/api/package-info.java new file mode 100644 index 00000000000..c979030e065 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/package-info.java @@ -0,0 +1,8 @@ +/** + * @author Alexander Weigl + * @version 1 (20.10.24) + */ +@NullMarked +package org.keyproject.key.api; + +import org.jspecify.annotations.NullMarked; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/package-info.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/package-info.java new file mode 100644 index 00000000000..ccaca3e03b6 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteapi/package-info.java @@ -0,0 +1,8 @@ +/** + * @author Alexander Weigl + * @version 1 (20.10.24) + */ +@NullMarked +package org.keyproject.key.api.remoteapi; + +import org.jspecify.annotations.NullMarked; diff --git a/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/package-info.java b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/package-info.java new file mode 100644 index 00000000000..2151dd10601 --- /dev/null +++ b/keyext.api/src/main/java/org/keyproject/key/api/remoteclient/package-info.java @@ -0,0 +1,8 @@ +/** + * @author Alexander Weigl + * @version 1 (20.10.24) + */ +@NullMarked +package org.keyproject.key.api.remoteclient; + +import org.jspecify.annotations.NullMarked; From 512eb2bf806ad072fab52bc9480cb2e17afe4b71 Mon Sep 17 00:00:00 2001 From: Alexander Weigl Date: Fri, 22 Nov 2024 22:38:21 +0100 Subject: [PATCH 12/35] start of a simple Java client --- .../org/key_project/key/api/doc/DocGen.java | 4 +- keyext.api/build.gradle | 10 +- .../org/keyproject/key/api/StartServer.java | 4 +- .../key/api/adapters/KeyAdapter.java | 2 +- keyext.api/src/main/resources/logback.xml | 59 ++++ keyext.client.java/build.gradle | 37 +++ .../kit/iti/formal/keyextclientjava/Main.java | 27 ++ .../formal/keyextclientjava/MyKeyClient.java | 165 +++++++++++ .../formal/keyextclientjava/TreeNodeId.java | 11 + .../formal/keyextclientjava/rpc/JsonRPC.java | 107 ++++++++ .../keyextclientjava/rpc/KeyRemote.java | 22 ++ .../formal/keyextclientjava/rpc/RPCLayer.java | 258 ++++++++++++++++++ .../formal/keyextclientjava/hello-view.fxml | 16 ++ .../edu/kit/keyext/client/RPCLayerTest.java | 59 ++++ .../java/edu/kit/keyext/client/Starter.java | 25 ++ settings.gradle | 1 + 16 files changed, 799 insertions(+), 8 deletions(-) create mode 100644 keyext.api/src/main/resources/logback.xml create mode 100644 keyext.client.java/build.gradle create mode 100644 keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/Main.java create mode 100644 keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/MyKeyClient.java create mode 100644 keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/TreeNodeId.java create mode 100644 keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/rpc/JsonRPC.java create mode 100644 keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/rpc/KeyRemote.java create mode 100644 keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/rpc/RPCLayer.java create mode 100644 keyext.client.java/src/main/resources/edu/kit/iti/formal/keyextclientjava/hello-view.fxml create mode 100644 keyext.client.java/src/test/java/edu/kit/keyext/client/RPCLayerTest.java create mode 100644 keyext.client.java/src/test/java/edu/kit/keyext/client/Starter.java diff --git a/keyext.api.doc/src/main/java/org/key_project/key/api/doc/DocGen.java b/keyext.api.doc/src/main/java/org/key_project/key/api/doc/DocGen.java index d793e0fa08a..bab724c68b3 100644 --- a/keyext.api.doc/src/main/java/org/key_project/key/api/doc/DocGen.java +++ b/keyext.api.doc/src/main/java/org/key_project/key/api/doc/DocGen.java @@ -75,9 +75,9 @@ private void endpoints(Metamodel.Endpoint endpoint) { out.format("Server.%s( %s ) -> %s%n", endpoint.name(), a, sr.returnType().name()); case Metamodel.ClientRequest sr -> out.format("Client.%s( %s ) -> %s%n", endpoint.name(), a, sr.returnType().name()); - case Metamodel.ServerNotification _ -> + case Metamodel.ServerNotification ignored -> out.format("Server.%s( %s ) **async**%n", endpoint.name(), a); - case Metamodel.ClientNotification _ -> + case Metamodel.ClientNotification ignored -> out.format("Client.%s( %s ) **async**%n", endpoint.name(), a); default -> { } diff --git a/keyext.api/build.gradle b/keyext.api/build.gradle index bc997708b51..08b6cbda804 100644 --- a/keyext.api/build.gradle +++ b/keyext.api/build.gradle @@ -9,13 +9,17 @@ plugins { description "Verification server interface via JSON-RPC" +application { + mainClass.set("org.keyproject.key.api.StartServer") +} + dependencies { api(project(":key.core")) api(project(":key.ui")) - api("org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.21.1") - implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.websocket.jakarta:0.21.1") - implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.websocket.jakarta:0.21.1") + api("org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.23.1") + implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.websocket.jakarta:0.23.1") + implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.websocket.jakarta:0.23.1") implementation("org.eclipse.jetty.websocket:websocket-javax-server:10.0.16") implementation("info.picocli:picocli:4.7.5") diff --git a/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java b/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java index 53293b35680..ac90f8ad718 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/StartServer.java @@ -149,7 +149,7 @@ public void run() { public static void configureJson(GsonBuilder gsonBuilder) { - gsonBuilder.registerTypeHierarchyAdapter(Object.class, new GenericSerializer()); + // gsonBuilder.registerTypeHierarchyAdapter(Object.class, new GenericSerializer()); gsonBuilder.registerTypeAdapter(File.class, new KeyAdapter.FileTypeAdapter()); gsonBuilder.registerTypeAdapter(Throwable.class, new KeyAdapter.ThrowableAdapter()); } @@ -160,7 +160,7 @@ public static Launcher launch(OutputStream out, InputStream in, KeyAp var launcherBuilder = new Launcher.Builder() .setOutput(out) .setInput(in) - .traceMessages(new PrintWriter(System.err)) + // .traceMessages(new PrintWriter(System.err)) .validateMessages(true); launcherBuilder.configureGson(StartServer::configureJson); diff --git a/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java b/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java index bd423049595..a4f2b433aab 100644 --- a/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java +++ b/keyext.api/src/main/java/org/keyproject/key/api/adapters/KeyAdapter.java @@ -57,7 +57,7 @@ public JsonElement serialize(ProofMacro src, Type typeOfSrc, public static class FileTypeAdapter extends TypeAdapter { @Override public void write(JsonWriter out, File value) throws IOException { - out.value(value.toString()); + out.value(String.valueOf(value)); } @Override diff --git a/keyext.api/src/main/resources/logback.xml b/keyext.api/src/main/resources/logback.xml new file mode 100644 index 00000000000..4957bda9dc6 --- /dev/null +++ b/keyext.api/src/main/resources/logback.xml @@ -0,0 +1,59 @@ + + + + + + + + + ${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 + + + + + + + + + + + System.err + + [%relative] %highlight(%-5level) %cyan(%logger{0}): %msg %n + + + TRACE + + + + + + + + diff --git a/keyext.client.java/build.gradle b/keyext.client.java/build.gradle new file mode 100644 index 00000000000..50d4e4fcbc2 --- /dev/null +++ b/keyext.client.java/build.gradle @@ -0,0 +1,37 @@ +plugins { + id 'java' + id 'application' + id 'org.javamodularity.moduleplugin' version '1.8.12' + id 'org.openjfx.javafxplugin' version '0.1.0' +} + +repositories { + mavenCentral() +} + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +application { + mainClass = 'edu.kit.iti.formal.keyextclientjava.Main' +} + +javafx { + version = "23.0.2" + modules = [ 'javafx.controls' ] +} + +dependencies { + implementation project(":keyext.api") + + implementation('org.controlsfx:controlsfx:11.2.1') + implementation('org.kordamp.ikonli:ikonli-javafx:12.3.1') + implementation('org.kordamp.ikonli:ikonli-fontawesome5-pack:12.3.1') + implementation('org.kordamp.bootstrapfx:bootstrapfx-core:0.4.0') + implementation('com.google.code.gson:gson:2.11.0') +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/Main.java b/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/Main.java new file mode 100644 index 00000000000..1fb11b468d7 --- /dev/null +++ b/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/Main.java @@ -0,0 +1,27 @@ +/* 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 edu.kit.iti.formal.keyextclientjava; + +import java.io.IOException; +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.stage.Stage; + +public class Main { + public static class HelloApplication extends Application { + @Override + public void start(Stage stage) throws IOException { + Scene scene = new Scene(new MyKeyClient().root, 320, 240); + stage.setTitle("KeY Demo"); + stage.setScene(scene); + stage.show(); + } + + } + + public static void main(String[] args) { + Application.launch(HelloApplication.class, args); + } +} diff --git a/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/MyKeyClient.java b/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/MyKeyClient.java new file mode 100644 index 00000000000..39d87aec8f7 --- /dev/null +++ b/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/MyKeyClient.java @@ -0,0 +1,165 @@ +/* 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 edu.kit.iti.formal.keyextclientjava; + +import java.io.IOException; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.io.PrintWriter; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; +import javafx.geometry.Orientation; +import javafx.scene.control.*; +import javafx.scene.layout.BorderPane; +import javafx.stage.FileChooser; + +import de.uka.ilkd.key.proof.io.ProblemLoaderException; +import de.uka.ilkd.key.prover.TaskFinishedInfo; +import de.uka.ilkd.key.prover.TaskStartedInfo; + +import edu.kit.iti.formal.keyextclientjava.rpc.KeyRemote; +import edu.kit.iti.formal.keyextclientjava.rpc.RPCLayer; +import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer; +import org.keyproject.key.api.KeyApiImpl; +import org.keyproject.key.api.StartServer; +import org.keyproject.key.api.data.KeyIdentifications; +import org.keyproject.key.api.data.LoadParams; +import org.keyproject.key.api.remoteapi.KeyApi; +import org.keyproject.key.api.remoteapi.PrintOptions; +import org.keyproject.key.api.remoteclient.*; +import org.kordamp.ikonli.fontawesome5.FontAwesomeRegular; +import org.kordamp.ikonli.javafx.FontIcon; + +public class MyKeyClient { + public static final String JAR_FILE = ""; + private final ToolBar toolbar = new ToolBar(); + private final Label txtStatus = new Label("Yeah!"); + public final BorderPane root = new BorderPane(); + private final TreeView tnid = new TreeView<>(); + private final TextArea txtSequentView = new TextArea(); + + private final FileChooser fc = new FileChooser(); + private final KeyApi keyApi; + private KeyIdentifications.ProofId loadedProof; + + public MyKeyClient() throws IOException { + // region toolbar + var btnOpen = new Button("Open", new FontIcon(FontAwesomeRegular.FOLDER_OPEN)); + btnOpen.setOnAction(actionEvent -> openFile()); + toolbar.getItems().setAll(btnOpen, new Separator(Orientation.VERTICAL)); + // endregion + + var splitCenter = new SplitPane(tnid, txtSequentView); + splitCenter.setDividerPositions(.2); + root.setTop(toolbar); + root.setCenter(splitCenter); + root.setBottom(txtStatus); + + // var startKey = ForkJoinPool.commonPool().submit(this::startKey); + + PipedInputStream inClient = new PipedInputStream(); + PipedOutputStream outClient = new PipedOutputStream(); + PipedInputStream inServer = new PipedInputStream(); + PipedOutputStream outServer = new PipedOutputStream(); + + inClient.connect(outServer); + outClient.connect(inServer); + + KeyApiImpl impl = new KeyApiImpl(); + Launcher serverLauncher = StartServer.launch(outServer, inServer, impl); + impl.setClientApi(serverLauncher.getRemoteProxy()); + + var client = new SimpleClient(); + Launcher clientLauncher = new Launcher.Builder() + .setLocalService(client) + .setRemoteInterfaces(Collections.singleton(KeyApi.class)) + .setInput(inClient) + .setOutput(outClient) + .configureGson(StartServer::configureJson) + .traceMessages(new PrintWriter(System.err)) + .create(); + + Logger logger = Logger.getLogger(StreamMessageProducer.class.getName()); + logger.setLevel(Level.SEVERE); + + clientLauncher.startListening(); + serverLauncher.startListening(); + + this.keyApi = clientLauncher.getRemoteProxy(); + } + + private Object startKey() throws IOException { + return new KeyRemote(RPCLayer.startWithCLI(JAR_FILE)); + } + + private void openFile() { + var sel = fc.showOpenDialog(null); + if (sel != null) { + try { + loadedProof = keyApi.load( + new LoadParams(sel, null, null, null, null)) + .get().getRight(); + var root = keyApi.root(loadedProof).get(); + var sequent = + keyApi.print(root.nodeid(), new PrintOptions(true, 80, 4, true, false)) + .get(); + txtSequentView.setText(sequent.result()); + } catch (ProblemLoaderException e) { + throw new RuntimeException(e); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + + } + } + + private class SimpleClient implements ClientApi { + @Override + public void sayHello(String e) { + + } + + @Override + public void logTrace(LogTraceParams params) { + + } + + @Override + public void showMessage(ShowMessageParams params) { + + } + + @Override + public CompletableFuture userResponse(ShowMessageRequestParams params) { + return null; + } + + @Override + public CompletableFuture showDocument(ShowDocumentParams params) { + return null; + } + + @Override + public void taskFinished(TaskFinishedInfo info) { + + } + + @Override + public void taskProgress(int position) { + + } + + @Override + public void taskStarted(TaskStartedInfo info) { + + } + } +} diff --git a/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/TreeNodeId.java b/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/TreeNodeId.java new file mode 100644 index 00000000000..d79f384505b --- /dev/null +++ b/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/TreeNodeId.java @@ -0,0 +1,11 @@ +/* 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 edu.kit.iti.formal.keyextclientjava; + +/** + * @author Alexander Weigl + * @version 1 (22.11.24) + */ +public record TreeNodeId() { +} diff --git a/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/rpc/JsonRPC.java b/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/rpc/JsonRPC.java new file mode 100644 index 00000000000..adb642c758c --- /dev/null +++ b/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/rpc/JsonRPC.java @@ -0,0 +1,107 @@ +/* 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 edu.kit.iti.formal.keyextclientjava.rpc; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.jspecify.annotations.Nullable; + +/** + * @author Alexander Weigl + * @version 1 (24.11.24) + */ +public class JsonRPC { + public static final String CONTENT_LENGTH = "Content-Length:"; + private static final Gson gson = new Gson(); + + public static String createNotification(String method, Object... args) { + return createRequest(null, method, args); + } + + public static String createRequest(@Nullable String id, String method, Object... args) { + JsonArray params = gson.toJsonTree(args).getAsJsonArray(); + return createRequest(id, method, params); + } + + public static String createRequest(@Nullable String id, String method, JsonArray params) { + var jsonObject = new JsonObject(); + jsonObject.addProperty("jsonrpc", "2.0"); + if (id != null) { + jsonObject.addProperty("id", id); + } + + jsonObject.addProperty("method", method); + jsonObject.add("params", params); + + return jsonObject.toString(); + } + + public static String createResponse(String id, Object result) { + return createResponse(id, gson.toJsonTree(result), null); + } + + public static String createErrorReponse(String id, int code, String message, + @Nullable Object data) { + return createResponse(id, null, createError(code, message, data)); + } + + public static String createResponse(String id, @Nullable JsonElement result, + @Nullable JsonObject error) { + if (result != null && error != null) { + throw new IllegalArgumentException("result and error at the same time is invalid"); + } + + if (!(result != null || error != null)) { + throw new IllegalArgumentException("at least one of result and error must be not null"); + } + + var jsonObject = new JsonObject(); + jsonObject.addProperty("jsonrpc", "2.0"); + jsonObject.addProperty("id", id); + + if (result != null) { + jsonObject.add("result", result); + } + if (error != null) { + jsonObject.add("error", error); + } + + return jsonObject.toString(); + } + + /** + * 5.1 Error object + * When a rpc call encounters an error, the Response Object MUST contain the error member with a + * value that is a Object with the following members: + *

+ * code + * A Number that indicates the error type that occurred. + * This MUST be an integer. + * message + * A String providing a short description of the error. + * The message SHOULD be limited to a concise single sentence. + * data + * A Primitive or Structured value that contains additional information about the error. + * This may be omitted. + * The value of this member is defined by the Server (e.g. detailed error information, nested + * errors etc.). + * + * @return + */ + public static JsonObject createError(int code, String message, @Nullable Object data) { + var jsonObject = new JsonObject(); + jsonObject.addProperty("code", code); + jsonObject.addProperty("message", message); + if (data != null) { + jsonObject.add("data", gson.toJsonTree(data)); + } + return jsonObject; + } + + public static String addHeader(String response) { + return "%s %d\r\n\r\n%s\r\n".formatted(CONTENT_LENGTH, response.length(), response); + } +} diff --git a/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/rpc/KeyRemote.java b/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/rpc/KeyRemote.java new file mode 100644 index 00000000000..71c49088273 --- /dev/null +++ b/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/rpc/KeyRemote.java @@ -0,0 +1,22 @@ +/* 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 edu.kit.iti.formal.keyextclientjava.rpc; + + +/** + * @author Alexander Weigl + * @version 1 (22.11.24) + */ +public class KeyRemote { + private final RPCLayer layer; + + public KeyRemote(RPCLayer rpcLayer) { + this.layer = rpcLayer; + } + + public String meta_version() { + final var obj = layer.callSync("meta/version"); + return obj.get("result").getAsString(); + } +} diff --git a/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/rpc/RPCLayer.java b/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/rpc/RPCLayer.java new file mode 100644 index 00000000000..569b8a091f9 --- /dev/null +++ b/keyext.client.java/src/main/java/edu/kit/iti/formal/keyextclientjava/rpc/RPCLayer.java @@ -0,0 +1,258 @@ +/* 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 edu.kit.iti.formal.keyextclientjava.rpc; + +import java.io.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.jspecify.annotations.Nullable; + +/** + * @author Alexander Weigl + * @version 1 (22.11.24) + */ +public final class RPCLayer { + private final BufferedReader incoming; + private final Writer outgoing; + + private @Nullable Thread threadListener; + private @Nullable Thread threadMessageHandling; + + private final ConcurrentHashMap waiting = new ConcurrentHashMap<>(); + public final ArrayBlockingQueue incomingMessages = new ArrayBlockingQueue<>(16); + + private final AtomicInteger idCounter = new AtomicInteger(); + + + public RPCLayer(Reader incoming, Writer outgoing) { + this.incoming = new BufferedReader(incoming); + this.outgoing = outgoing; + } + + public static RPCLayer startWithCLI(String jarFile) throws IOException { + var pbuilder = new ProcessBuilder("java", "-jar", jarFile); + var p = pbuilder.start(); + var incoming = new InputStreamReader(p.getInputStream()); + var outgoing = new OutputStreamWriter(p.getOutputStream()); + return new RPCLayer(incoming, outgoing); + } + + public JsonObject waitForResponse(String id) throws InterruptedException, ExecutionException { + var cond = waiting.get(id); + var r = cond.get(); + waiting.remove(id); + return r; + } + + public JsonObject callSync(String methodName, Object... args) { + var id = nextId(); + waiting.put(id, new SimpleFuture()); + try { + sendAsync(id, methodName, args); + var response = waitForResponse(id); + return handleError(response); + } catch (InterruptedException | IOException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + private JsonObject handleError(JsonObject response) { + if (response.get("error") != null) { + throw new RuntimeException("Request did not succeed " + response.get("error")); + } + return response; + } + + public void callAsync(String methodName, Object... args) throws IOException { + var id = nextId(); + sendAsync(id, methodName, args); + } + + private String nextId() { + return "" + idCounter.getAndIncrement(); + } + + private void sendAsync(String id, String methodName, Object[] args) throws IOException { + var json = JsonRPC.createRequest(id, methodName, args); + final var str = JsonRPC.addHeader(json); + outgoing.write(str); + outgoing.flush(); + } + + public void start() { + threadListener = + new Thread(new JsonStreamListener(incoming, incomingMessages), "JSON Message Reader"); + threadMessageHandling = new Thread(this::handler, "JSON Message Handler"); + threadListener.start(); + threadMessageHandling.start(); + } + + private void handler() { + try { + while (true) { + var obj = incomingMessages.poll(1, TimeUnit.MINUTES); + if (obj == null) { + continue; + } + if (obj.get("id") != null) { + var id = obj.get("id").getAsString(); + var syncObj = waiting.get(id); + syncObj.put(obj); + } else { + // TODO handle notification + System.out.println("Notification received"); + System.out.println(obj); + } + } + } catch (InterruptedException ignored) { + } + } + + public void dispose() { + if (threadListener != null) { + threadListener.interrupt(); + } + } + + public static class JsonStreamListener implements Runnable { + private final char[] CLENGTH = (JsonRPC.CONTENT_LENGTH + " ").toCharArray(); + private final PushbackReader incoming; + private final ArrayBlockingQueue incomingMessageQueue; + /** + * Internal buffer for reading efficient. + */ + private char[] buf = new char[4 * 1024]; + + public JsonStreamListener(Reader incoming, ArrayBlockingQueue queue) { + this.incoming = new PushbackReader(new BufferedReader(incoming)); // ensure bufferness + this.incomingMessageQueue = queue; + } + + @Override + public void run() { + try { + while (true) { + final var message = readMessage(); + if (message == null) + break; + final var jsonObject = JsonParser.parseString(message).getAsJsonObject(); + incomingMessageQueue.add(jsonObject); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + protected void processIncomingMessage(String message) { + + } + + + public @Nullable String readMessage() throws IOException { + expectedContentLength(); + final var len = readLength() + 2; // also read \r\n, + + if (len == 2) {// EOF reached + return null; + } + + if (buf.length <= len) { // reallocate more if necessary + buf = new char[len]; + } + + int count = incoming.read(buf, 0, len); + assert count == len; + consumeCRNL(); + return new String(buf, 0, count).trim(); + } + + private int readLength() throws IOException { + int c; + int len = 0; + do { + c = incoming.read(); + // if (Character.isWhitespace(c)) continue; + if (c == -1) + return 0; + if (c == '\r' || c == '\n') + break; + if (Character.isDigit(c)) { + len = len * 10 + c - '0'; + } else { + throw new IllegalStateException("Expected: a digit, but got '%c'".formatted(c)); + } + } while (c != -1); + c = incoming.read(); // consume the '\n' + assert c == '\n'; + return len; + } + + private void expectedContentLength() throws IOException { + for (var e : CLENGTH) { + int c = incoming.read(); + if (c == -1) + return; + if (e != c) { + throw new IllegalStateException("Expected: '%c', but got '%c'".formatted(e, c)); + } + } + } + + private void consumeCRNL() throws IOException { + int c = incoming.read(); + if (c == '\r') { + c = incoming.read(); + assert c == '\n'; + } else { + incoming.unread(c); + } + } + } +} + + +class SimpleFuture implements Future { + private final CountDownLatch latch = new CountDownLatch(1); + private JsonObject value; + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return value != null; + } + + @Override + public JsonObject get() throws InterruptedException, ExecutionException { + latch.await(); + return value; + } + + @Override + public JsonObject get(long timeout, TimeUnit unit) + throws InterruptedException, TimeoutException { + if (latch.await(timeout, unit)) { + return value; + } else { + throw new TimeoutException(); + } + } + + void put(JsonObject value) { + this.value = value; + latch.countDown(); + } + +} diff --git a/keyext.client.java/src/main/resources/edu/kit/iti/formal/keyextclientjava/hello-view.fxml b/keyext.client.java/src/main/resources/edu/kit/iti/formal/keyextclientjava/hello-view.fxml new file mode 100644 index 00000000000..982318240d9 --- /dev/null +++ b/keyext.client.java/src/main/resources/edu/kit/iti/formal/keyextclientjava/hello-view.fxml @@ -0,0 +1,16 @@ + + + + + + + + + + + + +