diff --git a/README.md b/README.md index f4b0394f..7229e48b 100644 --- a/README.md +++ b/README.md @@ -88,9 +88,8 @@ Implemented: * `val` (including `val rec`) * `fun` (declare function) * Operators: `=` `<>` `<` `>` `<=` `>=` - `~` `+` `-` `*` `/` `div` `mod` `^` + `~` `+` `-` `*` `/` `div` `mod` `^` `::` `o` `@` `andalso` `orelse` - `::` * Built-in constants and functions: `it` `true` `false` `nil` `abs` `not` `ignore` * Type derivation @@ -121,7 +120,7 @@ Not implemented: * `exception` * `while` * References, and operators `!` and `:=` -* Operators: `before` `o` +* Operators: `before` * User-defined operators (`infix`, `infixr`) * Type annotations in expressions and patterns diff --git a/src/main/java/net/hydromatic/morel/ast/AstBuilder.java b/src/main/java/net/hydromatic/morel/ast/AstBuilder.java index 33a9b129..c06be9c4 100644 --- a/src/main/java/net/hydromatic/morel/ast/AstBuilder.java +++ b/src/main/java/net/hydromatic/morel/ast/AstBuilder.java @@ -260,6 +260,10 @@ public Ast.Exp caret(Ast.Exp a0, Ast.Exp a1) { return infix(Op.CARET, a0, a1); } + public Ast.Exp o(Ast.Exp a0, Ast.Exp a1) { + return infix(Op.COMPOSE, a0, a1); + } + public Ast.Exp except(Ast.Exp a0, Ast.Exp a1) { return infix(Op.EXCEPT, a0, a1); } diff --git a/src/main/java/net/hydromatic/morel/compile/BuiltIn.java b/src/main/java/net/hydromatic/morel/compile/BuiltIn.java index e68dd512..61bf4d81 100644 --- a/src/main/java/net/hydromatic/morel/compile/BuiltIn.java +++ b/src/main/java/net/hydromatic/morel/compile/BuiltIn.java @@ -170,6 +170,18 @@ public enum BuiltIn { ts.forallType(1, h -> ts.fnType(ts.tupleType(h.get(0), h.get(0)), h.get(0)))), + /** Operator "General.op o", of type "(β → γ) * + * (α → β) → α → γ" + * + *

"f o g" is the function composition of "f" and "g". Thus, "(f o g) a" + * is equivalent to "f (g a)". */ + GENERAL_OP_O("General", "op o", "op o", ts -> + ts.forallType(3, h -> + ts.fnType( + ts.tupleType(ts.fnType(h.get(1), h.get(2)), + ts.fnType(h.get(0), h.get(1))), + ts.fnType(h.get(0), h.get(2))))), + /** Constant "String.maxSize", of type "int". * *

"The longest allowed size of a string". */ @@ -311,11 +323,20 @@ public enum BuiltIn { * *

"l1 @ l2" returns the list that is the concatenation of l1 and l2. */ - // TODO: make this infix "@" rather than prefix "at" + // TODO: remove LIST_AT("List", "at", ts -> ts.forallType(1, h -> ts.fnType(ts.tupleType(h.list(0), h.list(0)), h.list(0)))), + /** Operator "List.op @", of type "α list * α list → α + * list". + * + *

"l1 @ l2" returns the list that is the concatenation of l1 and l2. + */ + LIST_OP_AT("List", "op @", "op @", ts -> + ts.forallType(1, h -> + ts.fnType(ts.tupleType(h.list(0), h.list(0)), h.list(0)))), + /** Function "List.hd", of type "α list → α". * *

"hd l" returns the first element of l. It raises {@code Empty} if l is diff --git a/src/main/java/net/hydromatic/morel/compile/TypeResolver.java b/src/main/java/net/hydromatic/morel/compile/TypeResolver.java index ace72109..06e7ca14 100644 --- a/src/main/java/net/hydromatic/morel/compile/TypeResolver.java +++ b/src/main/java/net/hydromatic/morel/compile/TypeResolver.java @@ -414,7 +414,9 @@ private Ast.Exp deduceType(TypeEnv env, Ast.Exp node, Unifier.Variable v) { } return reg(apply.copy(fn2, arg2), null, v); + case AT: case CARET: + case COMPOSE: case PLUS: case MINUS: case TIMES: diff --git a/src/main/java/net/hydromatic/morel/eval/Codes.java b/src/main/java/net/hydromatic/morel/eval/Codes.java index 200eb907..c1eb38e3 100644 --- a/src/main/java/net/hydromatic/morel/eval/Codes.java +++ b/src/main/java/net/hydromatic/morel/eval/Codes.java @@ -241,6 +241,17 @@ private static int div(EvalEnv env, Object arg) { return Math.floorDiv((int) list.get(0), (int) list.get(1)); } + /** @see BuiltIn#GENERAL_OP_O */ + private static final Applicable GENERAL_OP_O = Codes::compose; + + /** Implements {@link #GENERAL_OP_O}. */ + private static Applicable compose(EvalEnv evalEnv, Object arg) { + @SuppressWarnings("rawtypes") final List list = (List) arg; + final Applicable f = (Applicable) list.get(0); + final Applicable g = (Applicable) list.get(1); + return (evalEnv2, arg2) -> f.apply(evalEnv2, g.apply(evalEnv2, arg2)); + } + /** @see BuiltIn#OP_CARET */ private static final Applicable OP_CARET = Codes::caret; @@ -1049,6 +1060,7 @@ public static Applicable aggregate(Environment env, Code aggregateCode, .put(BuiltIn.NOT, NOT) .put(BuiltIn.ABS, ABS) .put(BuiltIn.IGNORE, IGNORE) + .put(BuiltIn.GENERAL_OP_O, GENERAL_OP_O) .put(BuiltIn.OP_CARET, OP_CARET) .put(BuiltIn.OP_CONS, OP_CONS) .put(BuiltIn.OP_DIV, OP_DIV) @@ -1088,6 +1100,7 @@ public static Applicable aggregate(Environment env, Code aggregateCode, .put(BuiltIn.LIST_NULL, LIST_NULL) .put(BuiltIn.LIST_LENGTH, LIST_LENGTH) .put(BuiltIn.LIST_AT, LIST_AT) + .put(BuiltIn.LIST_OP_AT, LIST_AT) // op @ == List.at .put(BuiltIn.LIST_HD, LIST_HD) .put(BuiltIn.LIST_TL, LIST_TL) .put(BuiltIn.LIST_LAST, LIST_LAST) diff --git a/src/main/java/net/hydromatic/morel/util/Folder.java b/src/main/java/net/hydromatic/morel/util/Folder.java new file mode 100644 index 00000000..f6c1bce9 --- /dev/null +++ b/src/main/java/net/hydromatic/morel/util/Folder.java @@ -0,0 +1,113 @@ +/* + * Licensed to Julian Hyde under one or more contributor license + * agreements. See the NOTICE file distributed with this work + * for additional information regarding copyright ownership. + * Julian Hyde licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package net.hydromatic.morel.util; + +import net.hydromatic.morel.ast.Ast; +import net.hydromatic.morel.ast.Op; + +import java.util.List; +import java.util.Objects; +import java.util.function.Function; + +import static net.hydromatic.morel.ast.AstBuilder.ast; + +/** + * Enable creating right-deep trees. + * + * @param Element type + */ +public abstract class Folder { + final E e; + + Folder(E e) { + this.e = Objects.requireNonNull(e); + } + + abstract E combine(List> list); + + public static E combineAll(List> list) { + if (list.size() == 0) { + throw new AssertionError(); + } + final Folder head = list.get(0); + final List> tail = list.subList(1, list.size()); + return head.combine(tail); + } + + private static Folder end(E e) { + return new End<>(e); + } + + /** Appends an element using "@". */ + public static void at(List> list, Ast.Exp e) { + append(list, e, e1 -> op(e1, Op.AT)); + } + + /** Appends an element using "::". */ + public static void cons(List> list, Ast.Exp e) { + append(list, e, e1 -> op(e1, Op.CONS)); + } + + /** Adds an element to an empty list. */ + public static void start(List> list, E e) { + if (!list.isEmpty()) { + throw new AssertionError(); + } + list.add(end(e)); + } + + /** Adds an element and operator to a non-empty list. */ + private static void append(List> list, E e, + Function> fn) { + if (list.isEmpty()) { + throw new AssertionError(); + } + @SuppressWarnings("unchecked") + final End end = (End) list.get(list.size() - 1); + list.set(list.size() - 1, fn.apply(end.e)); + list.add(end(e)); + } + + /** Creates a folder that combines an expression with whatever follows + * using an infix operator. */ + private static Folder op(Ast.Exp e, final Op at) { + return new Folder(e) { + Ast.Exp combine(List> list) { + final Ast.Exp rest = combineAll(list); + return ast.infixCall(e.pos.plus(rest.pos), at, e, rest); + } + }; + } + + /** Sub-class of {@code Folder} that marks the end of a list. + * + * @param element type */ + private static class End extends Folder { + End(E e) { + super(e); + } + + E combine(List> list) { + assert list.isEmpty(); + return e; + } + } +} + +// End Folder.java diff --git a/src/main/javacc/MorelParser.jj b/src/main/javacc/MorelParser.jj index 731477c8..3b3271b3 100644 --- a/src/main/javacc/MorelParser.jj +++ b/src/main/javacc/MorelParser.jj @@ -32,7 +32,7 @@ import net.hydromatic.morel.ast.Ast.*; import net.hydromatic.morel.ast.AstBuilder; import net.hydromatic.morel.ast.AstNode; import net.hydromatic.morel.ast.Pos; -import net.hydromatic.morel.util.Pair; +import net.hydromatic.morel.util.Folder;import net.hydromatic.morel.util.Pair; import com.google.common.collect.ImmutableList; @@ -581,14 +581,16 @@ Exp expression6() : Exp expression5() : { Exp e; - final List list = new ArrayList<>(); + final List> list = new ArrayList<>(); } { - e = expression6() { list.add(e); } + e = expression6() { Folder.start(list, e); } ( - e = expression6() { list.add(e); } + e = expression6() { Folder.at(list, e); } + | + e = expression6() { Folder.cons(list, e); } )* - { return ast.foldCons(list); } + { return Folder.combineAll(list); } } /** Parses an expression of precedence level 4 ({@code =}, {@code <>}, @@ -636,6 +638,22 @@ Exp expression4() : { return e; } } +/** Parses an expression of precedence level 3 (o). */ +Exp expression3() : +{ + Exp e; + Exp e2; +} +{ + e = expression4() + ( + e2 = expression4() { + e = ast.o(e, e2); + } + )* + { return e; } +} + /** Parses an expression of precedence level 2 (andalso). */ Exp expression2() : { @@ -643,9 +661,9 @@ Exp expression2() : Exp e2; } { - e = expression4() + e = expression3() ( - e2 = expression4() { + e2 = expression3() { e = ast.andAlso(e, e2); } )* @@ -1257,6 +1275,7 @@ AstNode statementSemicolon() : | < LET: "LET" > | < MOD: "MOD" > | < NOT_ELEM: "NOTELEM" > +| < O: "O" > | < OF: "OF" > | < ORELSE: "ORELSE" > | < REC: "REC" > @@ -1336,6 +1355,7 @@ AstNode statementSemicolon() : | < SLASH: "/" > | < TILDE: "~" > | < CONS: "::" > + | < AT: "@" > | < ELLIPSIS: "..." > | < QUOTE: "'" > | < DOUBLE_QUOTE: "\"" > diff --git a/src/test/java/net/hydromatic/morel/MainTest.java b/src/test/java/net/hydromatic/morel/MainTest.java index bbc5ff19..65bdf365 100644 --- a/src/test/java/net/hydromatic/morel/MainTest.java +++ b/src/test/java/net/hydromatic/morel/MainTest.java @@ -249,6 +249,29 @@ public void describeTo(Description description) { .assertParse("1 :: 2 :: 3 :: []"); ml("1 + 2 :: 3 + 4 * 5 :: 6").assertParseSame(); + // o is left-associative; + // lower precedence than "=" (4), higher than "andalso" (2) + ml("f o g").assertParseSame(); + ml("f o g o h").assertParseSame(); + ml("f o (g o h)").assertParseSame(); + ml("(f o g) o h").assertParse("f o g o h"); + + ml("a = f o g andalso c = d").assertParseSame(); + ml("a = (f o g) andalso (c = d)").assertParse("a = (f o g) andalso c = d"); + ml("(a = f) o g andalso (c = d)").assertParse("a = f o g andalso c = d"); + + // @ is right-associative; + // lower precedence than "+" (6), higher than "=" (4) + ml("f @ g").assertParseSame(); + ml("f @ g @ h").assertParseSame(); + ml("f @ (g @ h)").assertParse("f @ g @ h"); + ml("(f @ g) @ h").assertParseSame(); + + // ^ is left-associative; + // lower precedence than "*" (7), higher than "@" (5) + ml("a * f ^ g @ b").assertParseSame(); + ml("(a * f) ^ (g @ b)").assertParse("a * f ^ (g @ b)"); + ml("(1 + 2, 3, true, (5, 6), 7 = 8)").assertParseSame(); ml("let val x = 2 in x + (3 + x) + x end").assertParseSame(); diff --git a/src/test/java/net/hydromatic/morel/UtilTest.java b/src/test/java/net/hydromatic/morel/UtilTest.java index 455fe2b2..d8704d82 100644 --- a/src/test/java/net/hydromatic/morel/UtilTest.java +++ b/src/test/java/net/hydromatic/morel/UtilTest.java @@ -18,6 +18,9 @@ */ package net.hydromatic.morel; +import net.hydromatic.morel.ast.Ast; +import net.hydromatic.morel.ast.Pos; +import net.hydromatic.morel.util.Folder; import net.hydromatic.morel.util.MapList; import net.hydromatic.morel.util.Ord; import net.hydromatic.morel.util.TailList; @@ -28,6 +31,8 @@ import java.util.Arrays; import java.util.List; +import static net.hydromatic.morel.ast.AstBuilder.ast; + import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; @@ -88,6 +93,19 @@ public class UtilTest { assertThat(abc.get(2), is("c")); assertThat(String.join(",", abc), is("a,b,c")); } + + @Test public void testFolder() { + final List> list = new ArrayList<>(); + Folder.start(list, ast.stringLiteral(Pos.ZERO, "a")); + Folder.at(list, ast.stringLiteral(Pos.ZERO, "b")); + Folder.at(list, ast.stringLiteral(Pos.ZERO, "c")); + assertThat(Folder.combineAll(list).toString(), is("\"a\" @ \"b\" @ \"c\"")); + + list.clear(); + Folder.start(list, ast.stringLiteral(Pos.ZERO, "a")); + Folder.cons(list, ast.stringLiteral(Pos.ZERO, "b")); + assertThat(Folder.combineAll(list).toString(), is("\"a\" :: \"b\"")); + } } // End UtilTest.java diff --git a/src/test/resources/script/builtIn.sml b/src/test/resources/script/builtIn.sml index c1c744f8..7f1d86ec 100644 --- a/src/test/resources/script/builtIn.sml +++ b/src/test/resources/script/builtIn.sml @@ -17,6 +17,18 @@ * License. *) +(* Operators --------------------------------------------------- *) + +(*) op o - function composition +val plusOne = fn x => x + 1; +val timesTwo = fn x => x * 2; +val plusThree = fn x => x + 3; +plusOne o timesTwo; +(plusOne o timesTwo) 3; +plusOne o timesTwo o plusThree; +((plusOne o timesTwo) o plusThree) 3; +(plusOne o (timesTwo o plusThree)) 3; + (* Miscellaneous ----------------------------------------------- *) ignore; ignore (1 + 2); @@ -170,6 +182,9 @@ List.at ([1], []); List.at ([], [2]); List.at ([], []); +[1] @ [2, 3]; +[] @ []; + (*) val hd : 'a list -> 'a List.hd; List.hd [1,2,3]; diff --git a/src/test/resources/script/builtIn.sml.out b/src/test/resources/script/builtIn.sml.out index eb3e52d6..5b9f332f 100644 --- a/src/test/resources/script/builtIn.sml.out +++ b/src/test/resources/script/builtIn.sml.out @@ -17,6 +17,34 @@ * License. *) +(* Operators --------------------------------------------------- *) + +(*) op o - function composition +val plusOne = fn x => x + 1; +val plusOne = fn : int -> int + +val timesTwo = fn x => x * 2; +val timesTwo = fn : int -> int + +val plusThree = fn x => x + 3; +val plusThree = fn : int -> int + +plusOne o timesTwo; +val it = fn : int -> int + +(plusOne o timesTwo) 3; +val it = 7 : int + +plusOne o timesTwo o plusThree; +val it = fn : int -> int + +((plusOne o timesTwo) o plusThree) 3; +val it = 13 : int + +(plusOne o (timesTwo o plusThree)) 3; +val it = 13 : int + + (* Miscellaneous ----------------------------------------------- *) ignore; val it = fn : 'a -> unit @@ -38,9 +66,9 @@ List; val it = {all=fn,app=fn,at=fn,collate=fn,concat=fn,drop=fn,exists=fn,filter=fn, find=fn,foldl=fn,foldr=fn,getItem=fn,hd=fn,last=fn,length=fn,map=fn, - mapPartial=fn,nil=[],nth=fn,null=fn,partition=fn,rev=fn,revAppend=fn, - tabulate=fn,take=fn,tl=fn} - : {all:('a -> bool) -> 'a list -> bool, app:('b -> unit) -> 'b list -> unit, at:'c list * 'c list -> 'c list, collate:('d * 'd -> int) -> 'd list * 'd list -> int, concat:'e list list -> 'e list, drop:'f list * int -> 'f list, exists:('g -> bool) -> 'g list -> bool, filter:('h -> bool) -> 'h list -> 'h list, find:('i -> bool) -> 'i list -> 'i, foldl:('j * 'k -> 'k) -> 'k -> 'j list -> 'k, foldr:('l * 'm -> 'm) -> 'm -> 'l list -> 'm, getItem:'n list -> 'n * 'n list, hd:'o list -> 'o, last:'p list -> 'p, length:'q list -> int, map:('r -> 's) -> 'r list -> 's list, mapPartial:('t -> 'u) -> 't list -> 'u list, nil:'v list, nth:'w list * int -> 'w, null:'x list -> bool, partition:('y -> bool) -> 'y list -> 'y list * 'y list, rev:'z list -> 'z list, revAppend:'ba list * 'ba list -> 'ba list, tabulate:int * (int -> 'bb) -> 'bb list, take:'bc list * int -> 'bc list, tl:'bd list -> 'bd list} + mapPartial=fn,nil=[],nth=fn,null=fn,op @=fn,partition=fn,rev=fn, + revAppend=fn,tabulate=fn,take=fn,tl=fn} + : {all:('a -> bool) -> 'a list -> bool, app:('b -> unit) -> 'b list -> unit, at:'c list * 'c list -> 'c list, collate:('d * 'd -> int) -> 'd list * 'd list -> int, concat:'e list list -> 'e list, drop:'f list * int -> 'f list, exists:('g -> bool) -> 'g list -> bool, filter:('h -> bool) -> 'h list -> 'h list, find:('i -> bool) -> 'i list -> 'i, foldl:('j * 'k -> 'k) -> 'k -> 'j list -> 'k, foldr:('l * 'm -> 'm) -> 'm -> 'l list -> 'm, getItem:'n list -> 'n * 'n list, hd:'o list -> 'o, last:'p list -> 'p, length:'q list -> int, map:('r -> 's) -> 'r list -> 's list, mapPartial:('t -> 'u) -> 't list -> 'u list, nil:'v list, nth:'w list * int -> 'w, null:'x list -> bool, op @:'y list * 'y list -> 'y list, partition:('z -> bool) -> 'z list -> 'z list * 'z list, rev:'ba list -> 'ba list, revAppend:'bb list * 'bb list -> 'bb list, tabulate:int * (int -> 'bc) -> 'bc list, take:'bd list * int -> 'bd list, tl:'be list -> 'be list} List.rev; val it = fn : 'a list -> 'a list @@ -341,6 +369,13 @@ List.at ([], []); val it = [] : 'a list +[1] @ [2, 3]; +val it = [1,2,3] : int list + +[] @ []; +val it = [] : 'a list + + (*) val hd : 'a list -> 'a List.hd; val it = fn : 'a list -> 'a @@ -793,9 +828,9 @@ val it = fn : unit -> (string * string) list Sys.env (); val it = - [ + [("General","{op o:forall 'a 'b 'c. ('b -> 'c) * ('a -> 'b) -> 'a -> 'c}"), ("List", - "{all:forall 'a. ('a -> bool) -> 'a list -> bool, app:forall 'a. ('a -> unit) -> 'a list -> unit, at:forall 'a. 'a list * 'a list -> 'a list, collate:forall 'a. ('a * 'a -> int) -> 'a list * 'a list -> int, concat:forall 'a. 'a list list -> 'a list, drop:forall 'a. 'a list * int -> 'a list, exists:forall 'a. ('a -> bool) -> 'a list -> bool, filter:forall 'a. ('a -> bool) -> 'a list -> 'a list, find:forall 'a. ('a -> bool) -> 'a list -> 'a, foldl:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, foldr:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, getItem:forall 'a. 'a list -> 'a * 'a list, hd:forall 'a. 'a list -> 'a, last:forall 'a. 'a list -> 'a, length:forall 'a. 'a list -> int, map:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, mapPartial:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, nil:forall 'a. 'a list, nth:forall 'a. 'a list * int -> 'a, null:forall 'a. 'a list -> bool, partition:forall 'a. ('a -> bool) -> 'a list -> 'a list * 'a list, rev:forall 'a. 'a list -> 'a list, revAppend:forall 'a. 'a list * 'a list -> 'a list, tabulate:forall 'a. int * (int -> 'a) -> 'a list, take:forall 'a. 'a list * int -> 'a list, tl:forall 'a. 'a list -> 'a list}"), + "{all:forall 'a. ('a -> bool) -> 'a list -> bool, app:forall 'a. ('a -> unit) -> 'a list -> unit, at:forall 'a. 'a list * 'a list -> 'a list, collate:forall 'a. ('a * 'a -> int) -> 'a list * 'a list -> int, concat:forall 'a. 'a list list -> 'a list, drop:forall 'a. 'a list * int -> 'a list, exists:forall 'a. ('a -> bool) -> 'a list -> bool, filter:forall 'a. ('a -> bool) -> 'a list -> 'a list, find:forall 'a. ('a -> bool) -> 'a list -> 'a, foldl:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, foldr:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, getItem:forall 'a. 'a list -> 'a * 'a list, hd:forall 'a. 'a list -> 'a, last:forall 'a. 'a list -> 'a, length:forall 'a. 'a list -> int, map:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, mapPartial:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, nil:forall 'a. 'a list, nth:forall 'a. 'a list * int -> 'a, null:forall 'a. 'a list -> bool, op @:forall 'a. 'a list * 'a list -> 'a list, partition:forall 'a. ('a -> bool) -> 'a list -> 'a list * 'a list, rev:forall 'a. 'a list -> 'a list, revAppend:forall 'a. 'a list * 'a list -> 'a list, tabulate:forall 'a. int * (int -> 'a) -> 'a list, take:forall 'a. 'a list * int -> 'a list, tl:forall 'a. 'a list -> 'a list}"), ("Relational", "{count:forall 'a. 'a list -> int, max:forall 'a. 'a list -> 'a, min:forall 'a. 'a list -> 'a, sum:forall 'a. 'a list -> 'a}"), ("String", @@ -804,8 +839,8 @@ val it = ("count","forall 'a. 'a list -> int"), ("env","unit -> (string * string) list"),("false","bool"), ("ignore","forall 'a. 'a -> unit"),("it","(string * string) list"), - ("map","forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list"), - ("max","forall 'a. 'a list -> 'a"),...] : (string * string) list + ("map","forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list"),...] + : (string * string) list env; @@ -813,9 +848,9 @@ val it = fn : unit -> (string * string) list env (); val it = - [ + [("General","{op o:forall 'a 'b 'c. ('b -> 'c) * ('a -> 'b) -> 'a -> 'c}"), ("List", - "{all:forall 'a. ('a -> bool) -> 'a list -> bool, app:forall 'a. ('a -> unit) -> 'a list -> unit, at:forall 'a. 'a list * 'a list -> 'a list, collate:forall 'a. ('a * 'a -> int) -> 'a list * 'a list -> int, concat:forall 'a. 'a list list -> 'a list, drop:forall 'a. 'a list * int -> 'a list, exists:forall 'a. ('a -> bool) -> 'a list -> bool, filter:forall 'a. ('a -> bool) -> 'a list -> 'a list, find:forall 'a. ('a -> bool) -> 'a list -> 'a, foldl:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, foldr:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, getItem:forall 'a. 'a list -> 'a * 'a list, hd:forall 'a. 'a list -> 'a, last:forall 'a. 'a list -> 'a, length:forall 'a. 'a list -> int, map:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, mapPartial:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, nil:forall 'a. 'a list, nth:forall 'a. 'a list * int -> 'a, null:forall 'a. 'a list -> bool, partition:forall 'a. ('a -> bool) -> 'a list -> 'a list * 'a list, rev:forall 'a. 'a list -> 'a list, revAppend:forall 'a. 'a list * 'a list -> 'a list, tabulate:forall 'a. int * (int -> 'a) -> 'a list, take:forall 'a. 'a list * int -> 'a list, tl:forall 'a. 'a list -> 'a list}"), + "{all:forall 'a. ('a -> bool) -> 'a list -> bool, app:forall 'a. ('a -> unit) -> 'a list -> unit, at:forall 'a. 'a list * 'a list -> 'a list, collate:forall 'a. ('a * 'a -> int) -> 'a list * 'a list -> int, concat:forall 'a. 'a list list -> 'a list, drop:forall 'a. 'a list * int -> 'a list, exists:forall 'a. ('a -> bool) -> 'a list -> bool, filter:forall 'a. ('a -> bool) -> 'a list -> 'a list, find:forall 'a. ('a -> bool) -> 'a list -> 'a, foldl:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, foldr:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, getItem:forall 'a. 'a list -> 'a * 'a list, hd:forall 'a. 'a list -> 'a, last:forall 'a. 'a list -> 'a, length:forall 'a. 'a list -> int, map:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, mapPartial:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, nil:forall 'a. 'a list, nth:forall 'a. 'a list * int -> 'a, null:forall 'a. 'a list -> bool, op @:forall 'a. 'a list * 'a list -> 'a list, partition:forall 'a. ('a -> bool) -> 'a list -> 'a list * 'a list, rev:forall 'a. 'a list -> 'a list, revAppend:forall 'a. 'a list * 'a list -> 'a list, tabulate:forall 'a. int * (int -> 'a) -> 'a list, take:forall 'a. 'a list * int -> 'a list, tl:forall 'a. 'a list -> 'a list}"), ("Relational", "{count:forall 'a. 'a list -> int, max:forall 'a. 'a list -> 'a, min:forall 'a. 'a list -> 'a, sum:forall 'a. 'a list -> 'a}"), ("String", @@ -824,5 +859,5 @@ val it = ("count","forall 'a. 'a list -> int"), ("env","unit -> (string * string) list"),("false","bool"), ("ignore","forall 'a. 'a -> unit"),("it","(string * string) list"), - ("map","forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list"), - ("max","forall 'a. 'a list -> 'a"),...] : (string * string) list + ("map","forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list"),...] + : (string * string) list diff --git a/src/test/resources/script/simple.sml b/src/test/resources/script/simple.sml index 8a23c918..0bb2e151 100644 --- a/src/test/resources/script/simple.sml +++ b/src/test/resources/script/simple.sml @@ -71,7 +71,7 @@ str "" 20 []; [{a="aaaaaaaaaaaaaaaaaaaaaa",b="bbbbbbbbbbbbbb",c="cccccccccccccccccc",d="ddddddddd"}]; [{a="aaaaaaaaaaaaaaaaaaaaaa",b="bbbbbbbbbbbbbb",c="cccccccccccccccccc",d="ddddddddd"},{a="aaaaaaaaaaaaaaaaaaaaaa",b="bbbbbbbbbbbbbb",c="cccccccccccccccccc",d="ddddddddd"}]; -{a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o=15,p=16}; +{a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o_=15,p=16}; (* sml prints the following, but we cannot split types yet: diff --git a/src/test/resources/script/simple.sml.out b/src/test/resources/script/simple.sml.out index 68430ed4..934a215e 100644 --- a/src/test/resources/script/simple.sml.out +++ b/src/test/resources/script/simple.sml.out @@ -166,10 +166,10 @@ val it = {a="aaaaaaaaaaaaaaaaaaaaaa",b="bbbbbbbbbbbbbb",c="cccccccccccccccccc", d="ddddddddd"}] : {a:string, b:string, c:string, d:string} list -{a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o=15,p=16}; +{a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o_=15,p=16}; val it = - {a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o=15,p=16} - : {a:int, b:int, c:int, d:int, e:int, f:int, g:int, h:int, i:int, j:int, k:int, l:int, m:int, n:int, o:int, p:int} + {a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o_=15,p=16} + : {a:int, b:int, c:int, d:int, e:int, f:int, g:int, h:int, i:int, j:int, k:int, l:int, m:int, n:int, o_:int, p:int} (* sml prints the following, but we cannot split types yet: