Skip to content

Commit

Permalink
[MOREL-37] Raise exceptions in built-in functions
Browse files Browse the repository at this point in the history
Built-in functions now raise exceptions Empty, Size,
Subscript. (It is not yet possible to define, raise or handle
exceptions in user code.)

Move class ComparableSingletonList to top-level.
  • Loading branch information
julianhyde committed May 12, 2020
1 parent d5fb0ae commit 0cedd58
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 131 deletions.
69 changes: 43 additions & 26 deletions src/main/java/net/hydromatic/morel/compile/BuiltIn.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@ public enum BuiltIn {
/** Function "abs", of type "int → int". */
ABS(null, "abs", ts -> ts.fnType(INT, INT)),

/** Function "ignore", of type "α → unit". */
IGNORE(null, "ignore", ts ->
ts.forallType(1, h -> ts.fnType(h.get(0), UNIT))),

/** Infix operator "^", of type "string * string → string". */
OP_CARET(null, "op ^", ts -> ts.fnType(ts.tupleType(STRING, STRING), STRING)),

Expand Down Expand Up @@ -170,6 +166,10 @@ public enum BuiltIn {
ts.forallType(1, h ->
ts.fnType(ts.tupleType(h.get(0), h.get(0)), h.get(0)))),

/** Function "General.ignore", of type "α → unit". */
IGNORE("General", "ignore", "ignore", ts ->
ts.forallType(1, h -> ts.fnType(h.get(0), UNIT))),

/** Operator "General.op o", of type "(β → γ) *
* (α → β) → α → γ"
*
Expand All @@ -195,7 +195,9 @@ public enum BuiltIn {
/** Function "String.sub", of type "string * int → char".
*
* <p>"sub (s, i)" returns the i(th) character of s, counting from zero. This
* raises {@code Subscript} if i &lt; 0 or |s| &le; i. */
* raises
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#SUBSCRIPT Subscript}
* if i &lt; 0 or |s| &le; i. */
STRING_SUB("String", "sub", ts -> ts.fnType(ts.tupleType(STRING, INT), CHAR)),

/** Function "String.extract", of type "string * int * int option &rarr;
Expand All @@ -205,13 +207,15 @@ public enum BuiltIn {
*
* <p>"extract (s, i, NONE)" and "extract (s, i, SOME j)" return substrings of
* s. The first returns the substring of s from the i(th) character to the end
* of the string, i.e., the string s[i..|s|-1]. This raises {@code Subscript}
* of the string, i.e., the string s[i..|s|-1]. This raises
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#SUBSCRIPT Subscript}
* if i &lt; 0 * or |s| &lt; i.
*
* <p>The second form returns the substring of size j starting at index i,
* i.e., the string s[i..i+j-1]. It raises {@code Subscript} if i &lt; 0 or j
* &lt; 0 or |s| &lt; i + j. Note that, if defined, extract returns the empty
* string when i = |s|. */
* i.e., the string s[i..i+j-1]. It raises
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#SUBSCRIPT Subscript}
* if i &lt; 0 or j &lt; 0 or |s| &lt; i + j. Note that, if defined, extract
* returns the empty string when i = |s|. */
STRING_EXTRACT("String", "extract", ts ->
ts.fnType(ts.tupleType(STRING, INT), STRING)),

Expand All @@ -226,16 +230,18 @@ public enum BuiltIn {
/** Function "String.concat", of type "string list &rarr; string".
*
* <p>"concat l" is the concatenation of all the strings in l. This raises
* {@code Size} if the sum of all the sizes is greater than maxSize. */
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#SIZE Size}
* if the sum of all the sizes is greater than maxSize. */
STRING_CONCAT("String", "concat", ts ->
ts.fnType(ts.listType(STRING), STRING)),

/** Function "String.concatWith", of type "string &rarr; string list &rarr;
* string".
*
* <p>"concatWith s l" returns the concatenation of the strings in the list l
* using the string s as a separator. This raises {@code Size} if the size of
* the resulting string would be greater than maxSize. */
* using the string s as a separator. This raises
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#SIZE Size}
* if the size of the resulting string would be greater than maxSize. */
STRING_CONCAT_WITH("String", "concatWith", ts ->
ts.fnType(STRING, ts.listType(STRING), STRING)),

Expand All @@ -247,7 +253,8 @@ public enum BuiltIn {
/** Function "String.implode", of type "char list &rarr; string".
*
* <p>"implode l" generates the string containing the characters in the list
* l. This is equivalent to concat (List.map str l). This raises {@code Size}
* l. This is equivalent to {@code concat (List.map str l)}. This raises
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#SIZE Size}
* if the resulting string would have size greater than maxSize. */
STRING_IMPLODE("String", "implode", ts ->
ts.fnType(ts.listType(CHAR), STRING)),
Expand Down Expand Up @@ -339,24 +346,27 @@ public enum BuiltIn {

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

/** Function "List.tl", of type "&alpha; list &rarr; &alpha; list".
*
* <p>"tl l" returns all but the first element of l. It raises {@code Empty}
* <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.
*/
LIST_TL("List", "tl", ts ->
ts.forallType(1, h -> ts.fnType(h.list(0), h.list(0)))),

/** Function "List.last", of type "&alpha; list &rarr; &alpha;".
*
* <p>"last l" returns the last element of l. It raises {@code Empty} if l is
* nil.
* <p>"last l" returns the last element of l. It raises
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#EMPTY empty}
* if l is nil.
*/
LIST_LAST("List", "last", ts ->
ts.forallType(1, h -> ts.fnType(h.list(0), h.get(0)))),
Expand All @@ -366,8 +376,9 @@ public enum BuiltIn {
*
* <p>"getItem l" returns {@code NONE} if the list is empty, and
* {@code SOME(hd l,tl l)} otherwise. This function is particularly useful for
* creating value readers from lists of characters. For example, Int.scan
* StringCvt.DEC getItem has the type {@code (int,char list) StringCvt.reader}
* creating value readers from lists of characters. For example,
* {@code Int.scan StringCvt.DEC getItem} has the type
* {@code (int, char list) StringCvt.reader}
* and can be used to scan decimal integers from lists of characters.
*/
// TODO: make it return an option
Expand All @@ -378,17 +389,20 @@ public enum BuiltIn {
/** Function "List.nth", of type "&alpha; list * int &rarr; &alpha;".
*
* <p>"nth (l, i)" returns the i(th) element of the list l, counting from 0.
* It raises {@code Subscript} if i &lt; 0 or i &ge; length l. We have
* nth(l,0) = hd l, ignoring exceptions.
* It raises
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#SUBSCRIPT Subscript}
* if i &lt; 0 or i &ge; length l.
* We have {@code nth(l,0) = hd l}, ignoring exceptions.
*/
LIST_NTH("List", "nth", ts ->
ts.forallType(1, h -> ts.fnType(ts.tupleType(h.list(0), INT), h.get(0)))),

/** Function "List.take", of type "&alpha; list * int &rarr; &alpha; list".
*
* <p>"take (l, i)" returns the first i elements of the list l. It raises
* {@code Subscript} if i &lt; 0 or i &gt; length l.
* We have take(l, length l) = l.
* {@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}.
*/
LIST_TAKE("List", "take", ts ->
ts.forallType(1, h ->
Expand All @@ -399,7 +413,9 @@ public enum BuiltIn {
* <p>"drop (l, i)" returns what is left after dropping the first i elements
* of the list l.
*
* <p>It raises {@code Subscript} if i &lt; 0 or i &gt; length 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.
Expand Down Expand Up @@ -552,7 +568,8 @@ public enum BuiltIn {
*
* <p>"tabulate (n, f)" returns a list of length n equal to
* {@code [f(0), f(1), ..., f(n-1)]}, created from left to right. It raises
* {@code Size} if n &lt; 0.
* {@link net.hydromatic.morel.eval.Codes.BuiltInExn#SIZE Size}
* if n &lt; 0.
*/
LIST_TABULATE("List", "tabulate", ts ->
ts.forallType(1, h ->
Expand Down
41 changes: 9 additions & 32 deletions src/main/java/net/hydromatic/morel/compile/Compiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@
import net.hydromatic.morel.type.ListType;
import net.hydromatic.morel.type.RecordType;
import net.hydromatic.morel.type.Type;
import net.hydromatic.morel.util.ComparableSingletonList;
import net.hydromatic.morel.util.Ord;
import net.hydromatic.morel.util.Pair;
import net.hydromatic.morel.util.TailList;

import java.math.BigDecimal;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -480,7 +480,7 @@ private void compileTyCon(Environment env, Type dataType,
final Type type = Objects.requireNonNull(typeMap.getType(tyCon));
final Object value;
if (tyCon.type == null) {
value = Codes.constant(new ComparableSingletonList<>(tyCon.id.name));
value = Codes.constant(ComparableSingletonList.of(tyCon.id.name));
} else {
value = Codes.tyCon(dataType, tyCon.id.name);
}
Expand Down Expand Up @@ -609,10 +609,14 @@ private void compileValBind(Environment env, Ast.ValBind valBind,
final Type type0 = typeMap.getType(valBind.e);
final Type type = typeMap.typeSystem.ensureClosed(type0);
actions.add((output, outBindings, evalEnv) -> {
final Object o = code.eval(evalEnv);
outBindings.add(Binding.of(name, type, o));
final StringBuilder buf = new StringBuilder();
Pretty.pretty(buf, type, new Pretty.TypedVal(name, o, type0));
try {
final Object o = code.eval(evalEnv);
outBindings.add(Binding.of(name, type, o));
Pretty.pretty(buf, type, new Pretty.TypedVal(name, o, type0));
} catch (Codes.MorelRuntimeException e) {
e.describeTo(buf);
}
output.add(buf.toString());
});
}
Expand Down Expand Up @@ -649,33 +653,6 @@ public Object eval(EvalEnv env) {
return refCode.eval(env);
}
}

/** A comparable singleton list.
*
* @param <E> Element type */
private static class ComparableSingletonList<E extends Comparable<E>>
extends AbstractList<E>
implements Comparable<ComparableSingletonList<E>> {
private final E element;

ComparableSingletonList(E element) {
this.element = Objects.requireNonNull(element);
}

@Override public E get(int index) {
assert index == 0;
return element;
}

@Override public int size() {
return 1;
}

@Override public int compareTo(ComparableSingletonList<E> o) {
return element.compareTo(o.element);
}
}

}

// End Compiler.java
15 changes: 15 additions & 0 deletions src/main/java/net/hydromatic/morel/compile/Pretty.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package net.hydromatic.morel.compile;

import net.hydromatic.morel.foreign.RelList;
import net.hydromatic.morel.type.DataType;
import net.hydromatic.morel.type.ForallType;
import net.hydromatic.morel.type.ListType;
import net.hydromatic.morel.type.PrimitiveType;
Expand Down Expand Up @@ -239,6 +240,20 @@ private static StringBuilder pretty2(@Nonnull StringBuilder buf,
return pretty2(buf, indent, lineEnd, depth + 1, ((ForallType) type).type,
value);

case DATA_TYPE:
final DataType dataType = (DataType) type;
//noinspection unchecked
list = (List) value;
final String tyConName = (String) list.get(0);
buf.append(tyConName);
final Type typeConArgType = dataType.typeConstructors.get(tyConName);
if (list.size() == 2) {
final Object arg = list.get(1);
buf.append(' ');
pretty2(buf, indent, lineEnd, depth + 1, typeConArgType, arg);
}
return buf;

default:
return buf.append(value);
}
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/net/hydromatic/morel/compile/TypeMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Lists;

import net.hydromatic.morel.ast.AstNode;
import net.hydromatic.morel.type.RecordType;
Expand Down Expand Up @@ -131,7 +132,12 @@ public Type visit(Unifier.Sequence sequence) {
default:
final Type type = typeMap.typeSystem.lookupOpt(sequence.operator);
if (type != null) {
return type;
if (sequence.terms.isEmpty()) {
return type;
}
final List<Type> types =
Lists.transform(sequence.terms, t -> t.accept(this));
return typeMap.typeSystem.apply(type, types);
}
if (sequence.operator.startsWith(TypeResolver.RECORD_TY_CON)) {
// E.g. "record:a:b" becomes record type "{a:t0, b:t1}".
Expand Down
Loading

0 comments on commit 0cedd58

Please sign in to comment.