Skip to content

Commit

Permalink
Works
Browse files Browse the repository at this point in the history
  • Loading branch information
julianhyde committed Dec 14, 2024
1 parent f4d9f31 commit dd145ce
Show file tree
Hide file tree
Showing 5 changed files with 859 additions and 132 deletions.
261 changes: 258 additions & 3 deletions src/main/java/net/hydromatic/morel/compile/BuiltIn.java
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,250 @@ public enum BuiltIn {
* is a suffix of itself. */
STRING_IS_SUFFIX("String", "isSuffix", ts -> ts.fnType(STRING, STRING, BOOL)),

/** Constant "Bag.nil", of type "α bag".
*
* <p>"nil" is the empty bag.
*/
BAG_NIL("Bag", "nil", ts -> ts.forallType(1, h -> h.bag(0))),

/** Function "Bag.null", of type "&alpha; bag &rarr; bool".
*
* <p>"null l" returns true if the bag l is empty.
*/
BAG_NULL("Bag", "null", ts ->
ts.forallType(1, h -> ts.fnType(h.bag(0), BOOL))),

/** Function "Bag.length", of type "&alpha; bag &rarr; int".
*
* <p>"length l" returns the number of elements in the bag l.
*/
BAG_LENGTH("Bag", "length", ts ->
ts.forallType(1, h -> ts.fnType(h.bag(0), INT))),

/** Function "Bag.at", of type "&alpha; bag * &alpha; bag &rarr; &alpha;
* bag".
*
* <p>"l1 @ l2" returns the bag that is the concatenation of l1 and l2.
*/
// TODO: remove
BAG_AT("Bag", "at", ts ->
ts.forallType(1, h ->
ts.fnType(ts.tupleType(h.bag(0), h.bag(0)), h.bag(0)))),

/** Operator "Bag.op @", of type "&alpha; bag * &alpha; bag &rarr; &alpha;
* bag".
*
* <p>"l1 @ l2" returns the bag that is the concatenation of l1 and l2.
*/
BAG_OP_AT("Bag", "op @", ts ->
ts.forallType(1, h ->
ts.fnType(ts.tupleType(h.bag(0), h.bag(0)), h.bag(0)))),

/** Function "Bag.hd", of type "&alpha; bag &rarr; &alpha;".
*
* <p>"hd l" returns the first element of l. It raises
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#EMPTY Empty}
* if l is nil.
*/
BAG_HD("Bag", "hd", ts ->
ts.forallType(1, h -> ts.fnType(h.bag(0), h.get(0)))),

/** Function "Bag.tl", of type "&alpha; bag &rarr; &alpha; bag".
*
* <p>"tl l" returns all but the first element of l. It raises
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#EMPTY empty}
* if l is nil.
*/
BAG_TL("Bag", "tl", ts ->
ts.forallType(1, h -> ts.fnType(h.bag(0), h.bag(0)))),

/** Function "Bag.getItem", of type "&alpha; bag &rarr;
* (&alpha; * &alpha; bag) option".
*
* <p>"getItem l" returns {@code NONE} if the bag is empty, and
* {@code SOME(hd l,tl l)} otherwise. This function is particularly useful for
* creating value readers from bags of characters. For example,
* {@code Int.scan StringCvt.DEC getItem} has the type
* {@code (int, char bag) StringCvt.reader}
* and can be used to scan decimal integers from bags of characters.
*/
BAG_GET_ITEM("Bag", "getItem", ts ->
ts.forallType(1, h ->
ts.fnType(h.bag(0),
ts.option(ts.tupleType(h.get(0), h.bag(0)))))),

/** Function "Bag.nth", of type "&alpha; bag * int &rarr; &alpha;".
*
* <p>"nth (l, i)" returns the {@code i}<sup>th</sup> element of the bag
* {@code l}, counting from 0. It raises
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#SUBSCRIPT Subscript}
* if {@code i < 0} or {@code i >= length l}.
* We have {@code nth(l,0) = hd l}, ignoring exceptions.
*/
BAG_NTH("Bag", "nth", ts ->
ts.forallType(1, h -> ts.fnType(ts.tupleType(h.bag(0), INT), h.get(0)))),

/** Function "Bag.take", of type "&alpha; bag * int &rarr; &alpha; bag".
*
* <p>"take (l, i)" returns the first i elements of the bag l. It raises
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#SUBSCRIPT Subscript}
* if i &lt; 0 or i &gt; length l.
* We have {@code take(l, length l) = l}.
*/
BAG_TAKE("Bag", "take", ts ->
ts.forallType(1, h ->
ts.fnType(ts.tupleType(h.bag(0), INT), h.bag(0)))),

/** Function "Bag.drop", of type "&alpha; bag * int &rarr; &alpha; bag".
*
* <p>"drop (l, i)" returns what is left after dropping the first i elements
* of the bag l.
*
* <p>It raises
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#SUBSCRIPT Subscript}
* if i &lt; 0 or i &gt; length l.
*
* <p>It holds that
* {@code take(l, i) @ drop(l, i) = l} when 0 &le; i &le; length l.
*
* <p>We also have {@code drop(l, length l) = []}.
*/
BAG_DROP("Bag", "drop", ts ->
ts.forallType(1, h ->
ts.fnType(ts.tupleType(h.bag(0), INT), h.bag(0)))),

/** Function "Bag.concat", of type "&alpha; bag bag &rarr; &alpha; bag".
*
* <p>"concat l" returns the bag that is the concatenation of all the bags
* in l in order.
* {@code concat[l1,l2,...ln] = l1 @ l2 @ ... @ ln}
*/
BAG_CONCAT("Bag", "concat", ts ->
ts.forallType(1, h -> ts.fnType(ts.bagType(h.bag(0)), h.bag(0)))),

/** Function "Bag.app", of type "(&alpha; &rarr; unit) &rarr; &alpha; bag
* &rarr; unit".
*
* <p>"app f l" applies f to the elements of l, from left to right.
*/
BAG_APP("Bag", "app", ts ->
ts.forallType(1, h ->
ts.fnType(ts.fnType(h.get(0), UNIT), h.bag(0), UNIT))),

/** Function "Bag.map", of type
* "(&alpha; &rarr; &beta;) &rarr; &alpha; bag &rarr; &beta; bag".
*
* <p>"map f l" applies f to each element of l from left to right, returning
* the bag of results.
*/
BAG_MAP("Bag", "map", ts ->
ts.forallType(2, h ->
ts.fnType(ts.fnType(h.get(0), h.get(1)),
h.bag(0), h.bag(1)))),

/** Function "Bag.mapPartial", of type
* "(&alpha; &rarr; &beta; option) &rarr; &alpha; bag &rarr; &beta; bag".
*
* <p>"mapPartial f l" applies f to each element of l from left to right,
* returning a bag of results, with SOME stripped, where f was defined. f is
* not defined for an element of l if f applied to the element returns NONE.
* The above expression is equivalent to:
* {@code ((map valOf) o (filter isSome) o (map f)) l}
*/
BAG_MAP_PARTIAL("Bag", "mapPartial", ts ->
ts.forallType(2, h ->
ts.fnType(ts.fnType(h.get(0), h.option(1)), h.bag(0), h.bag(1)))),

/** Function "Bag.find", of type "(&alpha; &rarr; bool) &rarr; &alpha; bag
* &rarr; &alpha; option".
*
* <p>"find f l" applies f to each element x of the bag l, from left to
* right, until {@code f x} evaluates to true. It returns SOME(x) if such an x
* exists; otherwise it returns NONE.
*/
BAG_FIND("Bag", "find", ts ->
ts.forallType(1, h ->
ts.fnType(h.predicate(0), h.bag(0), h.option(0)))),

/** Function "Bag.filter", of type
* "(&alpha; &rarr; bool) &rarr; &alpha; bag &rarr; &alpha; bag".
*
* <p>"filter f l" applies f to each element x of l, from left to right, and
* returns the bag of those x for which {@code f x} evaluated to true, in the
* same order as they occurred in the argument bag.
*/
BAG_FILTER("Bag", "filter", ts ->
ts.forallType(1, h -> ts.fnType(h.predicate(0), h.bag(0), h.bag(0)))),

/** Function "Bag.partition", of type "(&alpha; &rarr; bool) &rarr;
* &alpha; bag &rarr; &alpha; bag * &alpha; bag".
*
* <p>"partition f l" applies f to each element x of l, from left to right,
* and returns a pair (pos, neg) where pos is the bag of those x for which
* {@code f x} evaluated to true, and neg is the bag of those for which
* {@code f x} evaluated to false. The elements of pos and neg retain the same
* relative order they possessed in l.
*/
BAG_PARTITION("Bag", "partition", ts ->
ts.forallType(1, h ->
ts.fnType(h.predicate(0), h.bag(0),
ts.tupleType(h.bag(0), h.bag(0))))),

/** Function "Bag.fold", of type "(&alpha; * &beta; &rarr; &beta;) &rarr;
* &beta; &rarr; &alpha; bag &rarr; &beta;".
*
* <p>"fold f init [x1, x2, ..., xn]" returns
* {@code f(xn,...,f(x2, f(x1, init))...)}
* or {@code init} if the bag is empty.
*/
BAG_FOLD("Bag", "fold", ts ->
ts.forallType(2, h ->
ts.fnType(ts.fnType(ts.tupleType(h.get(0), h.get(1)), h.get(1)),
h.get(1), h.bag(0), h.get(1)))),

/** Function "Bag.exists", of type "(&alpha; &rarr; bool) &rarr; &alpha; bag
* &rarr; bool".
*
* <p>"exists f l" applies f to each element x of the bag l, from left to
* right, until {@code f x} evaluates to true; it returns true if such an x
* exists and false otherwise.
*/
BAG_EXISTS("Bag", "exists", ts ->
ts.forallType(1, h -> ts.fnType(h.predicate(0), h.bag(0), BOOL))),

/** Function "Bag.all", of type
* "(&alpha; &rarr; bool) &rarr; &alpha; bag &rarr; bool".
*
* <p>"all f l" applies f to each element x of the bag l, from left to right,
* until {@code f x} evaluates to false; it returns false if such an x exists
* and true otherwise. It is equivalent to not(exists (not o f) l)).
*/
BAG_ALL("Bag", "all", ts ->
ts.forallType(1, h -> ts.fnType(h.predicate(0), h.bag(0), BOOL))),

/** Function "Bag.fromList" of type "&alpha; list &rarr; &alpha; vector".
*
* <p>{@code fromList l} creates a new vector from {@code l}, whose length is
* {@code length l} and with the {@code i}<sup>th</sup> element of {@code l}
* used as the {@code i}<sup>th</sup> element of the vector. If the length of
* the list is greater than {@code maxLen}, then the
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#SIZE Size}
* exception is raised. */
BAG_FROM_LIST("Bag", "fromList", "bag", ts ->
ts.forallType(1, h -> ts.fnType(h.list(0), h.bag(0)))),

/** Function "Bag.tabulate", of type
* "int * (int &rarr; &alpha;) &rarr; &alpha; bag".
*
* <p>"tabulate (n, f)" returns a bag of length n equal to
* {@code [f(0), f(1), ..., f(n-1)]}, created from left to right. It raises
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#SIZE Size}
* if n &lt; 0.
*/
BAG_TABULATE("Bag", "tabulate", ts ->
ts.forallType(1, h ->
ts.fnType(ts.tupleType(INT, ts.fnType(INT, h.get(0))), h.bag(0)))),

/** Constant "List.nil", of type "&alpha; list".
*
* <p>"nil" is the empty list.
Expand Down Expand Up @@ -598,16 +842,26 @@ public enum BuiltIn {
ts.forallType(1, h ->
ts.fnType(ts.fnType(h.get(0), UNIT), h.list(0), UNIT))),

/** Function "List.mapi", of type
* "(int * &alpha; &rarr; &beta;) &rarr; &alpha; list &rarr; &beta; list".
*
* <p>"mapi f l" applies f to each element of l from left to right, returning
* the list of results.
*/
LIST_MAPI("List", "mapi", ts ->
ts.forallType(2, h ->
ts.fnType(ts.fnType(ts.tupleType(INT, h.get(0)), h.get(1)),
h.list(0), h.list(1)))),

/** Function "List.map", of type
* "(&alpha; &rarr; &beta;) &rarr; &alpha; list &rarr; &beta; list".
*
* <p>"map f l" applies f to each element of l from left to right, returning
* the list of results.
*/
LIST_MAP("List", "map", "map", ts ->
ts.forallType(2, t ->
ts.fnType(ts.fnType(t.get(0), t.get(1)),
ts.listType(t.get(0)), ts.listType(t.get(1))))),
ts.forallType(2, h ->
ts.fnType(ts.fnType(h.get(0), h.get(1)), h.list(0), h.list(1)))),

/** Function "List.mapPartial", of type
* "(&alpha; &rarr; &beta; option) &rarr; &alpha; list &rarr; &beta; list".
Expand Down Expand Up @@ -1814,6 +2068,7 @@ public static void dataTypes(TypeSystem typeSystem, List<Binding> bindings) {
h.tyCon("LESS").tyCon("EQUAL").tyCon("GREATER"));
defineDataType(typeSystem, bindings, "option", false, 1, h ->
h.tyCon("NONE").tyCon("SOME", h.get(0)));
defineEqType(typeSystem, "bag", 1);
defineEqType(typeSystem, "vector", 1);

// Define two internal datatypes:
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/net/hydromatic/morel/compile/Pretty.java
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,15 @@ private StringBuilder pretty2(@NonNull StringBuilder buf,
return printList(buf.append('#'), indent, lineEnd, depth, argType,
list);
}
if (dataType.name.equals("bag")) {
// A bag value is printed the same as a list, distinguishable only by
// its type, e.g.
// val odds = [1,3,5] : int list
// val evens = [0,2,4] : int bag
final Type argType = Iterables.getOnlyElement(dataType.arguments);
return printList(buf, indent, lineEnd, depth, argType,
list);
}
final String tyConName = (String) list.get(0);
buf.append(tyConName);
final Type typeConArgType =
Expand Down
Loading

0 comments on commit dd145ce

Please sign in to comment.