Skip to content

Commit

Permalink
[MOREL-230] Allow lambda (fn) to have multiple branches, similar to…
Browse files Browse the repository at this point in the history
… `case`

Also fix type derivation when a nilary constructor (e.g. LESS
from the order datatype) is used as a pattern. Previously it
was regarded as an identifier.
  • Loading branch information
julianhyde committed Nov 18, 2024
1 parent f5a72da commit 0b5d53a
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 7 deletions.
11 changes: 10 additions & 1 deletion src/main/java/net/hydromatic/morel/compile/TypeResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,16 @@ private Ast.Pat deducePatType(TypeEnv env, Ast.Pat pat,
return reg(pat, v, toTerm(PrimitiveType.STRING));

case ID_PAT:
termMap.put((Ast.IdPat) pat, v);
final Ast.IdPat idPat = (Ast.IdPat) pat;
final Pair<DataType, Type.Key> pair1 =
typeSystem.lookupTyCon(idPat.name);
if (pair1 != null) {
// It is a zero argument constructor, e.g. the LESS constructor of the
// order type
final DataType dataType0 = pair1.left;
return reg(pat, v, toTerm(dataType0, Subst.EMPTY));
}
termMap.put(idPat, v);
// fall through

case WILDCARD_PAT:
Expand Down
6 changes: 3 additions & 3 deletions src/main/javacc/MorelParser.jj
Original file line number Diff line number Diff line change
Expand Up @@ -540,11 +540,11 @@ OrderItem orderItem() :
Exp fn() :
{
final Span span;
final Match match;
final List<Match> matchList;
}
{
<FN> { span = Span.of(pos()); } match = match() {
return ast.fn(span.end(this), match);
<FN> { span = Span.of(pos()); } matchList = matchList() {
return ast.fn(span.end(this), matchList);
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/test/java/net/hydromatic/morel/MainTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ public class MainTest {
ml("fn _ => 42").assertParseSame();
ml("fn x => case x of 0 => 1 | _ => 2").assertParseSame();
ml("fn () => 42").assertParseSame();
ml("fn [] => 0 | x :: _ => x + 1").assertParseSame();

// apply
ml("(fn x => x + 1) 3").assertParseSame();
Expand All @@ -315,7 +316,7 @@ public class MainTest {
ml("1e2").assertParse("1E+2");
ml("1E2").assertParse("1E+2");

// keywords such as 'val' and 'in' are case sensitive
// keywords such as 'val' and 'in' are case-sensitive
ml("let val x = 1 in x + 1 end").assertParseSame();
ml("let VAL x = 1 in x + 1 end")
.assertParseThrowsParseException(
Expand Down
6 changes: 4 additions & 2 deletions src/test/resources/script/datatype.smli
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,10 @@ f NONE;
> val it = ~1 : int

(*) Test cases from [MOREL-126].
fun f ((SOME i) :: NIL) = i + 1
| f NIL = 0;
fun f ((SOME i) :: nil) = i + 1
| f nil = 0;
> 0.0-0.0 Warning: match nonexhaustive
> raised at: 0.0-0.0
> val f = fn : int option list -> int
f [];
> val it = 0 : int
Expand Down
101 changes: 101 additions & 0 deletions src/test/resources/script/match.smli
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,105 @@ ack 2 3;
ack 3 3;
> val it = 61 : int

(* Various types, exhaustive & non-exhaustive ------------------ *)
(*) From https://smlhelp.github.io/book/docs/start/syntax/

(*) unit, exhaustive
(fn () => ());
> val it = fn : unit -> unit

(*) bool, non-exhaustive
(fn true => 1);
> stdIn:3.2-3.14 Warning: match nonexhaustive
> raised at: stdIn:3.2-3.14
> val it = fn : bool -> int

(*) bool, exhaustive
(fn true => 1
| false => 0);
> val it = fn : bool -> int

(*) order, non-exhaustive
(fn LESS => ~1);
> stdIn:3.2-3.15 Warning: match nonexhaustive
> raised at: stdIn:3.2-3.15
> val it = fn : order -> int

(*) order, non-exhaustive
(fn LESS => ~1
| EQUAL => 0);
> stdIn:3.2-4.15 Warning: match nonexhaustive
> raised at: stdIn:3.2-4.15
> val it = fn : order -> int

(*) order, exhaustive
(fn LESS => ~1
| EQUAL => 0
| GREATER => 1);
> val it = fn : order -> int

(*) int, non-exhaustive
(fn 0 => true);
> stdIn:3.2-3.14 Warning: match nonexhaustive
> raised at: stdIn:3.2-3.14
> val it = fn : int -> bool

(*) order, exhaustive
(fn 0 => true
| _ => false);
> val it = fn : int -> bool

(*) int list, non-exhaustive
(fn x::_ => x + 1);
> stdIn:3.2-3.18 Warning: match nonexhaustive
> raised at: stdIn:3.2-3.18
> val it = fn : int list -> int

(*) int list, exhaustive
(fn [] => 0
| x::_ => x + 1);
> val it = fn : int list -> int

(*) int * bool, non-exhaustive
(fn (0,b) => true andalso b);
> stdIn:3.2-3.28 Warning: match nonexhaustive
> raised at: stdIn:3.2-3.28
> val it = fn : int * bool -> bool

(*) int * bool, exhaustive
(fn (0,b) => true andalso b
| (n,_) => false);
> val it = fn : int * bool -> bool

(* Exhaustive function of option list ------------------------- *)
(*) Non-exhaustive function
fun f [(SOME i)] = i
| f [] = 0;
> 0.0-0.0 Warning: match nonexhaustive
> raised at: 0.0-0.0
> val f = fn : int option list -> int

(*) Add NONE, still non-exhaustive
fun f [(SOME i)] = i
| f [NONE] = ~1
| f [] = 0;
> 0.0-0.0 Warning: match nonexhaustive
> raised at: 0.0-0.0
> val f = fn : int option list -> int

(*) Allow list longer than 1, still non-exhaustive
fun f [(SOME i)] = i
| f [] = 0
| f (one :: two :: rest) = ~2;
> 0.0-0.0 Warning: match nonexhaustive
> raised at: 0.0-0.0
> val f = fn : int option list -> int

(*) Exhaustive
fun f [(SOME i)] = i
| f [NONE] = ~1
| f [] = 0
| f (one :: two :: rest) = ~2;
> val f = fn : int option list -> int

(*) End match.smli
6 changes: 6 additions & 0 deletions src/test/resources/script/type.smli
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,12 @@ in
end;
*)

(*) Lambda applied to datatype
fn LESS => ~1
| EQUAL => 0
| GREATER => 1;
> val it = fn : order -> int

(*) Lambda with record argument
fn {a, b} => if b then a else a + 1;
> val it = fn : {a:int, b:bool} -> int
Expand Down

0 comments on commit 0b5d53a

Please sign in to comment.