haskell_match 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +98 -0
- data/LICENSE-APACHE +202 -0
- data/LICENSE-MIT +21 -0
- data/README.md +1484 -0
- data/ext/haskell_match/Cargo.lock +33 -0
- data/ext/haskell_match/Cargo.toml +22 -0
- data/ext/haskell_match/extconf.rb +41 -0
- data/ext/haskell_match/src/core/ast.rs +190 -0
- data/ext/haskell_match/src/core/error.rs +52 -0
- data/ext/haskell_match/src/core/exhaust.rs +699 -0
- data/ext/haskell_match/src/core/hs/ast.rs +256 -0
- data/ext/haskell_match/src/core/hs/json.rs +225 -0
- data/ext/haskell_match/src/core/hs/layout.rs +346 -0
- data/ext/haskell_match/src/core/hs/lexer.rs +688 -0
- data/ext/haskell_match/src/core/hs/mod.rs +14 -0
- data/ext/haskell_match/src/core/hs/parser.rs +1945 -0
- data/ext/haskell_match/src/core/lexer.rs +590 -0
- data/ext/haskell_match/src/core/mod.rs +19 -0
- data/ext/haskell_match/src/core/parser.rs +1116 -0
- data/ext/haskell_match/src/core/pretty.rs +373 -0
- data/ext/haskell_match/src/core/resolve.rs +336 -0
- data/ext/haskell_match/src/core/tree.rs +921 -0
- data/ext/haskell_match/src/core/typecheck.rs +226 -0
- data/ext/haskell_match/src/core/types.rs +404 -0
- data/ext/haskell_match/src/lib.rs +19 -0
- data/ext/haskell_match/src/ruby/mod.rs +1195 -0
- data/ext/haskell_match/src/ruby/runtime.rs +1045 -0
- data/lib/haskell_match/binding_plan.rb +84 -0
- data/lib/haskell_match/case_of.rb +71 -0
- data/lib/haskell_match/clauses.rb +354 -0
- data/lib/haskell_match/data.rb +417 -0
- data/lib/haskell_match/deep_call.rb +98 -0
- data/lib/haskell_match/deriving.rb +130 -0
- data/lib/haskell_match/dsl.rb +71 -0
- data/lib/haskell_match/errors.rb +85 -0
- data/lib/haskell_match/field_types.rb +140 -0
- data/lib/haskell_match/function.rb +240 -0
- data/lib/haskell_match/haskell/compiler.rb +961 -0
- data/lib/haskell_match/haskell.rb +326 -0
- data/lib/haskell_match/inspect.rb +45 -0
- data/lib/haskell_match/lazy_list.rb +210 -0
- data/lib/haskell_match/native_loader.rb +64 -0
- data/lib/haskell_match/pattern.rb +75 -0
- data/lib/haskell_match/pattern_ast.rb +394 -0
- data/lib/haskell_match/prelude.rb +448 -0
- data/lib/haskell_match/scope.rb +44 -0
- data/lib/haskell_match/version.rb +5 -0
- data/lib/haskell_match.rb +41 -0
- metadata +124 -0
|
@@ -0,0 +1,921 @@
|
|
|
1
|
+
//! Compilation of a clause matrix into a decision tree.
|
|
2
|
+
//!
|
|
3
|
+
//! The tree is evaluated against an environment of *slots*. Slots `0..arity`
|
|
4
|
+
//! hold the function arguments; every constructor test that succeeds copies
|
|
5
|
+
//! the constructor's fields into freshly numbered slots, so a leaf can name
|
|
6
|
+
//! each bound variable by slot. Each argument position is inspected at most
|
|
7
|
+
//! once per path through the tree.
|
|
8
|
+
|
|
9
|
+
use super::ast::{HKey, Lit, LitKind, Pat, VarId};
|
|
10
|
+
use super::types::{ConId, TypeEnv, TypeId};
|
|
11
|
+
|
|
12
|
+
#[derive(Clone, Debug, PartialEq)]
|
|
13
|
+
pub enum Tree {
|
|
14
|
+
/// No clause matches.
|
|
15
|
+
Fail,
|
|
16
|
+
Leaf(Leaf),
|
|
17
|
+
/// A clause whose pattern matched but which has a guard; if the guard
|
|
18
|
+
/// is false, evaluation continues with `fallback`.
|
|
19
|
+
Guard {
|
|
20
|
+
leaf: Leaf,
|
|
21
|
+
fallback: Box<Tree>,
|
|
22
|
+
},
|
|
23
|
+
SwitchCon {
|
|
24
|
+
slot: usize,
|
|
25
|
+
ty: TypeId,
|
|
26
|
+
cases: Vec<ConCase>,
|
|
27
|
+
default: Option<Box<Tree>>,
|
|
28
|
+
},
|
|
29
|
+
SwitchLit {
|
|
30
|
+
slot: usize,
|
|
31
|
+
kind: LitKind,
|
|
32
|
+
cases: Vec<(Lit, Tree)>,
|
|
33
|
+
default: Box<Tree>,
|
|
34
|
+
},
|
|
35
|
+
/// Does the Hash in `slot` have every key in `keys`? If so their values
|
|
36
|
+
/// go to slots `base..base+keys.len()` and matching continues with
|
|
37
|
+
/// `then`; otherwise (or for a non-Hash) with `otherwise`.
|
|
38
|
+
TestHash {
|
|
39
|
+
slot: usize,
|
|
40
|
+
keys: Vec<HKey>,
|
|
41
|
+
base: usize,
|
|
42
|
+
then: Box<Tree>,
|
|
43
|
+
otherwise: Box<Tree>,
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#[derive(Clone, Debug, PartialEq)]
|
|
48
|
+
pub struct ConCase {
|
|
49
|
+
pub con: ConId,
|
|
50
|
+
pub tag: usize,
|
|
51
|
+
pub arity: usize,
|
|
52
|
+
/// First slot receiving this constructor's fields.
|
|
53
|
+
pub base: usize,
|
|
54
|
+
pub tree: Tree,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#[derive(Clone, Debug, PartialEq)]
|
|
58
|
+
pub struct Leaf {
|
|
59
|
+
pub clause: usize,
|
|
60
|
+
/// Where each variable of the clause (by `VarId`) gets its value.
|
|
61
|
+
pub binds: Vec<BindSrc>,
|
|
62
|
+
/// Irrefutable sub-patterns to destructure once the clause is chosen.
|
|
63
|
+
pub lazies: Vec<LazyPat>,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
67
|
+
pub enum BindSrc {
|
|
68
|
+
Slot(usize),
|
|
69
|
+
/// Filled in by evaluating `Leaf::lazies[i]`.
|
|
70
|
+
Lazy(usize),
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#[derive(Clone, Debug, PartialEq)]
|
|
74
|
+
pub struct LazyPat {
|
|
75
|
+
pub slot: usize,
|
|
76
|
+
pub pat: Pat,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
#[derive(Clone, Debug)]
|
|
80
|
+
pub struct Compiled {
|
|
81
|
+
pub tree: Tree,
|
|
82
|
+
pub arity: usize,
|
|
83
|
+
pub n_slots: usize,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#[derive(Clone, Debug)]
|
|
87
|
+
struct Row {
|
|
88
|
+
pats: Vec<Pat>,
|
|
89
|
+
clause: usize,
|
|
90
|
+
binds: Vec<Option<BindSrc>>,
|
|
91
|
+
lazies: Vec<LazyPat>,
|
|
92
|
+
guarded: bool,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
struct Compiler<'a> {
|
|
96
|
+
env: &'a TypeEnv,
|
|
97
|
+
n_slots: usize,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/// Input clause: argument patterns, number of variables, guarded flag.
|
|
101
|
+
pub struct Clause {
|
|
102
|
+
pub pats: Vec<Pat>,
|
|
103
|
+
pub n_vars: usize,
|
|
104
|
+
pub guarded: bool,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
pub fn compile(env: &TypeEnv, clauses: &[Clause], arity: usize) -> Compiled {
|
|
108
|
+
let mut c = Compiler {
|
|
109
|
+
env,
|
|
110
|
+
n_slots: arity,
|
|
111
|
+
};
|
|
112
|
+
let occs: Vec<usize> = (0..arity).collect();
|
|
113
|
+
let mut rows: Vec<Row> = clauses
|
|
114
|
+
.iter()
|
|
115
|
+
.enumerate()
|
|
116
|
+
.map(|(i, cl)| Row {
|
|
117
|
+
pats: cl.pats.clone(),
|
|
118
|
+
clause: i,
|
|
119
|
+
binds: vec![None; cl.n_vars],
|
|
120
|
+
lazies: Vec::new(),
|
|
121
|
+
guarded: cl.guarded,
|
|
122
|
+
})
|
|
123
|
+
.collect();
|
|
124
|
+
for r in rows.iter_mut() {
|
|
125
|
+
normalize(r, &occs);
|
|
126
|
+
}
|
|
127
|
+
let tree = c.compile(&occs, rows);
|
|
128
|
+
Compiled {
|
|
129
|
+
tree,
|
|
130
|
+
arity,
|
|
131
|
+
n_slots: c.n_slots,
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/// Collect the variables bound inside a pattern.
|
|
136
|
+
fn vars_in(p: &Pat, out: &mut Vec<VarId>) {
|
|
137
|
+
match p {
|
|
138
|
+
Pat::Wild | Pat::Lit(_) => {}
|
|
139
|
+
Pat::Var(v) => out.push(*v),
|
|
140
|
+
Pat::As(v, inner) => {
|
|
141
|
+
out.push(*v);
|
|
142
|
+
vars_in(inner, out);
|
|
143
|
+
}
|
|
144
|
+
Pat::Lazy(inner) => vars_in(inner, out),
|
|
145
|
+
Pat::Con(_, args) => args.iter().for_each(|a| vars_in(a, out)),
|
|
146
|
+
Pat::Hash(fields) => fields.iter().for_each(|(_, a)| vars_in(a, out)),
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
fn hash_keys(fields: &[(HKey, Pat)]) -> Vec<HKey> {
|
|
151
|
+
fields.iter().map(|(k, _)| k.clone()).collect()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
fn key_subset(a: &[HKey], b: &[HKey]) -> bool {
|
|
155
|
+
a.iter().all(|k| b.contains(k))
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// Strip binders from every column so each pattern is `Wild`, `Con` or
|
|
159
|
+
/// `Lit`, recording where the stripped variables get their values.
|
|
160
|
+
fn normalize(row: &mut Row, occs: &[usize]) {
|
|
161
|
+
for (i, slot) in occs.iter().enumerate() {
|
|
162
|
+
loop {
|
|
163
|
+
match std::mem::replace(&mut row.pats[i], Pat::Wild) {
|
|
164
|
+
Pat::Var(v) => {
|
|
165
|
+
row.binds[v] = Some(BindSrc::Slot(*slot));
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
Pat::As(v, inner) => {
|
|
169
|
+
row.binds[v] = Some(BindSrc::Slot(*slot));
|
|
170
|
+
row.pats[i] = *inner;
|
|
171
|
+
}
|
|
172
|
+
Pat::Lazy(inner) => {
|
|
173
|
+
let idx = row.lazies.len();
|
|
174
|
+
let mut vs = Vec::new();
|
|
175
|
+
vars_in(&inner, &mut vs);
|
|
176
|
+
for v in vs {
|
|
177
|
+
row.binds[v] = Some(BindSrc::Lazy(idx));
|
|
178
|
+
}
|
|
179
|
+
row.lazies.push(LazyPat {
|
|
180
|
+
slot: *slot,
|
|
181
|
+
pat: *inner,
|
|
182
|
+
});
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
other => {
|
|
186
|
+
row.pats[i] = other;
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
impl<'a> Compiler<'a> {
|
|
195
|
+
fn alloc(&mut self, n: usize) -> usize {
|
|
196
|
+
let base = self.n_slots;
|
|
197
|
+
self.n_slots += n;
|
|
198
|
+
base
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
fn leaf(row: &Row) -> Leaf {
|
|
202
|
+
Leaf {
|
|
203
|
+
clause: row.clause,
|
|
204
|
+
binds: row
|
|
205
|
+
.binds
|
|
206
|
+
.iter()
|
|
207
|
+
.map(|b| b.expect("every clause variable is bound by its pattern"))
|
|
208
|
+
.collect(),
|
|
209
|
+
lazies: row.lazies.clone(),
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
fn compile(&mut self, occs: &[usize], rows: Vec<Row>) -> Tree {
|
|
214
|
+
if rows.is_empty() {
|
|
215
|
+
return Tree::Fail;
|
|
216
|
+
}
|
|
217
|
+
let first = &rows[0];
|
|
218
|
+
let col = match first.pats.iter().position(|p| !p.is_wild()) {
|
|
219
|
+
None => {
|
|
220
|
+
let leaf = Self::leaf(first);
|
|
221
|
+
if first.guarded {
|
|
222
|
+
let rest = rows[1..].to_vec();
|
|
223
|
+
return Tree::Guard {
|
|
224
|
+
leaf,
|
|
225
|
+
fallback: Box::new(self.compile(occs, rest)),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
return Tree::Leaf(leaf);
|
|
229
|
+
}
|
|
230
|
+
Some(c) => c,
|
|
231
|
+
};
|
|
232
|
+
let slot = occs[col];
|
|
233
|
+
match &first.pats[col] {
|
|
234
|
+
Pat::Con(c0, _) => {
|
|
235
|
+
let ty = self.env.type_of_con(*c0);
|
|
236
|
+
let mut heads: Vec<ConId> = Vec::new();
|
|
237
|
+
for r in &rows {
|
|
238
|
+
if let Pat::Con(c, _) = &r.pats[col] {
|
|
239
|
+
if !heads.contains(c) {
|
|
240
|
+
heads.push(*c);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
let mut cases = Vec::with_capacity(heads.len());
|
|
245
|
+
for c in &heads {
|
|
246
|
+
let arity = self.env.con(*c).arity;
|
|
247
|
+
let base = self.alloc(arity);
|
|
248
|
+
let mut new_occs: Vec<usize> = occs[..col].to_vec();
|
|
249
|
+
new_occs.extend(base..base + arity);
|
|
250
|
+
new_occs.extend_from_slice(&occs[col + 1..]);
|
|
251
|
+
let mut new_rows = Vec::new();
|
|
252
|
+
for r in &rows {
|
|
253
|
+
match &r.pats[col] {
|
|
254
|
+
Pat::Con(cc, args) if cc == c => {
|
|
255
|
+
let mut nr = r.clone();
|
|
256
|
+
nr.pats = r.pats[..col].to_vec();
|
|
257
|
+
nr.pats.extend(args.iter().cloned());
|
|
258
|
+
nr.pats.extend_from_slice(&r.pats[col + 1..]);
|
|
259
|
+
normalize(&mut nr, &new_occs);
|
|
260
|
+
new_rows.push(nr);
|
|
261
|
+
}
|
|
262
|
+
Pat::Wild => {
|
|
263
|
+
let mut nr = r.clone();
|
|
264
|
+
nr.pats = r.pats[..col].to_vec();
|
|
265
|
+
nr.pats.extend(std::iter::repeat_n(Pat::Wild, arity));
|
|
266
|
+
nr.pats.extend_from_slice(&r.pats[col + 1..]);
|
|
267
|
+
new_rows.push(nr);
|
|
268
|
+
}
|
|
269
|
+
_ => {}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
cases.push(ConCase {
|
|
273
|
+
con: *c,
|
|
274
|
+
tag: self.env.con(*c).tag,
|
|
275
|
+
arity,
|
|
276
|
+
base,
|
|
277
|
+
tree: self.compile(&new_occs, new_rows),
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
let default = if self.env.is_complete(ty, &heads) {
|
|
281
|
+
None
|
|
282
|
+
} else {
|
|
283
|
+
Some(Box::new(self.compile_default(occs, col, &rows)))
|
|
284
|
+
};
|
|
285
|
+
Tree::SwitchCon {
|
|
286
|
+
slot,
|
|
287
|
+
ty,
|
|
288
|
+
cases,
|
|
289
|
+
default,
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
Pat::Lit(l0) => {
|
|
293
|
+
let kind = l0.kind();
|
|
294
|
+
let mut lits: Vec<Lit> = Vec::new();
|
|
295
|
+
for r in &rows {
|
|
296
|
+
if let Pat::Lit(l) = &r.pats[col] {
|
|
297
|
+
if !lits.iter().any(|x| x.same(l)) {
|
|
298
|
+
lits.push(l.clone());
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
let mut cases = Vec::with_capacity(lits.len());
|
|
303
|
+
let mut new_occs: Vec<usize> = occs[..col].to_vec();
|
|
304
|
+
new_occs.extend_from_slice(&occs[col + 1..]);
|
|
305
|
+
for l in &lits {
|
|
306
|
+
let mut new_rows = Vec::new();
|
|
307
|
+
for r in &rows {
|
|
308
|
+
let keep = match &r.pats[col] {
|
|
309
|
+
Pat::Lit(ll) => ll.same(l),
|
|
310
|
+
Pat::Wild => true,
|
|
311
|
+
_ => false,
|
|
312
|
+
};
|
|
313
|
+
if keep {
|
|
314
|
+
let mut nr = r.clone();
|
|
315
|
+
nr.pats.remove(col);
|
|
316
|
+
new_rows.push(nr);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
cases.push((l.clone(), self.compile(&new_occs, new_rows)));
|
|
320
|
+
}
|
|
321
|
+
let default = Box::new(self.compile_default(occs, col, &rows));
|
|
322
|
+
Tree::SwitchLit {
|
|
323
|
+
slot,
|
|
324
|
+
kind,
|
|
325
|
+
cases,
|
|
326
|
+
default,
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
Pat::Hash(f0) => {
|
|
330
|
+
// Hash patterns overlap (a value may have the keys of several),
|
|
331
|
+
// so they are tested one key set at a time, in clause order.
|
|
332
|
+
let keys = hash_keys(f0);
|
|
333
|
+
let n = keys.len();
|
|
334
|
+
let base = self.alloc(n);
|
|
335
|
+
let mut then_occs: Vec<usize> = occs[..col].to_vec();
|
|
336
|
+
then_occs.extend(base..base + n);
|
|
337
|
+
then_occs.extend_from_slice(&occs[col + 1..]);
|
|
338
|
+
let mut then_rows = Vec::new();
|
|
339
|
+
let mut else_rows = Vec::new();
|
|
340
|
+
for r in &rows {
|
|
341
|
+
match &r.pats[col] {
|
|
342
|
+
Pat::Hash(f) => {
|
|
343
|
+
let ks = hash_keys(f);
|
|
344
|
+
if key_subset(&ks, &keys) {
|
|
345
|
+
// has every key the row needs: its sub-patterns
|
|
346
|
+
// take the key columns, unmentioned keys are `_`
|
|
347
|
+
let mut nr = r.clone();
|
|
348
|
+
nr.pats = r.pats[..col].to_vec();
|
|
349
|
+
for k in &keys {
|
|
350
|
+
nr.pats.push(
|
|
351
|
+
f.iter()
|
|
352
|
+
.find(|(kk, _)| kk == k)
|
|
353
|
+
.map(|(_, p)| p.clone())
|
|
354
|
+
.unwrap_or(Pat::Wild),
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
nr.pats.extend_from_slice(&r.pats[col + 1..]);
|
|
358
|
+
normalize(&mut nr, &then_occs);
|
|
359
|
+
then_rows.push(nr);
|
|
360
|
+
}
|
|
361
|
+
// a value lacking one of `keys` cannot match a row
|
|
362
|
+
// needing all of them; any other row may still match
|
|
363
|
+
if !key_subset(&keys, &ks) {
|
|
364
|
+
else_rows.push(r.clone());
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
Pat::Wild => {
|
|
368
|
+
let mut nr = r.clone();
|
|
369
|
+
nr.pats = r.pats[..col].to_vec();
|
|
370
|
+
nr.pats.extend(std::iter::repeat_n(Pat::Wild, n));
|
|
371
|
+
nr.pats.extend_from_slice(&r.pats[col + 1..]);
|
|
372
|
+
then_rows.push(nr);
|
|
373
|
+
else_rows.push(r.clone());
|
|
374
|
+
}
|
|
375
|
+
_ => {}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
Tree::TestHash {
|
|
379
|
+
slot,
|
|
380
|
+
keys,
|
|
381
|
+
base,
|
|
382
|
+
then: Box::new(self.compile(&then_occs, then_rows)),
|
|
383
|
+
otherwise: Box::new(self.compile(occs, else_rows)),
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
_ => unreachable!("normalized rows contain only Wild, Con, Lit and Hash"),
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
fn compile_default(&mut self, occs: &[usize], col: usize, rows: &[Row]) -> Tree {
|
|
391
|
+
let mut new_occs: Vec<usize> = occs[..col].to_vec();
|
|
392
|
+
new_occs.extend_from_slice(&occs[col + 1..]);
|
|
393
|
+
let new_rows: Vec<Row> = rows
|
|
394
|
+
.iter()
|
|
395
|
+
.filter(|r| r.pats[col].is_wild())
|
|
396
|
+
.map(|r| {
|
|
397
|
+
let mut nr = r.clone();
|
|
398
|
+
nr.pats.remove(col);
|
|
399
|
+
nr
|
|
400
|
+
})
|
|
401
|
+
.collect();
|
|
402
|
+
self.compile(&new_occs, new_rows)
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
#[cfg(test)]
|
|
407
|
+
pub mod interp {
|
|
408
|
+
//! A reference interpreter over an abstract value model, used to test the
|
|
409
|
+
//! compiled trees without Ruby.
|
|
410
|
+
use super::*;
|
|
411
|
+
use crate::core::types::{TypeKind, CON_CONS, CON_FALSE, CON_NIL, CON_TRUE};
|
|
412
|
+
|
|
413
|
+
#[derive(Clone, Debug, PartialEq)]
|
|
414
|
+
pub enum Val {
|
|
415
|
+
Con(ConId, Vec<Val>),
|
|
416
|
+
List(Vec<Val>),
|
|
417
|
+
Tuple(Vec<Val>),
|
|
418
|
+
Bool(bool),
|
|
419
|
+
Int(i64),
|
|
420
|
+
Str(String),
|
|
421
|
+
Hash(Vec<(HKey, Val)>),
|
|
422
|
+
Other(String),
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
#[derive(Debug, PartialEq)]
|
|
426
|
+
pub enum Outcome {
|
|
427
|
+
Matched { clause: usize, binds: Vec<Val> },
|
|
428
|
+
NoMatch,
|
|
429
|
+
TypeMismatch(TypeId),
|
|
430
|
+
LazyFailed,
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
fn con_of(env: &TypeEnv, ty: TypeId, v: &Val) -> Option<(usize, Vec<Val>)> {
|
|
434
|
+
match (env.ty(ty).kind, v) {
|
|
435
|
+
(TypeKind::Adt, Val::Con(c, fields)) if env.type_of_con(*c) == ty => {
|
|
436
|
+
Some((env.con(*c).tag, fields.clone()))
|
|
437
|
+
}
|
|
438
|
+
(TypeKind::Bool, Val::Bool(b)) => Some((if *b { 1 } else { 0 }, vec![])),
|
|
439
|
+
(TypeKind::List, Val::List(items)) => {
|
|
440
|
+
if items.is_empty() {
|
|
441
|
+
Some((0, vec![]))
|
|
442
|
+
} else {
|
|
443
|
+
Some((1, vec![items[0].clone(), Val::List(items[1..].to_vec())]))
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
(TypeKind::List, Val::Str(s)) => {
|
|
447
|
+
let mut chars = s.chars();
|
|
448
|
+
match chars.next() {
|
|
449
|
+
None => Some((0, vec![])),
|
|
450
|
+
Some(c) => Some((1, vec![Val::Str(c.to_string()), Val::Str(chars.collect())])),
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
(TypeKind::Tuple(n), Val::Tuple(items)) if items.len() == n => Some((0, items.clone())),
|
|
454
|
+
_ => None,
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
fn lit_eq(l: &Lit, v: &Val) -> bool {
|
|
459
|
+
match (l, v) {
|
|
460
|
+
(Lit::Int(i), Val::Int(j)) => i == j,
|
|
461
|
+
(Lit::Char(c), Val::Str(t)) => t.chars().count() == 1 && t.starts_with(*c),
|
|
462
|
+
_ => false,
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
fn lit_kind_ok(k: LitKind, v: &Val) -> bool {
|
|
467
|
+
matches!(
|
|
468
|
+
(k, v),
|
|
469
|
+
(LitKind::Num, Val::Int(_)) | (LitKind::Char, Val::Str(_))
|
|
470
|
+
)
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/// Match `pat` against `v` interpretively (for lazy patterns).
|
|
474
|
+
fn destructure(env: &TypeEnv, pat: &Pat, v: &Val, out: &mut Vec<Option<Val>>) -> bool {
|
|
475
|
+
match pat {
|
|
476
|
+
Pat::Wild => true,
|
|
477
|
+
Pat::Var(x) => {
|
|
478
|
+
out[*x] = Some(v.clone());
|
|
479
|
+
true
|
|
480
|
+
}
|
|
481
|
+
Pat::As(x, p) => {
|
|
482
|
+
out[*x] = Some(v.clone());
|
|
483
|
+
destructure(env, p, v, out)
|
|
484
|
+
}
|
|
485
|
+
Pat::Lazy(p) => destructure(env, p, v, out),
|
|
486
|
+
Pat::Lit(l) => lit_eq(l, v),
|
|
487
|
+
Pat::Hash(fields) => match v {
|
|
488
|
+
Val::Hash(entries) => fields.iter().all(|(k, p)| {
|
|
489
|
+
entries
|
|
490
|
+
.iter()
|
|
491
|
+
.find(|(kk, _)| kk == k)
|
|
492
|
+
.map(|(_, val)| destructure(env, p, val, out))
|
|
493
|
+
.unwrap_or(false)
|
|
494
|
+
}),
|
|
495
|
+
_ => false,
|
|
496
|
+
},
|
|
497
|
+
Pat::Con(c, args) => {
|
|
498
|
+
let ty = env.type_of_con(*c);
|
|
499
|
+
let _ = (CON_CONS, CON_NIL, CON_TRUE, CON_FALSE);
|
|
500
|
+
match con_of(env, ty, v) {
|
|
501
|
+
Some((tag, fields)) if tag == env.con(*c).tag => args
|
|
502
|
+
.iter()
|
|
503
|
+
.zip(fields.iter())
|
|
504
|
+
.all(|(a, f)| destructure(env, a, f, out)),
|
|
505
|
+
_ => false,
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
pub fn eval(
|
|
512
|
+
env: &TypeEnv,
|
|
513
|
+
compiled: &Compiled,
|
|
514
|
+
args: &[Val],
|
|
515
|
+
guards: &dyn Fn(usize, &[Val]) -> bool,
|
|
516
|
+
) -> Outcome {
|
|
517
|
+
let mut slots: Vec<Option<Val>> = vec![None; compiled.n_slots];
|
|
518
|
+
for (i, a) in args.iter().enumerate() {
|
|
519
|
+
slots[i] = Some(a.clone());
|
|
520
|
+
}
|
|
521
|
+
let mut node = &compiled.tree;
|
|
522
|
+
loop {
|
|
523
|
+
match node {
|
|
524
|
+
Tree::Fail => return Outcome::NoMatch,
|
|
525
|
+
Tree::Leaf(leaf) => return bind(env, leaf, &slots),
|
|
526
|
+
Tree::Guard { leaf, fallback } => match bind(env, leaf, &slots) {
|
|
527
|
+
Outcome::Matched { clause, binds } => {
|
|
528
|
+
if guards(clause, &binds) {
|
|
529
|
+
return Outcome::Matched { clause, binds };
|
|
530
|
+
}
|
|
531
|
+
node = fallback;
|
|
532
|
+
}
|
|
533
|
+
other => return other,
|
|
534
|
+
},
|
|
535
|
+
Tree::SwitchCon {
|
|
536
|
+
slot,
|
|
537
|
+
ty,
|
|
538
|
+
cases,
|
|
539
|
+
default,
|
|
540
|
+
} => {
|
|
541
|
+
let v = slots[*slot].clone().unwrap();
|
|
542
|
+
match con_of(env, *ty, &v) {
|
|
543
|
+
None => return Outcome::TypeMismatch(*ty),
|
|
544
|
+
Some((tag, fields)) => match cases.iter().find(|c| c.tag == tag) {
|
|
545
|
+
Some(case) => {
|
|
546
|
+
for (i, f) in fields.into_iter().enumerate() {
|
|
547
|
+
slots[case.base + i] = Some(f);
|
|
548
|
+
}
|
|
549
|
+
node = &case.tree;
|
|
550
|
+
}
|
|
551
|
+
None => match default {
|
|
552
|
+
Some(d) => node = d,
|
|
553
|
+
None => panic!("complete switch without case for tag {}", tag),
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
Tree::SwitchLit {
|
|
559
|
+
slot,
|
|
560
|
+
kind,
|
|
561
|
+
cases,
|
|
562
|
+
default,
|
|
563
|
+
} => {
|
|
564
|
+
let v = slots[*slot].clone().unwrap();
|
|
565
|
+
if !lit_kind_ok(*kind, &v) {
|
|
566
|
+
return Outcome::TypeMismatch(usize::MAX);
|
|
567
|
+
}
|
|
568
|
+
match cases.iter().find(|(l, _)| lit_eq(l, &v)) {
|
|
569
|
+
Some((_, t)) => node = t,
|
|
570
|
+
None => node = default,
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
Tree::TestHash {
|
|
574
|
+
slot,
|
|
575
|
+
keys,
|
|
576
|
+
base,
|
|
577
|
+
then,
|
|
578
|
+
otherwise,
|
|
579
|
+
} => {
|
|
580
|
+
let v = slots[*slot].clone().unwrap();
|
|
581
|
+
let entries = match &v {
|
|
582
|
+
Val::Hash(e) => e,
|
|
583
|
+
_ => {
|
|
584
|
+
if matches!(**otherwise, Tree::Fail) {
|
|
585
|
+
return Outcome::TypeMismatch(usize::MAX - 1);
|
|
586
|
+
}
|
|
587
|
+
node = otherwise;
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
let mut all = true;
|
|
592
|
+
for (i, k) in keys.iter().enumerate() {
|
|
593
|
+
match entries.iter().find(|(kk, _)| kk == k) {
|
|
594
|
+
Some((_, val)) => slots[*base + i] = Some(val.clone()),
|
|
595
|
+
None => {
|
|
596
|
+
all = false;
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
node = if all { then } else { otherwise };
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
fn bind(env: &TypeEnv, leaf: &Leaf, slots: &[Option<Val>]) -> Outcome {
|
|
608
|
+
let mut out: Vec<Option<Val>> = vec![None; leaf.binds.len()];
|
|
609
|
+
for lz in &leaf.lazies {
|
|
610
|
+
let v = slots[lz.slot].clone().unwrap();
|
|
611
|
+
if !destructure(env, &lz.pat, &v, &mut out) {
|
|
612
|
+
return Outcome::LazyFailed;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
for (i, b) in leaf.binds.iter().enumerate() {
|
|
616
|
+
if let BindSrc::Slot(s) = b {
|
|
617
|
+
out[i] = slots[*s].clone();
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
Outcome::Matched {
|
|
621
|
+
clause: leaf.clause,
|
|
622
|
+
binds: out.into_iter().map(|v| v.expect("bound")).collect(),
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
#[cfg(test)]
|
|
628
|
+
mod tests {
|
|
629
|
+
use super::interp::{eval, Outcome, Val};
|
|
630
|
+
use super::*;
|
|
631
|
+
use crate::core::parser::parse_pattern;
|
|
632
|
+
use crate::core::resolve::resolve_clause;
|
|
633
|
+
use crate::core::types::ConSpec;
|
|
634
|
+
|
|
635
|
+
fn env() -> TypeEnv {
|
|
636
|
+
let mut env = TypeEnv::new();
|
|
637
|
+
env.register(
|
|
638
|
+
"Maybe",
|
|
639
|
+
&[
|
|
640
|
+
ConSpec {
|
|
641
|
+
name: "Nothing".into(),
|
|
642
|
+
arity: 0,
|
|
643
|
+
fields: None,
|
|
644
|
+
handle: 0,
|
|
645
|
+
},
|
|
646
|
+
ConSpec {
|
|
647
|
+
name: "Just".into(),
|
|
648
|
+
arity: 1,
|
|
649
|
+
fields: None,
|
|
650
|
+
handle: 0,
|
|
651
|
+
},
|
|
652
|
+
],
|
|
653
|
+
)
|
|
654
|
+
.unwrap();
|
|
655
|
+
env
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
fn build(env: &mut TypeEnv, clauses: &[&[&str]]) -> (Compiled, Vec<Vec<String>>) {
|
|
659
|
+
let mut cs = Vec::new();
|
|
660
|
+
let mut names = Vec::new();
|
|
661
|
+
let mut arity = None;
|
|
662
|
+
for c in clauses {
|
|
663
|
+
let guarded = c.last() == Some(&"|");
|
|
664
|
+
let pats: Vec<&str> = c.iter().copied().filter(|s| *s != "|").collect();
|
|
665
|
+
arity = Some(pats.len());
|
|
666
|
+
let raws: Vec<_> = pats.iter().map(|s| parse_pattern(s).unwrap()).collect();
|
|
667
|
+
let (ps, b) = resolve_clause(env, &raws).unwrap();
|
|
668
|
+
cs.push(Clause {
|
|
669
|
+
pats: ps,
|
|
670
|
+
n_vars: b.names.len(),
|
|
671
|
+
guarded,
|
|
672
|
+
});
|
|
673
|
+
names.push(b.names);
|
|
674
|
+
}
|
|
675
|
+
(compile(env, &cs, arity.unwrap()), names)
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
fn just(env: &TypeEnv, v: Val) -> Val {
|
|
679
|
+
Val::Con(env.lookup_con("Just").unwrap(), vec![v])
|
|
680
|
+
}
|
|
681
|
+
fn nothing(env: &TypeEnv) -> Val {
|
|
682
|
+
Val::Con(env.lookup_con("Nothing").unwrap(), vec![])
|
|
683
|
+
}
|
|
684
|
+
fn list(items: Vec<Val>) -> Val {
|
|
685
|
+
Val::List(items)
|
|
686
|
+
}
|
|
687
|
+
fn no_guards(_: usize, _: &[Val]) -> bool {
|
|
688
|
+
true
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
fn matched(clause: usize, binds: Vec<Val>) -> Outcome {
|
|
692
|
+
Outcome::Matched { clause, binds }
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
#[test]
|
|
696
|
+
fn maybe_function() {
|
|
697
|
+
let mut env = env();
|
|
698
|
+
let (c, names) = build(&mut env, &[&["Nothing"], &["Just x"]]);
|
|
699
|
+
assert_eq!(names, vec![Vec::<String>::new(), vec!["x".to_string()]]);
|
|
700
|
+
assert_eq!(
|
|
701
|
+
eval(&env, &c, &[nothing(&env)], &no_guards),
|
|
702
|
+
matched(0, vec![])
|
|
703
|
+
);
|
|
704
|
+
assert_eq!(
|
|
705
|
+
eval(&env, &c, &[just(&env, Val::Int(5))], &no_guards),
|
|
706
|
+
matched(1, vec![Val::Int(5)])
|
|
707
|
+
);
|
|
708
|
+
assert_eq!(
|
|
709
|
+
eval(&env, &c, &[Val::Int(5)], &no_guards),
|
|
710
|
+
Outcome::TypeMismatch(env.lookup_type("Maybe").unwrap())
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
#[test]
|
|
715
|
+
fn list_functions() {
|
|
716
|
+
let mut env = env();
|
|
717
|
+
let (c, _) = build(&mut env, &[&["[]"], &["[x]"], &["(x:y:rest)"]]);
|
|
718
|
+
assert_eq!(
|
|
719
|
+
eval(&env, &c, &[list(vec![])], &no_guards),
|
|
720
|
+
matched(0, vec![])
|
|
721
|
+
);
|
|
722
|
+
assert_eq!(
|
|
723
|
+
eval(&env, &c, &[list(vec![Val::Int(1)])], &no_guards),
|
|
724
|
+
matched(1, vec![Val::Int(1)])
|
|
725
|
+
);
|
|
726
|
+
assert_eq!(
|
|
727
|
+
eval(
|
|
728
|
+
&env,
|
|
729
|
+
&c,
|
|
730
|
+
&[list(vec![Val::Int(1), Val::Int(2), Val::Int(3)])],
|
|
731
|
+
&no_guards
|
|
732
|
+
),
|
|
733
|
+
matched(2, vec![Val::Int(1), Val::Int(2), list(vec![Val::Int(3)])])
|
|
734
|
+
);
|
|
735
|
+
// as-pattern
|
|
736
|
+
let (c, names) = build(&mut env, &[&["all@(x:_)"], &["[]"]]);
|
|
737
|
+
assert_eq!(names[0], vec!["all", "x"]);
|
|
738
|
+
let l = list(vec![Val::Int(7), Val::Int(8)]);
|
|
739
|
+
assert_eq!(
|
|
740
|
+
eval(&env, &c, std::slice::from_ref(&l), &no_guards),
|
|
741
|
+
matched(0, vec![l.clone(), Val::Int(7)])
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
#[test]
|
|
746
|
+
fn multi_argument_and_order() {
|
|
747
|
+
let mut env = env();
|
|
748
|
+
// zip
|
|
749
|
+
let (c, names) = build(&mut env, &[&["(x:xs)", "(y:ys)"], &["_", "_"]]);
|
|
750
|
+
assert_eq!(names[0], vec!["x", "xs", "y", "ys"]);
|
|
751
|
+
assert_eq!(
|
|
752
|
+
eval(
|
|
753
|
+
&env,
|
|
754
|
+
&c,
|
|
755
|
+
&[
|
|
756
|
+
list(vec![Val::Int(1), Val::Int(2)]),
|
|
757
|
+
list(vec![Val::Int(3)])
|
|
758
|
+
],
|
|
759
|
+
&no_guards
|
|
760
|
+
),
|
|
761
|
+
matched(
|
|
762
|
+
0,
|
|
763
|
+
vec![
|
|
764
|
+
Val::Int(1),
|
|
765
|
+
list(vec![Val::Int(2)]),
|
|
766
|
+
Val::Int(3),
|
|
767
|
+
list(vec![])
|
|
768
|
+
]
|
|
769
|
+
)
|
|
770
|
+
);
|
|
771
|
+
assert_eq!(
|
|
772
|
+
eval(
|
|
773
|
+
&env,
|
|
774
|
+
&c,
|
|
775
|
+
&[list(vec![]), list(vec![Val::Int(3)])],
|
|
776
|
+
&no_guards
|
|
777
|
+
),
|
|
778
|
+
matched(1, vec![])
|
|
779
|
+
);
|
|
780
|
+
assert_eq!(
|
|
781
|
+
eval(
|
|
782
|
+
&env,
|
|
783
|
+
&c,
|
|
784
|
+
&[list(vec![Val::Int(3)]), list(vec![])],
|
|
785
|
+
&no_guards
|
|
786
|
+
),
|
|
787
|
+
matched(1, vec![])
|
|
788
|
+
);
|
|
789
|
+
// first-match semantics with overlapping rows
|
|
790
|
+
let (c, _) = build(
|
|
791
|
+
&mut env,
|
|
792
|
+
&[&["(True, _)"], &["(_, True)"], &["(False, False)"]],
|
|
793
|
+
);
|
|
794
|
+
let t = |a, b| Val::Tuple(vec![Val::Bool(a), Val::Bool(b)]);
|
|
795
|
+
assert_eq!(
|
|
796
|
+
eval(&env, &c, &[t(true, true)], &no_guards),
|
|
797
|
+
matched(0, vec![])
|
|
798
|
+
);
|
|
799
|
+
assert_eq!(
|
|
800
|
+
eval(&env, &c, &[t(false, true)], &no_guards),
|
|
801
|
+
matched(1, vec![])
|
|
802
|
+
);
|
|
803
|
+
assert_eq!(
|
|
804
|
+
eval(&env, &c, &[t(false, false)], &no_guards),
|
|
805
|
+
matched(2, vec![])
|
|
806
|
+
);
|
|
807
|
+
assert_eq!(
|
|
808
|
+
eval(&env, &c, &[Val::Tuple(vec![Val::Bool(true)])], &no_guards),
|
|
809
|
+
Outcome::TypeMismatch(env.lookup_type("(,)").unwrap())
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
#[test]
|
|
814
|
+
fn literals_and_default() {
|
|
815
|
+
let mut env = env();
|
|
816
|
+
let (c, _) = build(&mut env, &[&["0"], &["1"], &["n"]]);
|
|
817
|
+
assert_eq!(
|
|
818
|
+
eval(&env, &c, &[Val::Int(0)], &no_guards),
|
|
819
|
+
matched(0, vec![])
|
|
820
|
+
);
|
|
821
|
+
assert_eq!(
|
|
822
|
+
eval(&env, &c, &[Val::Int(1)], &no_guards),
|
|
823
|
+
matched(1, vec![])
|
|
824
|
+
);
|
|
825
|
+
assert_eq!(
|
|
826
|
+
eval(&env, &c, &[Val::Int(9)], &no_guards),
|
|
827
|
+
matched(2, vec![Val::Int(9)])
|
|
828
|
+
);
|
|
829
|
+
assert_eq!(
|
|
830
|
+
eval(&env, &c, &[Val::Str("x".into())], &no_guards),
|
|
831
|
+
Outcome::TypeMismatch(usize::MAX)
|
|
832
|
+
);
|
|
833
|
+
let (c, _) = build(&mut env, &[&["Just \"a\""], &["Just s"], &["Nothing"]]);
|
|
834
|
+
assert_eq!(
|
|
835
|
+
eval(&env, &c, &[just(&env, Val::Str("a".into()))], &no_guards),
|
|
836
|
+
matched(0, vec![])
|
|
837
|
+
);
|
|
838
|
+
assert_eq!(
|
|
839
|
+
eval(&env, &c, &[just(&env, Val::Str("b".into()))], &no_guards),
|
|
840
|
+
matched(1, vec![Val::Str("b".into())])
|
|
841
|
+
);
|
|
842
|
+
// partial function
|
|
843
|
+
let (c, _) = build(&mut env, &[&["Just x"]]);
|
|
844
|
+
assert_eq!(
|
|
845
|
+
eval(&env, &c, &[nothing(&env)], &no_guards),
|
|
846
|
+
Outcome::NoMatch
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
#[test]
|
|
851
|
+
fn guards_fall_through() {
|
|
852
|
+
let mut env = env();
|
|
853
|
+
let (c, _) = build(&mut env, &[&["Just x", "|"], &["Just _"], &["Nothing"]]);
|
|
854
|
+
let positive =
|
|
855
|
+
|clause: usize, binds: &[Val]| clause != 0 || matches!(binds[0], Val::Int(i) if i > 0);
|
|
856
|
+
assert_eq!(
|
|
857
|
+
eval(&env, &c, &[just(&env, Val::Int(3))], &positive),
|
|
858
|
+
matched(0, vec![Val::Int(3)])
|
|
859
|
+
);
|
|
860
|
+
assert_eq!(
|
|
861
|
+
eval(&env, &c, &[just(&env, Val::Int(-3))], &positive),
|
|
862
|
+
matched(1, vec![])
|
|
863
|
+
);
|
|
864
|
+
assert_eq!(
|
|
865
|
+
eval(&env, &c, &[nothing(&env)], &positive),
|
|
866
|
+
matched(2, vec![])
|
|
867
|
+
);
|
|
868
|
+
// guard on the last clause with nothing after it
|
|
869
|
+
let (c, _) = build(&mut env, &[&["x", "|"]]);
|
|
870
|
+
assert_eq!(
|
|
871
|
+
eval(&env, &c, &[Val::Int(1)], &|_, _| false),
|
|
872
|
+
Outcome::NoMatch
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
#[test]
|
|
877
|
+
fn lazy_patterns() {
|
|
878
|
+
let mut env = env();
|
|
879
|
+
let (c, names) = build(&mut env, &[&["~(Just x)"]]);
|
|
880
|
+
assert_eq!(names[0], vec!["x"]);
|
|
881
|
+
assert_eq!(
|
|
882
|
+
eval(&env, &c, &[just(&env, Val::Int(1))], &no_guards),
|
|
883
|
+
matched(0, vec![Val::Int(1)])
|
|
884
|
+
);
|
|
885
|
+
// irrefutable: matches, but destructuring fails
|
|
886
|
+
assert_eq!(
|
|
887
|
+
eval(&env, &c, &[nothing(&env)], &no_guards),
|
|
888
|
+
Outcome::LazyFailed
|
|
889
|
+
);
|
|
890
|
+
// nested lazy with other binders
|
|
891
|
+
let (c, names) = build(&mut env, &[&["(a, ~(b, c))"]]);
|
|
892
|
+
assert_eq!(names[0], vec!["a", "b", "c"]);
|
|
893
|
+
let v = Val::Tuple(vec![
|
|
894
|
+
Val::Int(1),
|
|
895
|
+
Val::Tuple(vec![Val::Int(2), Val::Int(3)]),
|
|
896
|
+
]);
|
|
897
|
+
assert_eq!(
|
|
898
|
+
eval(&env, &c, &[v], &no_guards),
|
|
899
|
+
matched(0, vec![Val::Int(1), Val::Int(2), Val::Int(3)])
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
#[test]
|
|
904
|
+
fn slots_are_allocated_per_case() {
|
|
905
|
+
let mut env = env();
|
|
906
|
+
let (c, _) = build(
|
|
907
|
+
&mut env,
|
|
908
|
+
&[&["Just (Just x)"], &["Just Nothing"], &["Nothing"]],
|
|
909
|
+
);
|
|
910
|
+
assert!(c.n_slots >= 3);
|
|
911
|
+
match &c.tree {
|
|
912
|
+
Tree::SwitchCon {
|
|
913
|
+
slot: 0,
|
|
914
|
+
cases,
|
|
915
|
+
default: None,
|
|
916
|
+
..
|
|
917
|
+
} => assert_eq!(cases.len(), 2),
|
|
918
|
+
other => panic!("unexpected tree {:?}", other),
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|