-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add generic Serialize
and Deserialize
operators to IOUtils
.
#53
Comments
Created here a test with service loader: https://github.com/afonsonf/CommunityModules Also, is there any code formatting tool to use in this project? |
@afonsonf Looking at your prototype, I wonder if we should combine this loader mechanism with the existing loader for module overrides? For example, we could enhance TLC's module loader to load module overrides depending on constant parameters passed to the operator: ...
Inv ==
Serialize("An ordinary TLA+ string", "file.txt", "TXT", [charset|-> "UTF-8", append |-> TRUE]) When processing this spec at startup, |
Uhm, that seems more complex to develop What about creating a new module called serializers, and in there have all the serializers. The abstract serializer could be something like: serialize(value, dest, serializer, options) ==
case serializer = "RAW" -> RAWSerializer(value, dest, options)
[] serializer = "TXT" -> TXTSerializer(value, dest, options)
[] OTHER -> FALSE And then each serializer could have their implementation written in TLA, and if required, also an override in Serializer.java. What do you think? |
The TLA+ definition of The implementation of what I proposed above should be fairly straightforward. It should boil down to extending the annotation and annotation processing. I could implement a prototype ~next week. |
I have here yet another poc: https://github.com/afonsonf/CommunityModules/tree/poc2 For the annotation extension I'm still not sure how would you relate in the specprocessor the parameter of the operator and the annotation field. Not sure about extending the annotation only because the serializer, however if it is designed in a way that more new operators can used it, it would be nice. |
Counterproposal at lemmy/tlaplus@2249366 How two module overrides for the same operator would look like: diff --git a/modules/IOUtils.tla b/modules/IOUtils.tla
index ef7487b..4f47864 100644
--- a/modules/IOUtils.tla
+++ b/modules/IOUtils.tla
@@ -10,6 +10,15 @@ IOSerialize(val, absoluteFilename, compress) == TRUE
IODeserialize(absoluteFilename, compressed) == CHOOSE val : TRUE
+Serialize(value, dest, options) ==
+ (* value: TLA+ value to be serialized. *)
+ (* dest: Destination to serialize to such as a file or URL. *)
+ (* options: Record of serializer-specific options. *)
+ CHOOSE r \in [exitValue : Int, stdout : STRING, stderr : STRING] : TRUE
+
+Deserialize(src, options) ==
+ CHOOSE val : TRUE
+
----------------------------------------------------------------------------
IOExec(command) ==
diff --git a/modules/tlc2/overrides/IOUtils.java b/modules/tlc2/overrides/IOUtils.java
index 8bd44fd..0f4e336 100644
--- a/modules/tlc2/overrides/IOUtils.java
+++ b/modules/tlc2/overrides/IOUtils.java
@@ -35,8 +35,14 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import tla2sany.semantic.ExprOrOpArgNode;
import tlc2.output.EC;
+import tlc2.tool.EvalControl;
import tlc2.tool.EvalException;
+import tlc2.tool.TLCState;
+import tlc2.tool.coverage.CostModel;
+import tlc2.tool.impl.Tool;
+import tlc2.util.Context;
import tlc2.value.IValue;
import tlc2.value.ValueInputStream;
import tlc2.value.ValueOutputStream;
@@ -52,7 +58,7 @@ import util.UniqueString;
public class IOUtils {
@TLAPlusOperator(identifier = "IODeserialize", module = "IOUtils", warn = false)
- public static final IValue deserialize(final StringValue absolutePath, final BoolValue compress)
+ public static final IValue ioDeserialize(final StringValue absolutePath, final BoolValue compress)
throws IOException {
final ValueInputStream vis = new ValueInputStream(new File(absolutePath.val.toString()), compress.val);
try {
@@ -63,7 +69,7 @@ public class IOUtils {
}
@TLAPlusOperator(identifier = "IOSerialize", module = "IOUtils", warn = false)
- public static final IValue serialize(final IValue value, final StringValue absolutePath, final BoolValue compress)
+ public static final IValue ioSerialize(final IValue value, final StringValue absolutePath, final BoolValue compress)
throws IOException {
final ValueOutputStream vos = new ValueOutputStream(new File(absolutePath.val.toString()), compress.val);
try {
@@ -73,6 +79,38 @@ public class IOUtils {
}
return BoolValue.ValTrue;
}
+
+ @Evaluation(definition = "Serialize", module = "IOUtils", warn = false, silent = true, priority = 50)
+ public synchronized static Value serialize50(final Tool tool, final ExprOrOpArgNode[] args, final Context c,
+ final TLCState s0, final TLCState s1, final int control, final CostModel cm) {
+ final IValue opts = tool.eval(args[2]);
+ if (opts instanceof RecordValue) {
+ final RecordValue rv = (RecordValue) opts;
+ final StringValue name = (StringValue) rv.apply(new StringValue("ser"), EvalControl.Clear);
+ if ("frob".equals(name.getVal().toString())) {
+ return new RecordValue(EXEC_NAMES, new Value[] { IntValue.ValZero, new StringValue("frob"), new StringValue("") }, false);
+ }
+ }
+ return null;
+ }
+
+
+ @Evaluation(definition = "Serialize", module = "IOUtils", warn = false, silent = true)
+ public synchronized static Value serialize100(final Tool tool, final ExprOrOpArgNode[] args, final Context c,
+ final TLCState s0, final TLCState s1, final int control, final CostModel cm) {
+ final IValue opts = tool.eval(args[2]);
+
+ if (opts instanceof RecordValue) {
+ final RecordValue rv = (RecordValue) opts;
+ final StringValue name = (StringValue) rv.apply(new StringValue("ser"), EvalControl.Clear);
+ if ("bar".equals(name.getVal().toString())) {
+// final IValue value = tool.eval(args[0]);
+// final IValue dest = tool.eval(args[1]);
+ return new RecordValue(EXEC_NAMES, new Value[] { IntValue.ValZero, new StringValue("bar"), new StringValue("") }, false);
+ }
+ }
+ return null;
+ }
static {
// Eagerly lookup the environment, which is not going to change while the Java
diff --git a/tests/IOUtilsTests.tla b/tests/IOUtilsTests.tla
index b7d64c0..97565ab 100644
--- a/tests/IOUtilsTests.tla
+++ b/tests/IOUtilsTests.tla
@@ -1,5 +1,21 @@
---------------------------- MODULE IOUtilsTests ----------------------------
EXTENDS IOUtils, TLC, TLCExt, Integers
+
+---------------------------------------------------------------------------------------------------------------------------
+
+ASSUME PrintT("IOUtilsTests!D")
+
+ASSUME(LET ret == Serialize("Hello World", "/dev/null", [ser |-> "bar"])
+ IN /\ PrintT(ret)
+ /\ ret.exitValue = 0
+ /\ ret.stderr = ""
+ /\ ret.stdout = "bar")
+ASSUME(LET ret == Serialize("Hello World", "/dev/null", [ser |-> "frob"])
+ IN /\ PrintT(ret)
+ /\ ret.exitValue = 0
+ /\ ret.stderr = ""
+ /\ ret.stdout = "frob")
+
ASSUME PrintT("IOUtilsTests!A")
============================================================================= |
Your proposal seems to be a good option, should we proceed with that idea? |
Yeah I would prefer that the API be left generic and potentially pluggable with new serializers... especially for extracting information from TLC to help build higher-level tools (e.g. I'm using this for test case generation). This is also one area in particular where everyone has a preference and/or language-specific limitations that need to be worked around, so it's best to remain agnostic if possible. @lemmy I really love your proposal! |
Proposed functionality added to TLC with tlaplus/tlaplus@2249366 and tlaplus/tlaplus@f480341. |
@afonsonf Can you create a PR for the TLA+ |
Yes, I will work on it |
Thanks everybody! |
FWIW: tlaplus/tlaplus@1eb8156 puts #53 and this work together. :-) |
Append Json (or any other format) to a dump file: ----- MODULE Foo ------
EXTENDS IOUtils, Json, Naturals, Sequences
FormatOpts ==
[format |-> "TXT", charset |-> "UTF-8", openOptions |-> <<"WRITE", "CREATE", "APPEND">>]
FileName ==
"foo.ndjson"
ASSUME
/\ Serialize(ToJson({"a", "b", "c"}) \o "\n", FileName, FormatOpts).exitValue = 0
/\ Serialize(ToJson([ i \in 11..20 |-> i * i ]) \o "\n", FileName, FormatOpts).exitValue = 0
==================== /cc @kuujo |
IOExec!IOSerialize
serializes the given TLA+ value with Java's built-in serialization. Alternatively,IOUtils!IOExec
provides a low-level mechanism to write to files (see example at the bottom). More specifically, theJson
andCSV
modules have dedicated serialization mechanisms.As a long-term replacement, TLA+ should subsume serialization under:
The advantage of this high-level interface is that serialization/deserialization is independent of a particular serializer and always looks the same while allowing serializers to add additional features such as formatting:
Variant:
Serializer
moved into options:Serializers will be loaded by some service loader mechanism.
The text was updated successfully, but these errors were encountered: