Skip to content

Commit

Permalink
It works
Browse files Browse the repository at this point in the history
  • Loading branch information
julianhyde committed Dec 12, 2023
1 parent 728923c commit 1b02c28
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 40 deletions.
33 changes: 21 additions & 12 deletions src/main/java/net/hydromatic/morel/compile/Pretty.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,21 +171,23 @@ private StringBuilder pretty2(@Nonnull StringBuilder buf,

case LIST:
final ListType listType = (ListType) type;
//noinspection unchecked,rawtypes
list = (List) value;
list = toList(value);
if (list instanceof RelList) {
// Do not attempt to print the elements of a foreign list. It might be huge.
// Do not attempt to print the elements of a foreign list. It might be
// huge.
return buf.append("<relation>");
}
if (value instanceof Codes.TypedValue) {
// A TypedValue is probably a field in a record that represents a
// database catalog or a directory of CSV files. If the user wishes to
// see the contents of each file they should use a query.
return buf.append("<relation>");
}
return printList(buf, indent, lineEnd, depth, listType.elementType, list);

case RECORD_TYPE:
final RecordType recordType = (RecordType) type;
//noinspection unchecked,rawtypes
list =
value instanceof Codes.TypedValue
? ((Codes.TypedValue) value).valueAs(List.class)
: (List) value;
list = toList(value);
buf.append("{");
start = buf.length();
forEachIndexed(list, recordType.argNameTypes.entrySet(),
Expand All @@ -200,8 +202,7 @@ private StringBuilder pretty2(@Nonnull StringBuilder buf,

case TUPLE_TYPE:
final TupleType tupleType = (TupleType) type;
//noinspection unchecked,rawtypes
list = (List) value;
list = toList(value);
buf.append("(");
start = buf.length();
forEachIndexed(list, tupleType.argTypes,
Expand All @@ -219,8 +220,7 @@ private StringBuilder pretty2(@Nonnull StringBuilder buf,

case DATA_TYPE:
final DataType dataType = (DataType) type;
//noinspection unchecked,rawtypes
list = (List) value;
list = toList(value);
if (dataType.name.equals("vector")) {
final Type argType = Iterables.getOnlyElement(dataType.arguments);
return printList(buf.append('#'), indent, lineEnd, depth, argType,
Expand Down Expand Up @@ -252,6 +252,15 @@ private StringBuilder pretty2(@Nonnull StringBuilder buf,
}
}

@SuppressWarnings("unchecked")
private static List<Object> toList(Object value) {
if (value instanceof Codes.TypedValue) {
Codes.TypedValue typedValue = (Codes.TypedValue) value;
return (List<Object>) typedValue.valueAs(List.class);
}
return (List<Object>) value;
}

private static Type unqualified(Type type) {
return type instanceof ForallType ? unqualified(((ForallType) type).type)
: type;
Expand Down
82 changes: 56 additions & 26 deletions src/main/java/net/hydromatic/morel/eval/Directory.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import net.hydromatic.morel.type.RecordType;
import net.hydromatic.morel.type.Type;
import net.hydromatic.morel.type.TypeSystem;
import net.hydromatic.morel.util.ImmutablePairList;
import net.hydromatic.morel.util.PairList;

import com.google.common.collect.ImmutableList;
Expand All @@ -39,6 +38,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
import java.util.function.Function;

import static java.util.Objects.requireNonNull;

Expand Down Expand Up @@ -117,20 +117,21 @@ private static String removeSuffix(String name, String suffix) {

private static class DataFile implements Codes.TypedValue {
private final File file;
private final ImmutablePairList<String, Type.Key> nameTypes;

DataFile(File file, PairList<String, Type.Key> nameTypes) {
this.file = file;
this.nameTypes = nameTypes.immutable();
private final Type.Key typeKey;
private final PairList<Integer, Function<String, Object>> parsers;

DataFile(File file, Type.Key typeKey,
PairList<Integer, Function<String, Object>> parsers) {
this.file = requireNonNull(file, "file");
this.typeKey = requireNonNull(typeKey, "typeKey");
this.parsers = parsers.immutable();
}

static DataFile create(File subFile) {
try (BufferedReader r = Util.reader(subFile)) {
String firstLine = r.readLine();
PairList<String, Type.Key> nameTypes = PairList.of();
if (firstLine == null) {
// File is empty. There will be no fields, and row type will be unit.
} else {
final PairList<String, Type.Key> nameTypes = PairList.of();
if (firstLine != null) {
for (String field : firstLine.split(",")) {
final String[] split = field.split(":");
final String subFieldName = split[0];
Expand All @@ -154,18 +155,56 @@ static DataFile create(File subFile) {
}
nameTypes.add(subFieldName, subType);
}
} else {
// File is empty. There will be no fields, and row type will be unit.
}
return new DataFile(subFile, nameTypes);
final ImmutableSortedMap<String, Type.Key> sortedNameTypes =
ImmutableSortedMap.<String, Type.Key>orderedBy(RecordType.ORDERING)
.putAll(nameTypes)
.build();
final PairList<Integer, Function<String, Object>> fieldParsers =
PairList.of();
nameTypes.forEachIndexed((i, name, typeKey) -> {
final int j = sortedNameTypes.keySet().asList().indexOf(name);
fieldParsers.add(j, parser(typeKey, i));
});

return new DataFile(subFile, Keys.list(Keys.record(sortedNameTypes)),
fieldParsers);
} catch (IOException e) {
// ignore, and skip file
return null;
}
}

static Function<String, Object> parser(Type.Key type, int ordinal) {
switch (type.op) {
case DATA_TYPE:
switch (type.toString()) {
case "int":
return Integer::parseInt;
case "real":
return Float::parseFloat;
case "string":
return DataFile::unquoteString;
default:
throw new IllegalArgumentException("unknown type " + type);
}
default:
throw new IllegalArgumentException("unknown type " + type);
}
}

/** Converts "abc" to "abc" and "'abc, def'" to "abc, def". */
private static Object unquoteString(String s) {
if (s.startsWith("'")) {
return s.substring(1, s.length() - 1);
}
return s;
}

@Override public <V> V valueAs(Class<V> clazz) {
Object[] values = new Object[nameTypes.size()];
List<String> sortedFieldNames =
RecordType.ORDERING.immutableSortedCopy(nameTypes.leftList());
Object[] values = new Object[parsers.size()];
try (BufferedReader r = Util.reader(file)) {
String firstLine = r.readLine();
if (firstLine == null) {
Expand All @@ -178,12 +217,8 @@ static DataFile create(File subFile) {
return clazz.cast(list);
}
String[] fields = line.split(",");
for (int i = 0; i < values.length; i++) {
final int j = nameTypes.leftList().indexOf(sortedFieldNames.get(i));
String field = fields[j];
Type.Key typeKey = nameTypes.rightList().get(j);
values[i] = Integer.parseInt(field);
}
parsers.forEachIndexed((i, j, parser) ->
values[i] = parser.apply(fields[j]));
list.add(ImmutableList.copyOf(values));
}
} catch (IOException e) {
Expand All @@ -193,12 +228,7 @@ static DataFile create(File subFile) {
}

@Override public Type.Key typeKey() {
return Keys.list(
Keys.record(
ImmutableSortedMap
.<String, Type.Key>orderedBy(RecordType.ORDERING)
.putAll(nameTypes)
.build()));
return typeKey;
}
}
}
Expand Down
26 changes: 24 additions & 2 deletions src/test/resources/script/file.smli
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,35 @@ file;
file.data;
> val it = {} : {...}

(*) Now we've done into the directory, its type has widened.
(*) Now we've gone into the directory, the type of 'file' has widened.
file;
> val it = {data={},script={},use={}} : {data:{...}, script:{...}, use:{...}}

file.data.scott;
> val it = {} : {...}
file.data.scott.dept;
> val it = {} : {...}
> val it =
> [{deptno=10,dname="ACCOUNTING",loc="NEW YORK"},
> {deptno=20,dname="RESEARCH",loc="DALLAS"},
> {deptno=30,dname="SALES",loc="CHICAGO"},
> {deptno=40,dname="OPERATIONS",loc="BOSTON"}]
> : {deptno:int, dname:string, loc:string} list

(*) Since dept is a list of records, we can query it.
from d in file.data.scott.dept
where d.dname elem ["ACCOUNTING", "SALES"]
compute sum of d.deptno;
> val it = 40 : int

(*) The type of 'file' has widened further.
file;
> val it =
> {data={scott={bonus=<relation>,dept=<relation>,salgrade=<relation>}},
> script={},use={}}
> : {data:{scott:{bonus:{bonus:real, ename:string, job:string, sal:real} list, dept:{deptno:int, dname:string, loc:string} list, salgrade:{grade:int, hisal:real, losal:real} list}}, script:{...}, use:{...}}

file.data.scott;
> val it = {bonus=<relation>,dept=<relation>,salgrade=<relation>}
> : {bonus:{bonus:real, ename:string, job:string, sal:real} list, dept:{deptno:int, dname:string, loc:string} list, salgrade:{grade:int, hisal:real, losal:real} list}

(*) End file.smli

0 comments on commit 1b02c28

Please sign in to comment.