Skip to content

Commit

Permalink
[MOREL-36] In record pattern, make labels optional, and disallow "...…
Browse files Browse the repository at this point in the history
…" anywhere but end
  • Loading branch information
julianhyde committed May 8, 2020
1 parent dba092b commit f3c2385
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 38 deletions.
2 changes: 1 addition & 1 deletion docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ In Standard ML but not in Morel:
| '<b>[</b>' <i>pat<sub>1</sub></i> <b>,</b> ... <b>,</b> <i>pat<sub>n</sub></i> '<b>]</b>' list (n &ge; 0)
<i>patrow</i> &rarr; '<b>...</b>' wildcard
| <i>lab</i> <b>=</b> <i>pat</i> [<b>,</b> <i>patrow</i>] pattern
| <i>id</i> [<b>,</b> <i>patrow</i>] variable
| <i>id</i> [<b>,</b> <i>patrow</i>] label as variable
</pre>

### Types
Expand Down
56 changes: 31 additions & 25 deletions src/main/javacc/MorelParser.jj
Original file line number Diff line number Diff line change
Expand Up @@ -1095,28 +1095,36 @@ Pat atomPat() :
<LBRACE> {
span = Span.of(getPos());
final Map<String, Pat> map = new LinkedHashMap<>();
final boolean[] ellipsis = {false};
boolean ellipsis = false;
}
recordPat(ellipsis, map)
(
<COMMA> recordPat(ellipsis, map)
)*
[
<ELLIPSIS> { ellipsis = true; }
|
recordPat(map)
(
LOOKAHEAD(2)
<COMMA> recordPat(map)
)*
[ <COMMA> <ELLIPSIS> { ellipsis = true; } ]
]
<RBRACE> {
return ast.recordPat(span.end(this), ellipsis[0], map);
return ast.recordPat(span.end(this), ellipsis, map);
}
}

/** Parses a "label = pat" inside a record pattern. */
void recordPat(boolean[] ellipsis, Map<String, Pat> map) :
void recordPat(Map<String, Pat> map) :
{
final String id;
final Ast.Pat pat;
}
{
<ELLIPSIS> { ellipsis[0] = true; }
|
( <NON_NEGATIVE_INTEGER_LITERAL> | <IDENTIFIER> ) { id = token.image; }
<EQ> pat = pat() { map.put(id, pat); }
(
<EQ> pat = pat() { map.put(id, pat); }
|
{ map.put(id, ast.idPat(getPos(), id)); }
)
}

/** Parses a type. */
Expand All @@ -1135,23 +1143,21 @@ Ast.Type atomicType() :
<LPAREN> {
span = Span.of(getPos());
}
type = type()
(
type = type()
<RPAREN> { return type; }
|
{
final List<Type> list = new ArrayList<>();
list.add(type);
Type type2;
}
(
<RPAREN> { return type; }
|
{
final List<Type> list = new ArrayList<>();
list.add(type);
Type type2;
}
(
<COMMA> type2 = type() { list.add(type2); }
)+
<RPAREN> {
return ast.compositeType(span.end(this), list);
}
)
<COMMA> type2 = type() { list.add(type2); }
)+
<RPAREN> {
return ast.compositeType(span.end(this), list);
}
)
}

Expand Down
23 changes: 21 additions & 2 deletions src/test/java/net/hydromatic/morel/MainTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ public void describeTo(Description description) {
ml("case 1 of 0 => \"zero\" | _ => \"nonzero\"").assertParseSame();
ml("case {a = 1, b = 2} of {a = 1, ...} => 1").assertParseSame();
ml("case {a = 1, b = 2} of {...} => 1").assertParseSame();
ml("case {a = 1, b = 2} of {..., a = 3} => 1")
ml("case {a = 1, b = 2} of {a = 3, ...} => 1")
.assertParse("case {a = 1, b = 2} of {a = 3, ...} => 1");

// fn
Expand Down Expand Up @@ -346,6 +346,13 @@ public void describeTo(Description description) {
.assertParseThrows(
throwsA(IllegalArgumentException.class,
is("cannot derive label for expression b + c")));

ml("case x of {a = a, b} => a + b")
.assertParse("case x of {a = a, b = b} => a + b");
ml("case x of {a, b = 2, ...} => a + b")
.assertParse("case x of {a = a, b = 2, ...} => a + b");
ml("fn {a, b = 2, ...} => a + b")
.assertParse("fn {a = a, b = 2, ...} => a + b");
}

/** Tests the name of {@link TypeVar}. */
Expand Down Expand Up @@ -414,6 +421,8 @@ public void describeTo(Description description) {
.assertType("int -> int * int -> int");
ml("fn (x, y) => (x + 1, fn z => (x + z, y + z), y)")
.assertType("int * int -> int * (int -> int * int) * int");
ml("fn {a = x, b = y, c} => x + y")
.assertType("{a:int, b:int, c:'a} -> int");
}

@Test public void testTypeLetRecFn() {
Expand Down Expand Up @@ -848,9 +857,19 @@ private <T extends Throwable> Matcher<Throwable> throwsA(Class<T> clazz,
@Test public void testRecordMatch() {
final String ml = "case {a=1, b=2, c=3}\n"
+ " of {a=2, b=2, c=3} => 0\n"
+ " | {a=1, ..., c=x} => x\n"
+ " | {a=1, c=x, ...} => x\n"
+ " | _ => ~1";
ml(ml).assertEval(is(3));
ml("fn {} => 0").assertParseSame();
ml("fn {a=1, ..., c=2}")
.assertParseThrows(
throwsA(ParseException.class,
containsString("Encountered \" \",\" \", \"\" at line 1, "
+ "column 13.")));
ml("fn {...} => 0").assertParseSame();
ml("fn {a = a, ...} => 0").assertParseSame();
ml("fn {a, b = {c, d}, ...} => 0")
.assertParse("fn {a = a, b = {c = c, d = d}, ...} => 0");
}

@Test public void testRecordCase() {
Expand Down
4 changes: 2 additions & 2 deletions src/test/resources/script/fixedPoint.sml
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,9 @@ val edges =
fun shortest_path edges =
let
val vertices =
from v in (from {source = s, target = _, weight = _} in edges yield s)
from v in (from {source, target, weight} in edges yield source)
union
(from {source = _, target = t, weight = _} in edges yield t)
(from {source, target, weight} in edges yield target)
group v
val edges0 =
from e in edges
Expand Down
4 changes: 2 additions & 2 deletions src/test/resources/script/fixedPoint.sml.out
Original file line number Diff line number Diff line change
Expand Up @@ -354,9 +354,9 @@ val edges =
fun shortest_path edges =
let
val vertices =
from v in (from {source = s, target = _, weight = _} in edges yield s)
from v in (from {source, target, weight} in edges yield source)
union
(from {source = _, target = t, weight = _} in edges yield t)
(from {source, target, weight} in edges yield target)
group v
val edges0 =
from e in edges
Expand Down
15 changes: 12 additions & 3 deletions src/test/resources/script/relational.sml
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ from (left, 2) in [(1, 2), (3, 4), (5, 2)]
(*) Record pattern
from {b = b, a = a} in [{a=1,b=2}];
from {b, a} in [{a=1,b=2}];
from {a = a, b = b} in [{a=1,b=2}];
from {b = a, a = b} in [{a=1,b=2}];
from {b = c, c = a, a = b} in [{a=1,b=2,c=3}];
Expand All @@ -555,8 +556,9 @@ from {a = c, b = true, c = a} in [{a=1,b=true,c=3}];
from {a = c, b = true, c = a} in [{a=1,b=true,c=3},{a=1,b=true,c=4}] group c compute sum of a;
from {a = a, b = b, c = _} in [{a=1,b=true,c=3},{a=1,b=true,c=4}];
from {a = a, b = b, c = _} in [{a=1,b=true,c=3},{a=1,b=true,c=4}], d in ["a", "b"];
from {a = a, ..., b = b} in [{a=1,b=true,c=3},{a=1,b=true,c=4}];
from {a = a, ..., c = c} in [{a=1,b=true,c=3},{a=1,b=true,c=4}];
from {a = a, b = b, ...} in [{a=1,b=true,c=3},{a=1,b=true,c=4}];
from {a = a, c = c, ...} in [{a=1,b=true,c=3},{a=1,b=true,c=4}];
from {a, c, ...} in [{a=1,b=true,c=3},{a=1,b=true,c=4}];
from {a = a, c = c, ...} in [{a=1,b=true,c=3},{a=1,b=true,c=4}];
from {b = y, ...} in [{a=1,b=2}];
from {b = y, a = (p, q)} in [{a=(1,true),b=2}];
Expand All @@ -577,6 +579,13 @@ fun listFields lists =
listFields [];
listFields [{a = 1, b = 2}, {a = 3, b = 0}, {a = 4, b = 5}];
(*) As above, using abbreviated record pattern
fun listFields2 lists =
from {a, b} in lists
yield a + 1;
listFields [];
listFields [{a = 1, b = 2}, {a = 3, b = 0}, {a = 4, b = 5}];
(*) Temporary functions
let
fun abbrev s =
Expand Down Expand Up @@ -638,7 +647,7 @@ from t in triples order t.foo 1; (* 1+2 < 2+6 = 3+5 *)
from t in triples order t.foo 1, t.x; (* (1+2,1) < (2+6,2) < (3+5,3) *)
from t in triples order t.foo 1, t.x desc; (* (1+2,~1) < (3+5,~3) < (2+6,~2) *)
from t in triples order t.foo ~1, t.y; (* (~1+2,2) < (~3+5,5) < (~2+6,6) *)
from {foo=foo,x=x,y=y} in triples
from {foo,x,y} in triples
order foo ~1, x; (* (~1+2,2) < (~3+5,5) < (~2+6,6) *)
from t1 in triples, t2 in triples
where t1.y = t2.y
Expand Down
25 changes: 22 additions & 3 deletions src/test/resources/script/relational.sml.out
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,9 @@ val it = [1,5] : int list
from {b = b, a = a} in [{a=1,b=2}];
val it = [{a=1,b=2}] : {a:int, b:int} list

from {b, a} in [{a=1,b=2}];
val it = [{a=1,b=2}] : {a:int, b:int} list

from {a = a, b = b} in [{a=1,b=2}];
val it = [{a=1,b=2}] : {a:int, b:int} list

Expand Down Expand Up @@ -886,10 +889,13 @@ val it =
[{a=1,b=true,d="a"},{a=1,b=true,d="b"},{a=1,b=true,d="a"},{a=1,b=true,d="b"}]
: {a:int, b:bool, d:string} list

from {a = a, ..., b = b} in [{a=1,b=true,c=3},{a=1,b=true,c=4}];
from {a = a, b = b, ...} in [{a=1,b=true,c=3},{a=1,b=true,c=4}];
val it = [{a=1,b=true},{a=1,b=true}] : {a:int, b:bool} list

from {a = a, ..., c = c} in [{a=1,b=true,c=3},{a=1,b=true,c=4}];
from {a = a, c = c, ...} in [{a=1,b=true,c=3},{a=1,b=true,c=4}];
val it = [{a=1,c=3},{a=1,c=4}] : {a:int, c:int} list

from {a, c, ...} in [{a=1,b=true,c=3},{a=1,b=true,c=4}];
val it = [{a=1,c=3},{a=1,c=4}] : {a:int, c:int} list

from {a = a, c = c, ...} in [{a=1,b=true,c=3},{a=1,b=true,c=4}];
Expand Down Expand Up @@ -938,6 +944,19 @@ listFields [{a = 1, b = 2}, {a = 3, b = 0}, {a = 4, b = 5}];
val it = [2,4,5] : int list


(*) As above, using abbreviated record pattern
fun listFields2 lists =
from {a, b} in lists
yield a + 1;
val listFields2 = fn : {a:int, b:'a} list -> int list

listFields [];
val it = [] : int list

listFields [{a = 1, b = 2}, {a = 3, b = 0}, {a = 4, b = 5}];
val it = [2,4,5] : int list


(*) Temporary functions
let
fun abbrev s =
Expand Down Expand Up @@ -1057,7 +1076,7 @@ from t in triples order t.foo ~1, t.y;
val it = [{foo=fn,x=1,y=2},{foo=fn,x=3,y=5},{foo=fn,x=2,y=6}]
: {foo:int -> int, x:int, y:int} list
(* (~1+2,2) < (~3+5,5) < (~2+6,6) *)
from {foo=foo,x=x,y=y} in triples
from {foo,x,y} in triples
order foo ~1, x;
val it = [{foo=fn,x=1,y=2},{foo=fn,x=3,y=5},{foo=fn,x=2,y=6}]
: {foo:int -> int, x:int, y:int} list
Expand Down

0 comments on commit f3c2385

Please sign in to comment.