Skip to content

Commit e081b9a

Browse files
committed
Add CSV!CSVRead operator.
[Feature]
1 parent 677d3d4 commit e081b9a

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

modules/CSV.tla

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ CSVWrite(template, val, file) ==
1313
*)
1414
TRUE
1515

16+
CSVRead(columns, delimiter, file) ==
17+
(*
18+
CSVRead(<<"C1", "C2", "C3">>, "#", "/tmp/out.csv")
19+
20+
<< [ C1 |-> "\"abc\"", C2 |-> "42", C3 |-> "{"\"x\"", "\"y\""}" ] >>
21+
*)
22+
TRUE
23+
24+
1625
CSVRecords(file) ==
1726
(* The number of records in the given file (including headers if any). *)
1827
CHOOSE n : n \in Nat

modules/tlc2/overrides/CSV.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,20 @@
3232
import java.nio.file.Paths;
3333
import java.nio.file.StandardOpenOption;
3434
import java.util.Arrays;
35+
import java.util.List;
36+
import java.util.stream.Collectors;
3537
import java.util.stream.Stream;
3638

3739
import tlc2.output.EC;
3840
import tlc2.tool.EvalException;
3941
import tlc2.value.Values;
4042
import tlc2.value.impl.BoolValue;
4143
import tlc2.value.impl.IntValue;
44+
import tlc2.value.impl.RecordValue;
4245
import tlc2.value.impl.StringValue;
4346
import tlc2.value.impl.TupleValue;
4447
import tlc2.value.impl.Value;
48+
import util.UniqueString;
4549

4650
public class CSV {
4751

@@ -61,6 +65,36 @@ public static Value write(final StringValue template, final Value parameter, fin
6165
return BoolValue.ValTrue;
6266
}
6367

68+
@TLAPlusOperator(identifier = "CSVRead", module = "CSV", minLevel = 1, warn = false)
69+
public static Value read(final Value columns, final StringValue delim, final StringValue absolutePath)
70+
throws IOException {
71+
72+
final TupleValue tv = (TupleValue) columns.toTuple();
73+
if (tv == null) {
74+
throw new EvalException(EC.TLC_MODULE_ONE_ARGUMENT_ERROR,
75+
new String[] { "CSVRead", "sequence", Values.ppr(columns.toString()) });
76+
}
77+
78+
final Path path = Paths.get(absolutePath.val.toString());
79+
if (!path.toFile().exists()) {
80+
// There are zero records in a file that doesn't exist.
81+
return TupleValue.EmptyTuple;
82+
}
83+
84+
final UniqueString[] names = Arrays.asList(tv.elems).stream().map(v -> ((StringValue) v).val)
85+
.collect(Collectors.toList()).toArray(UniqueString[]::new);
86+
87+
final List<String> lines = Files.readAllLines(path);
88+
final Value[] records = new Value[lines.size()];
89+
for (int i = 0; i < lines.size(); i++) {
90+
StringValue[] values = Arrays.asList(lines.get(i).split(delim.val.toString())).stream()
91+
.map(s -> new StringValue(s)).collect(Collectors.toList()).toArray(StringValue[]::new);
92+
records[i] = new RecordValue(names, values, false);
93+
}
94+
95+
return new TupleValue(records);
96+
}
97+
6498
@TLAPlusOperator(identifier = "CSVRecords", module = "CSV", minLevel = 1, warn = false)
6599
public static Value records(final StringValue absolutePath)
66100
throws IOException {

tests/CSVTests.tla

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---------------------------- MODULE CSVTests ----------------------------
2-
EXTENDS CSV, TLC, Sequences, IOUtils
2+
EXTENDS CSV, TLC, Sequences, IOUtils, TLCExt
33

44
ASSUME LET T == INSTANCE TLC IN T!PrintT("CSVTests")
55

@@ -14,11 +14,13 @@ ToFile ==
1414
"build/tests/CSVWriteTest-" \o ToString(JavaTime) \o ".csv"
1515

1616
\* Write Value to ToFile then check success by reading with IOExec.
17-
ASSUME(
17+
ASSUME
1818
CSVWrite(Template, Value, ToFile)
1919
=>
20-
/\ IOExec(<< "cat", ToFile >>).stdout = "42#\"abc\"#[a |-> \"a\", b |-> \"b\"]\n")
2120
/\ CSVRecords(ToFile) = 1
21+
\* Skip the third (record) element because it would come back as a string.
22+
/\ LET in == Head(CSVRead(<<"a", "b", "c">>, "#", ToFile))
23+
IN in.a = "42" /\ in.b = "\"abc\"" /\ in.c = "[a |-> \"a\", b |-> \"b\"]"
2224

2325
ASSUME
2426
CSVRecords("DoesNotExistNowhere.tla") = 0

0 commit comments

Comments
 (0)