valueMap;
diff --git a/src/main/java/net/hydromatic/morel/eval/MutableEvalEnv.java b/src/main/java/net/hydromatic/morel/eval/MutableEvalEnv.java
index 355422f8..c60c9268 100644
--- a/src/main/java/net/hydromatic/morel/eval/MutableEvalEnv.java
+++ b/src/main/java/net/hydromatic/morel/eval/MutableEvalEnv.java
@@ -20,7 +20,20 @@
/** An evaluation environment whose last entry is mutable. */
public interface MutableEvalEnv extends EvalEnv {
+ /** Puts a value into this environment. */
void set(Object value);
+
+ /** Puts a value into this environment in a way that may not succeed.
+ *
+ * For example, if this environment is based on the pattern (x, 2)
+ * then (1, 2) will succeed and will bind x to 1, but (3, 4) will fail.
+ *
+ *
The default implementation calls {@link #set} and always succeeds.
+ */
+ default boolean setOpt(Object value) {
+ set(value);
+ return true;
+ }
}
// End MutableEvalEnv.java
diff --git a/src/main/javacc/MorelParser.jj b/src/main/javacc/MorelParser.jj
index 3d8d64e9..3ed1a390 100644
--- a/src/main/javacc/MorelParser.jj
+++ b/src/main/javacc/MorelParser.jj
@@ -303,7 +303,7 @@ Exp from() :
{
final Span span;
Span stepSpan;
- final Map sources = new LinkedHashMap<>();
+ final Map sources = new LinkedHashMap<>();
final List steps = new ArrayList<>();
Exp filterExp;
Exp yieldExp = null;
@@ -351,14 +351,14 @@ Exp from() :
}
}
-void fromSource(Map sources) :
+void fromSource(Map sources) :
{
final Exp exp;
- final Id id;
+ final Pat pat;
}
{
- id = identifier() exp = expression() {
- sources.put(id, exp);
+ pat = pat() exp = expression() {
+ sources.put(pat, exp);
}
}
diff --git a/src/test/java/net/hydromatic/morel/MainTest.java b/src/test/java/net/hydromatic/morel/MainTest.java
index 4142852f..31a24f89 100644
--- a/src/test/java/net/hydromatic/morel/MainTest.java
+++ b/src/test/java/net/hydromatic/morel/MainTest.java
@@ -1469,6 +1469,15 @@ private Matcher throwsA(Class clazz,
.assertEvalIter(equalsOrdered(list()));
}
+ @Test public void testFromPattern() {
+ final String ml = "from (x, y) in [(1,2),(3,4),(3,0)] group sum = x + y";
+ final String expected = "from (x, y) in [(1, 2), (3, 4), (3, 0)] "
+ + "group sum = x + y";
+ ml(ml).assertParse(expected)
+ .assertType(is("int list"))
+ .assertEvalIter(equalsUnordered(3, 7));
+ }
+
/** Tests a program that uses an external collection from the "scott" JDBC
* database. */
@Test public void testScott() {
diff --git a/src/test/resources/script/relational.sml b/src/test/resources/script/relational.sml
index 5163ce4d..b96e90c9 100644
--- a/src/test/resources/script/relational.sml
+++ b/src/test/resources/script/relational.sml
@@ -429,6 +429,29 @@ from
group one = 1 compute two = sum of 2, three = sum of 3
yield {c1 = one, c5 = two + three};
+(*) Patterns left of 'in'
+fun sumPairs pairs =
+ from (left, right) in pairs
+ yield left + right;
+sumPairs [];
+sumPairs [(1, 2), (3, 4)];
+
+(*) Skip rows that do not match the pattern
+from (left, 2) in [(1, 2), (3, 4), (5, 2)]
+ yield left;
+
+fun listHeads lists =
+ from hd :: tl in lists
+ yield hd + 1;
+listHeads [];
+listHeads [[1, 2], [3], [4, 5, 6]];
+
+fun listFields lists =
+ from {a = x, b = y} in lists
+ yield x + 1;
+listFields [];
+listFields [{a = 1, b = 2}, {a = 3, b = 0}, {a = 4, b = 5}];
+
(*) Temporary functions
let
fun abbrev s =
diff --git a/src/test/resources/script/relational.sml.out b/src/test/resources/script/relational.sml.out
index a47ef875..18d47bf3 100644
--- a/src/test/resources/script/relational.sml.out
+++ b/src/test/resources/script/relational.sml.out
@@ -688,6 +688,49 @@ from
val it = [{c1=1,c5=5}] : {c1:int, c5:int} list
+(*) Patterns left of 'in'
+fun sumPairs pairs =
+ from (left, right) in pairs
+ yield left + right;
+val sumPairs = fn : (int * int) list -> int list
+
+sumPairs [];
+val it = [] : int list
+
+sumPairs [(1, 2), (3, 4)];
+val it = [3,7] : int list
+
+
+(*) Skip rows that do not match the pattern
+from (left, 2) in [(1, 2), (3, 4), (5, 2)]
+ yield left;
+val it = [1,5] : int list
+
+
+fun listHeads lists =
+ from hd :: tl in lists
+ yield hd + 1;
+val listHeads = fn : int list list -> int list
+
+listHeads [];
+val it = [] : int list
+
+listHeads [[1, 2], [3], [4, 5, 6]];
+val it = [2,4,5] : int list
+
+
+fun listFields lists =
+ from {a = x, b = y} in lists
+ yield x + 1;
+val listFields = 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 =