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,373 @@
|
|
|
1
|
+
//! Haskell-style rendering of patterns, used for diagnostics.
|
|
2
|
+
|
|
3
|
+
use super::ast::{HKey, Lit, Pat};
|
|
4
|
+
use super::types::{TypeEnv, TypeKind, CON_CONS, CON_NIL};
|
|
5
|
+
|
|
6
|
+
/// A witness pattern produced by the exhaustiveness checker.
|
|
7
|
+
#[derive(Clone, Debug, PartialEq)]
|
|
8
|
+
pub enum WPat {
|
|
9
|
+
Wild,
|
|
10
|
+
/// A literal position where the listed literals *are* handled: any other
|
|
11
|
+
/// value of that type is unmatched.
|
|
12
|
+
NotLit(Vec<Lit>),
|
|
13
|
+
Lit(Lit),
|
|
14
|
+
Con(super::types::ConId, Vec<WPat>),
|
|
15
|
+
/// A Hash with these keys (and any others).
|
|
16
|
+
Hash(Vec<(HKey, WPat)>),
|
|
17
|
+
/// A Hash lacking a key from each of the listed key sets: the Hash
|
|
18
|
+
/// patterns with those keys are handled, any other Hash is not.
|
|
19
|
+
NoKeys(Vec<Vec<HKey>>),
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
impl WPat {
|
|
23
|
+
pub fn from_pat(p: &Pat) -> WPat {
|
|
24
|
+
match p {
|
|
25
|
+
Pat::Wild | Pat::Var(_) | Pat::Lazy(_) => WPat::Wild,
|
|
26
|
+
Pat::As(_, p) => WPat::from_pat(p),
|
|
27
|
+
Pat::Lit(l) => WPat::Lit(l.clone()),
|
|
28
|
+
Pat::Con(c, args) => WPat::Con(*c, args.iter().map(WPat::from_pat).collect()),
|
|
29
|
+
Pat::Hash(fields) => WPat::Hash(
|
|
30
|
+
fields
|
|
31
|
+
.iter()
|
|
32
|
+
.map(|(k, p)| (k.clone(), WPat::from_pat(p)))
|
|
33
|
+
.collect(),
|
|
34
|
+
),
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
fn render_keys(keys: &[HKey]) -> String {
|
|
40
|
+
let shown: Vec<String> = keys.iter().map(|k| k.to_string()).collect();
|
|
41
|
+
if keys.len() == 1 {
|
|
42
|
+
format!("the key {}", shown[0])
|
|
43
|
+
} else {
|
|
44
|
+
format!("the keys {{{}}}", shown.join(", "))
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Render a witness vector the way GHC lists unmatched patterns:
|
|
49
|
+
/// `Just (Just _) []` plus a trailing note for literal positions.
|
|
50
|
+
pub fn render_witness(env: &TypeEnv, pats: &[WPat]) -> String {
|
|
51
|
+
let mut parts = Vec::new();
|
|
52
|
+
let mut notes: Vec<String> = Vec::new();
|
|
53
|
+
// GHC parenthesises constructor applications only when a clause has
|
|
54
|
+
// several arguments: `Just (Just _)` but `(Circle _) (Rect _ _)`.
|
|
55
|
+
let atomic = pats.len() > 1;
|
|
56
|
+
for p in pats {
|
|
57
|
+
parts.push(render(env, p, atomic, &mut notes));
|
|
58
|
+
}
|
|
59
|
+
let mut s = parts.join(" ");
|
|
60
|
+
if !notes.is_empty() {
|
|
61
|
+
s.push_str(" where ");
|
|
62
|
+
s.push_str(¬es.join(" and "));
|
|
63
|
+
}
|
|
64
|
+
s
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
fn render(env: &TypeEnv, p: &WPat, atomic: bool, notes: &mut Vec<String>) -> String {
|
|
68
|
+
match p {
|
|
69
|
+
WPat::Wild => "_".to_string(),
|
|
70
|
+
WPat::Lit(l) => render_lit(l, atomic),
|
|
71
|
+
WPat::NotLit(lits) => {
|
|
72
|
+
let name = format!("p{}", notes.len() + 1);
|
|
73
|
+
let shown: Vec<String> = lits.iter().map(|l| l.to_string()).collect();
|
|
74
|
+
notes.push(format!("{} is not one of {{{}}}", name, shown.join(", ")));
|
|
75
|
+
name
|
|
76
|
+
}
|
|
77
|
+
WPat::Hash(fields) => {
|
|
78
|
+
let inner: Vec<String> = fields
|
|
79
|
+
.iter()
|
|
80
|
+
.map(|(k, p)| format!("{} = {}", k, render(env, p, false, notes)))
|
|
81
|
+
.collect();
|
|
82
|
+
format!("{{{}}}", inner.join(", "))
|
|
83
|
+
}
|
|
84
|
+
WPat::NoKeys(sets) => {
|
|
85
|
+
let name = format!("p{}", notes.len() + 1);
|
|
86
|
+
let parts: Vec<String> = sets
|
|
87
|
+
.iter()
|
|
88
|
+
.map(|k| format!("without {}", render_keys(k)))
|
|
89
|
+
.collect();
|
|
90
|
+
notes.push(format!("{} is a Hash {}", name, parts.join(" or ")));
|
|
91
|
+
name
|
|
92
|
+
}
|
|
93
|
+
WPat::Con(c, args) => {
|
|
94
|
+
let con = env.con(*c);
|
|
95
|
+
match env.ty(con.ty).kind {
|
|
96
|
+
TypeKind::Bool => con.name.clone(),
|
|
97
|
+
TypeKind::Tuple(_) => {
|
|
98
|
+
let inner: Vec<String> =
|
|
99
|
+
args.iter().map(|a| render(env, a, false, notes)).collect();
|
|
100
|
+
format!("({})", inner.join(", "))
|
|
101
|
+
}
|
|
102
|
+
TypeKind::List => {
|
|
103
|
+
if *c == CON_NIL {
|
|
104
|
+
return "[]".to_string();
|
|
105
|
+
}
|
|
106
|
+
// try to render as a list literal when the spine is fully known
|
|
107
|
+
let mut items = Vec::new();
|
|
108
|
+
let mut cur = p;
|
|
109
|
+
loop {
|
|
110
|
+
match cur {
|
|
111
|
+
WPat::Con(cc, a) if *cc == CON_CONS => {
|
|
112
|
+
items.push(&a[0]);
|
|
113
|
+
cur = &a[1];
|
|
114
|
+
}
|
|
115
|
+
WPat::Con(cc, _) if *cc == CON_NIL => {
|
|
116
|
+
let inner: Vec<String> =
|
|
117
|
+
items.iter().map(|a| render(env, a, false, notes)).collect();
|
|
118
|
+
return format!("[{}]", inner.join(", "));
|
|
119
|
+
}
|
|
120
|
+
_ => break,
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
let head = render(env, &args[0], true, notes);
|
|
124
|
+
let tail = render_cons_tail(env, &args[1], notes);
|
|
125
|
+
format!("({}:{})", head, tail)
|
|
126
|
+
}
|
|
127
|
+
TypeKind::Adt => {
|
|
128
|
+
if args.is_empty() {
|
|
129
|
+
con.name.clone()
|
|
130
|
+
} else {
|
|
131
|
+
let inner: Vec<String> =
|
|
132
|
+
args.iter().map(|a| render(env, a, true, notes)).collect();
|
|
133
|
+
let s = if con.name.starts_with(':') && inner.len() == 2 {
|
|
134
|
+
format!("{} {} {}", inner[0], con.name, inner[1])
|
|
135
|
+
} else {
|
|
136
|
+
format!("{} {}", con.name, inner.join(" "))
|
|
137
|
+
};
|
|
138
|
+
if atomic {
|
|
139
|
+
format!("({})", s)
|
|
140
|
+
} else {
|
|
141
|
+
s
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
fn render_lit(l: &Lit, atomic: bool) -> String {
|
|
151
|
+
let negative = match l {
|
|
152
|
+
Lit::Int(i) => *i < 0,
|
|
153
|
+
Lit::Big(s) => s.starts_with('-'),
|
|
154
|
+
Lit::Float(f) => *f < 0.0,
|
|
155
|
+
_ => false,
|
|
156
|
+
};
|
|
157
|
+
if negative && atomic {
|
|
158
|
+
format!("({})", l)
|
|
159
|
+
} else {
|
|
160
|
+
l.to_string()
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/// Inside `(h:t)`, a cons tail is rendered without its own parentheses.
|
|
165
|
+
fn render_cons_tail(env: &TypeEnv, p: &WPat, notes: &mut Vec<String>) -> String {
|
|
166
|
+
match p {
|
|
167
|
+
WPat::Con(c, args) if *c == CON_CONS => {
|
|
168
|
+
let head = render(env, &args[0], true, notes);
|
|
169
|
+
let tail = render_cons_tail(env, &args[1], notes);
|
|
170
|
+
format!("{}:{}", head, tail)
|
|
171
|
+
}
|
|
172
|
+
other => render(env, other, true, notes),
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/// Render a resolved pattern (used for messages about user clauses).
|
|
177
|
+
pub fn render_pat(env: &TypeEnv, p: &Pat, names: &[String]) -> String {
|
|
178
|
+
render_pat_inner(env, p, names, false)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
fn render_pat_inner(env: &TypeEnv, p: &Pat, names: &[String], atomic: bool) -> String {
|
|
182
|
+
match p {
|
|
183
|
+
Pat::Wild => "_".into(),
|
|
184
|
+
Pat::Var(v) => names.get(*v).cloned().unwrap_or_else(|| format!("v{}", v)),
|
|
185
|
+
Pat::As(v, inner) => format!(
|
|
186
|
+
"{}@{}",
|
|
187
|
+
names.get(*v).cloned().unwrap_or_else(|| format!("v{}", v)),
|
|
188
|
+
render_pat_inner(env, inner, names, true)
|
|
189
|
+
),
|
|
190
|
+
Pat::Lazy(inner) => format!("~{}", render_pat_inner(env, inner, names, true)),
|
|
191
|
+
Pat::Lit(l) => render_lit(l, atomic),
|
|
192
|
+
Pat::Hash(fields) => {
|
|
193
|
+
let inner: Vec<String> = fields
|
|
194
|
+
.iter()
|
|
195
|
+
.map(|(k, p)| format!("{} = {}", k, render_pat_inner(env, p, names, false)))
|
|
196
|
+
.collect();
|
|
197
|
+
format!("{{{}}}", inner.join(", "))
|
|
198
|
+
}
|
|
199
|
+
Pat::Con(c, args) => {
|
|
200
|
+
let con = env.con(*c);
|
|
201
|
+
match env.ty(con.ty).kind {
|
|
202
|
+
TypeKind::Bool => con.name.clone(),
|
|
203
|
+
TypeKind::Tuple(_) => {
|
|
204
|
+
let inner: Vec<String> = args
|
|
205
|
+
.iter()
|
|
206
|
+
.map(|a| render_pat_inner(env, a, names, false))
|
|
207
|
+
.collect();
|
|
208
|
+
format!("({})", inner.join(", "))
|
|
209
|
+
}
|
|
210
|
+
TypeKind::List => {
|
|
211
|
+
if *c == CON_NIL {
|
|
212
|
+
return "[]".into();
|
|
213
|
+
}
|
|
214
|
+
let mut items = Vec::new();
|
|
215
|
+
let mut cur = p;
|
|
216
|
+
loop {
|
|
217
|
+
match cur {
|
|
218
|
+
Pat::Con(cc, a) if *cc == CON_CONS => {
|
|
219
|
+
items.push(&a[0]);
|
|
220
|
+
cur = &a[1];
|
|
221
|
+
}
|
|
222
|
+
Pat::Con(cc, _) if *cc == CON_NIL => {
|
|
223
|
+
let inner: Vec<String> = items
|
|
224
|
+
.iter()
|
|
225
|
+
.map(|a| render_pat_inner(env, a, names, false))
|
|
226
|
+
.collect();
|
|
227
|
+
return format!("[{}]", inner.join(", "));
|
|
228
|
+
}
|
|
229
|
+
_ => break,
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
let mut parts = vec![render_pat_inner(env, &args[0], names, true)];
|
|
233
|
+
let mut tail = &args[1];
|
|
234
|
+
while let Pat::Con(cc, a) = tail {
|
|
235
|
+
if *cc != CON_CONS {
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
parts.push(render_pat_inner(env, &a[0], names, true));
|
|
239
|
+
tail = &a[1];
|
|
240
|
+
}
|
|
241
|
+
parts.push(render_pat_inner(env, tail, names, true));
|
|
242
|
+
format!("({})", parts.join(":"))
|
|
243
|
+
}
|
|
244
|
+
TypeKind::Adt => {
|
|
245
|
+
if args.is_empty() {
|
|
246
|
+
con.name.clone()
|
|
247
|
+
} else {
|
|
248
|
+
let inner: Vec<String> = args
|
|
249
|
+
.iter()
|
|
250
|
+
.map(|a| render_pat_inner(env, a, names, true))
|
|
251
|
+
.collect();
|
|
252
|
+
let s = if con.name.starts_with(':') && inner.len() == 2 {
|
|
253
|
+
format!("{} {} {}", inner[0], con.name, inner[1])
|
|
254
|
+
} else {
|
|
255
|
+
format!("{} {}", con.name, inner.join(" "))
|
|
256
|
+
};
|
|
257
|
+
if atomic {
|
|
258
|
+
format!("({})", s)
|
|
259
|
+
} else {
|
|
260
|
+
s
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
#[cfg(test)]
|
|
270
|
+
mod tests {
|
|
271
|
+
use super::*;
|
|
272
|
+
use crate::core::parser::parse_pattern;
|
|
273
|
+
use crate::core::resolve::resolve_clause;
|
|
274
|
+
use crate::core::types::ConSpec;
|
|
275
|
+
|
|
276
|
+
fn env() -> TypeEnv {
|
|
277
|
+
let mut env = TypeEnv::new();
|
|
278
|
+
env.register(
|
|
279
|
+
"Maybe",
|
|
280
|
+
&[
|
|
281
|
+
ConSpec {
|
|
282
|
+
name: "Nothing".into(),
|
|
283
|
+
arity: 0,
|
|
284
|
+
fields: None,
|
|
285
|
+
handle: 0,
|
|
286
|
+
},
|
|
287
|
+
ConSpec {
|
|
288
|
+
name: "Just".into(),
|
|
289
|
+
arity: 1,
|
|
290
|
+
fields: None,
|
|
291
|
+
handle: 0,
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
)
|
|
295
|
+
.unwrap();
|
|
296
|
+
env
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
fn roundtrip(env: &mut TypeEnv, src: &str) -> String {
|
|
300
|
+
let raw = parse_pattern(src).unwrap();
|
|
301
|
+
let (pats, b) = resolve_clause(env, &[raw]).unwrap();
|
|
302
|
+
render_pat(env, &pats[0], &b.names)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
#[test]
|
|
306
|
+
fn renders_patterns() {
|
|
307
|
+
let mut env = env();
|
|
308
|
+
for (src, want) in [
|
|
309
|
+
("Just x", "Just x"),
|
|
310
|
+
("Just (Just _)", "Just (Just _)"),
|
|
311
|
+
("(x:xs)", "(x:xs)"),
|
|
312
|
+
("x:y:rest", "(x:y:rest)"),
|
|
313
|
+
("[a, b]", "[a, b]"),
|
|
314
|
+
("[]", "[]"),
|
|
315
|
+
("(a, Nothing, [])", "(a, Nothing, [])"),
|
|
316
|
+
("()", "()"),
|
|
317
|
+
("all@(x:_)", "all@(x:_)"),
|
|
318
|
+
("~(Just y)", "~(Just y)"),
|
|
319
|
+
("Just (-1)", "Just (-1)"),
|
|
320
|
+
("-1", "-1"),
|
|
321
|
+
("Just \"s\"", "Just ['s']"),
|
|
322
|
+
("Just 'c'", "Just 'c'"),
|
|
323
|
+
("\"ab\"", "['a', 'b']"),
|
|
324
|
+
("\"\"", "[]"),
|
|
325
|
+
("Just :ok", "Just :ok"),
|
|
326
|
+
("Just 1.5", "Just 1.5"),
|
|
327
|
+
("True", "True"),
|
|
328
|
+
("Just (x:xs)", "Just (x:xs)"),
|
|
329
|
+
] {
|
|
330
|
+
assert_eq!(roundtrip(&mut env, src), want);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
#[test]
|
|
335
|
+
fn renders_witnesses() {
|
|
336
|
+
let mut env = env();
|
|
337
|
+
let just = env.lookup_con("Just").unwrap();
|
|
338
|
+
let nothing = env.lookup_con("Nothing").unwrap();
|
|
339
|
+
let t2 = env.tuple_con(2);
|
|
340
|
+
let w = vec![
|
|
341
|
+
WPat::Con(just, vec![WPat::Con(nothing, vec![])]),
|
|
342
|
+
WPat::Con(
|
|
343
|
+
CON_CONS,
|
|
344
|
+
vec![
|
|
345
|
+
WPat::Wild,
|
|
346
|
+
WPat::Con(CON_CONS, vec![WPat::Wild, WPat::Wild]),
|
|
347
|
+
],
|
|
348
|
+
),
|
|
349
|
+
];
|
|
350
|
+
assert_eq!(render_witness(&env, &w), "(Just Nothing) (_:_:_)");
|
|
351
|
+
assert_eq!(render_witness(&env, &w[..1]), "Just Nothing");
|
|
352
|
+
assert_eq!(
|
|
353
|
+
render_witness(&env, &[WPat::Con(just, vec![WPat::Lit(Lit::Int(-1))])]),
|
|
354
|
+
"Just (-1)"
|
|
355
|
+
);
|
|
356
|
+
let w = vec![WPat::Con(
|
|
357
|
+
t2,
|
|
358
|
+
vec![
|
|
359
|
+
WPat::NotLit(vec![Lit::Int(0), Lit::Int(1)]),
|
|
360
|
+
WPat::Con(CON_NIL, vec![]),
|
|
361
|
+
],
|
|
362
|
+
)];
|
|
363
|
+
assert_eq!(
|
|
364
|
+
render_witness(&env, &w),
|
|
365
|
+
"(p1, []) where p1 is not one of {0, 1}"
|
|
366
|
+
);
|
|
367
|
+
let w = vec![WPat::Con(
|
|
368
|
+
CON_CONS,
|
|
369
|
+
vec![WPat::Wild, WPat::Con(CON_NIL, vec![])],
|
|
370
|
+
)];
|
|
371
|
+
assert_eq!(render_witness(&env, &w), "[_]");
|
|
372
|
+
}
|
|
373
|
+
}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
//! Resolution of `RawPat` into `Pat`: constructor lookup, arity checking,
|
|
2
|
+
//! record field handling, variable numbering and duplicate detection.
|
|
3
|
+
|
|
4
|
+
use super::ast::{Pat, RawPat, VarId};
|
|
5
|
+
use super::error::{CoreError, ErrorKind, Result};
|
|
6
|
+
use super::types::{TypeEnv, CON_CONS, CON_FALSE, CON_NIL, CON_TRUE};
|
|
7
|
+
|
|
8
|
+
/// Per-clause binding table. Variables are numbered in order of first
|
|
9
|
+
/// appearance, left to right across all argument patterns of the clause.
|
|
10
|
+
#[derive(Clone, Debug, Default, PartialEq)]
|
|
11
|
+
pub struct Bindings {
|
|
12
|
+
pub names: Vec<String>,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
impl Bindings {
|
|
16
|
+
fn bind(&mut self, name: &str) -> Result<VarId> {
|
|
17
|
+
if self.names.iter().any(|n| n == name) {
|
|
18
|
+
return Err(CoreError::new(
|
|
19
|
+
ErrorKind::DuplicateVariable,
|
|
20
|
+
format!("conflicting definitions for '{}' in the same clause", name),
|
|
21
|
+
));
|
|
22
|
+
}
|
|
23
|
+
self.names.push(name.to_string());
|
|
24
|
+
Ok(self.names.len() - 1)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// Resolve one clause consisting of `arity` argument patterns.
|
|
29
|
+
pub fn resolve_clause(env: &mut TypeEnv, raws: &[RawPat]) -> Result<(Vec<Pat>, Bindings)> {
|
|
30
|
+
let mut b = Bindings::default();
|
|
31
|
+
let mut pats = Vec::with_capacity(raws.len());
|
|
32
|
+
for raw in raws {
|
|
33
|
+
pats.push(resolve(env, raw, &mut b)?);
|
|
34
|
+
}
|
|
35
|
+
Ok((pats, b))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fn resolve(env: &mut TypeEnv, raw: &RawPat, b: &mut Bindings) -> Result<Pat> {
|
|
39
|
+
Ok(match raw {
|
|
40
|
+
RawPat::Wild => Pat::Wild,
|
|
41
|
+
RawPat::Var(v) => Pat::Var(b.bind(v)?),
|
|
42
|
+
RawPat::As(v, p) => {
|
|
43
|
+
let id = b.bind(v)?;
|
|
44
|
+
Pat::As(id, Box::new(resolve(env, p, b)?))
|
|
45
|
+
}
|
|
46
|
+
RawPat::Lazy(p) => {
|
|
47
|
+
let inner = resolve(env, p, b)?;
|
|
48
|
+
// `~_`, `~x` are already irrefutable; keep the tree small
|
|
49
|
+
match inner {
|
|
50
|
+
Pat::Wild | Pat::Var(_) | Pat::Lazy(_) => inner,
|
|
51
|
+
other => Pat::Lazy(Box::new(other)),
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
RawPat::Bang(p) => resolve(env, p, b)?,
|
|
55
|
+
RawPat::Lit(l) => Pat::Lit(l.clone()),
|
|
56
|
+
RawPat::Tuple(items) => {
|
|
57
|
+
let con = env.tuple_con(items.len());
|
|
58
|
+
let mut args = Vec::with_capacity(items.len());
|
|
59
|
+
for it in items {
|
|
60
|
+
args.push(resolve(env, it, b)?);
|
|
61
|
+
}
|
|
62
|
+
Pat::Con(con, args)
|
|
63
|
+
}
|
|
64
|
+
RawPat::List(items) => {
|
|
65
|
+
let mut acc = Pat::Con(CON_NIL, vec![]);
|
|
66
|
+
let mut resolved = Vec::with_capacity(items.len());
|
|
67
|
+
for it in items {
|
|
68
|
+
resolved.push(resolve(env, it, b)?);
|
|
69
|
+
}
|
|
70
|
+
for p in resolved.into_iter().rev() {
|
|
71
|
+
acc = Pat::Con(CON_CONS, vec![p, acc]);
|
|
72
|
+
}
|
|
73
|
+
acc
|
|
74
|
+
}
|
|
75
|
+
RawPat::Cons(h, t) => {
|
|
76
|
+
let h = resolve(env, h, b)?;
|
|
77
|
+
let t = resolve(env, t, b)?;
|
|
78
|
+
Pat::Con(CON_CONS, vec![h, t])
|
|
79
|
+
}
|
|
80
|
+
RawPat::Con(name, args) => {
|
|
81
|
+
let con = lookup(env, name)?;
|
|
82
|
+
let arity = env.con(con).arity;
|
|
83
|
+
if arity != args.len() {
|
|
84
|
+
return Err(CoreError::new(
|
|
85
|
+
ErrorKind::Arity,
|
|
86
|
+
format!(
|
|
87
|
+
"the constructor '{}' should have {} argument{}, but has been given {}",
|
|
88
|
+
name,
|
|
89
|
+
arity,
|
|
90
|
+
if arity == 1 { "" } else { "s" },
|
|
91
|
+
args.len()
|
|
92
|
+
),
|
|
93
|
+
));
|
|
94
|
+
}
|
|
95
|
+
let mut rargs = Vec::with_capacity(args.len());
|
|
96
|
+
for a in args {
|
|
97
|
+
rargs.push(resolve(env, a, b)?);
|
|
98
|
+
}
|
|
99
|
+
Pat::Con(con, rargs)
|
|
100
|
+
}
|
|
101
|
+
RawPat::Hash(fields) => {
|
|
102
|
+
let mut out = Vec::with_capacity(fields.len());
|
|
103
|
+
for (k, p) in fields {
|
|
104
|
+
out.push((k.clone(), resolve(env, p, b)?));
|
|
105
|
+
}
|
|
106
|
+
Pat::Hash(out)
|
|
107
|
+
}
|
|
108
|
+
RawPat::Record(name, fields, wildcard) => {
|
|
109
|
+
let con = lookup(env, name)?;
|
|
110
|
+
let (arity, field_names) = {
|
|
111
|
+
let c = env.con(con);
|
|
112
|
+
(c.arity, c.fields.clone())
|
|
113
|
+
};
|
|
114
|
+
let mut args = vec![None; arity];
|
|
115
|
+
if !fields.is_empty() || *wildcard {
|
|
116
|
+
let field_names = field_names.ok_or_else(|| {
|
|
117
|
+
CoreError::new(
|
|
118
|
+
ErrorKind::Field,
|
|
119
|
+
format!("constructor '{}' does not have named fields", name),
|
|
120
|
+
)
|
|
121
|
+
})?;
|
|
122
|
+
for (f, p) in fields {
|
|
123
|
+
let idx = field_names.iter().position(|n| n == f).ok_or_else(|| {
|
|
124
|
+
CoreError::new(
|
|
125
|
+
ErrorKind::Field,
|
|
126
|
+
format!(
|
|
127
|
+
"constructor '{}' does not have a field named '{}' (fields: {})",
|
|
128
|
+
name,
|
|
129
|
+
f,
|
|
130
|
+
field_names.join(", ")
|
|
131
|
+
),
|
|
132
|
+
)
|
|
133
|
+
})?;
|
|
134
|
+
args[idx] = Some(resolve(env, p, b)?);
|
|
135
|
+
}
|
|
136
|
+
if *wildcard {
|
|
137
|
+
// RecordWildCards: bind every remaining field to its own name
|
|
138
|
+
for (i, slot) in args.iter_mut().enumerate() {
|
|
139
|
+
if slot.is_none() {
|
|
140
|
+
*slot = Some(Pat::Var(b.bind(&field_names[i])?));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
Pat::Con(
|
|
146
|
+
con,
|
|
147
|
+
args.into_iter().map(|a| a.unwrap_or(Pat::Wild)).collect(),
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
fn lookup(env: &TypeEnv, name: &str) -> Result<super::types::ConId> {
|
|
154
|
+
match name {
|
|
155
|
+
"True" => return Ok(CON_TRUE),
|
|
156
|
+
"False" => return Ok(CON_FALSE),
|
|
157
|
+
_ => {}
|
|
158
|
+
}
|
|
159
|
+
env.lookup_con(name).ok_or_else(|| {
|
|
160
|
+
CoreError::new(
|
|
161
|
+
ErrorKind::UnknownConstructor,
|
|
162
|
+
format!("not in scope: data constructor '{}'", name),
|
|
163
|
+
)
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
#[cfg(test)]
|
|
168
|
+
mod tests {
|
|
169
|
+
use super::*;
|
|
170
|
+
use crate::core::ast::Lit;
|
|
171
|
+
use crate::core::parser::parse_pattern;
|
|
172
|
+
use crate::core::types::ConSpec;
|
|
173
|
+
|
|
174
|
+
fn env() -> TypeEnv {
|
|
175
|
+
let mut env = TypeEnv::new();
|
|
176
|
+
env.register(
|
|
177
|
+
"Maybe",
|
|
178
|
+
&[
|
|
179
|
+
ConSpec {
|
|
180
|
+
name: "Nothing".into(),
|
|
181
|
+
arity: 0,
|
|
182
|
+
fields: None,
|
|
183
|
+
handle: 0,
|
|
184
|
+
},
|
|
185
|
+
ConSpec {
|
|
186
|
+
name: "Just".into(),
|
|
187
|
+
arity: 1,
|
|
188
|
+
fields: None,
|
|
189
|
+
handle: 0,
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
)
|
|
193
|
+
.unwrap();
|
|
194
|
+
env.register(
|
|
195
|
+
"Person",
|
|
196
|
+
&[ConSpec {
|
|
197
|
+
name: "Person".into(),
|
|
198
|
+
arity: 2,
|
|
199
|
+
fields: Some(vec!["name".into(), "age".into()]),
|
|
200
|
+
handle: 0,
|
|
201
|
+
}],
|
|
202
|
+
)
|
|
203
|
+
.unwrap();
|
|
204
|
+
env
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
fn res(env: &mut TypeEnv, srcs: &[&str]) -> Result<(Vec<Pat>, Bindings)> {
|
|
208
|
+
let mut raws: Vec<RawPat> = Vec::new();
|
|
209
|
+
for s in srcs {
|
|
210
|
+
raws.push(parse_pattern(s)?);
|
|
211
|
+
}
|
|
212
|
+
resolve_clause(env, &raws)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
#[test]
|
|
216
|
+
fn resolves_constructors_and_sugar() {
|
|
217
|
+
let mut env = env();
|
|
218
|
+
let just = env.lookup_con("Just").unwrap();
|
|
219
|
+
let (pats, b) = res(&mut env, &["Just x"]).unwrap();
|
|
220
|
+
assert_eq!(pats, vec![Pat::Con(just, vec![Pat::Var(0)])]);
|
|
221
|
+
assert_eq!(b.names, vec!["x"]);
|
|
222
|
+
|
|
223
|
+
let (pats, b) = res(&mut env, &["[a, b]"]).unwrap();
|
|
224
|
+
assert_eq!(
|
|
225
|
+
pats[0],
|
|
226
|
+
Pat::Con(
|
|
227
|
+
CON_CONS,
|
|
228
|
+
vec![
|
|
229
|
+
Pat::Var(0),
|
|
230
|
+
Pat::Con(CON_CONS, vec![Pat::Var(1), Pat::Con(CON_NIL, vec![])])
|
|
231
|
+
]
|
|
232
|
+
)
|
|
233
|
+
);
|
|
234
|
+
assert_eq!(b.names, vec!["a", "b"]);
|
|
235
|
+
|
|
236
|
+
let t2 = env.tuple_con(2);
|
|
237
|
+
let (pats, _) = res(&mut env, &["(True, False)"]).unwrap();
|
|
238
|
+
assert_eq!(
|
|
239
|
+
pats[0],
|
|
240
|
+
Pat::Con(
|
|
241
|
+
t2,
|
|
242
|
+
vec![Pat::Con(CON_TRUE, vec![]), Pat::Con(CON_FALSE, vec![])]
|
|
243
|
+
)
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
let (pats, b) = res(&mut env, &["all@(x:xs)", "~(Just y)", "!z"]).unwrap();
|
|
247
|
+
assert_eq!(b.names, vec!["all", "x", "xs", "y", "z"]);
|
|
248
|
+
assert_eq!(
|
|
249
|
+
pats[0],
|
|
250
|
+
Pat::As(
|
|
251
|
+
0,
|
|
252
|
+
Box::new(Pat::Con(CON_CONS, vec![Pat::Var(1), Pat::Var(2)]))
|
|
253
|
+
)
|
|
254
|
+
);
|
|
255
|
+
assert_eq!(
|
|
256
|
+
pats[1],
|
|
257
|
+
Pat::Lazy(Box::new(Pat::Con(just, vec![Pat::Var(3)])))
|
|
258
|
+
);
|
|
259
|
+
assert_eq!(pats[2], Pat::Var(4));
|
|
260
|
+
// ~x is just x
|
|
261
|
+
let (pats, _) = res(&mut env, &["~x"]).unwrap();
|
|
262
|
+
assert_eq!(pats[0], Pat::Var(0));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
#[test]
|
|
266
|
+
fn records() {
|
|
267
|
+
let mut env = env();
|
|
268
|
+
let person = env.lookup_con("Person").unwrap();
|
|
269
|
+
let (pats, b) = res(&mut env, &["Person { age = a }"]).unwrap();
|
|
270
|
+
assert_eq!(pats[0], Pat::Con(person, vec![Pat::Wild, Pat::Var(0)]));
|
|
271
|
+
assert_eq!(b.names, vec!["a"]);
|
|
272
|
+
let (pats, b) = res(&mut env, &["Person { name, .. }"]).unwrap();
|
|
273
|
+
assert_eq!(pats[0], Pat::Con(person, vec![Pat::Var(0), Pat::Var(1)]));
|
|
274
|
+
assert_eq!(b.names, vec!["name", "age"]);
|
|
275
|
+
let (pats, _) = res(&mut env, &["Person {}"]).unwrap();
|
|
276
|
+
assert_eq!(pats[0], Pat::Con(person, vec![Pat::Wild, Pat::Wild]));
|
|
277
|
+
// `Con {}` is allowed for positional constructors too
|
|
278
|
+
let (pats, _) = res(&mut env, &["Just {}"]).unwrap();
|
|
279
|
+
assert_eq!(
|
|
280
|
+
pats[0],
|
|
281
|
+
Pat::Con(env.lookup_con("Just").unwrap(), vec![Pat::Wild])
|
|
282
|
+
);
|
|
283
|
+
let (pats, _) = res(&mut env, &["Person 'b' 3"]).unwrap();
|
|
284
|
+
assert_eq!(
|
|
285
|
+
pats[0],
|
|
286
|
+
Pat::Con(
|
|
287
|
+
person,
|
|
288
|
+
vec![Pat::Lit(Lit::Char('b')), Pat::Lit(Lit::Int(3))]
|
|
289
|
+
)
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
#[test]
|
|
294
|
+
fn errors() {
|
|
295
|
+
let mut env = env();
|
|
296
|
+
assert_eq!(
|
|
297
|
+
res(&mut env, &["Nope x"]).unwrap_err().kind,
|
|
298
|
+
ErrorKind::UnknownConstructor
|
|
299
|
+
);
|
|
300
|
+
assert_eq!(res(&mut env, &["Just"]).unwrap_err().kind, ErrorKind::Arity);
|
|
301
|
+
assert_eq!(
|
|
302
|
+
res(&mut env, &["Just a b"]).unwrap_err().kind,
|
|
303
|
+
ErrorKind::Arity
|
|
304
|
+
);
|
|
305
|
+
assert_eq!(
|
|
306
|
+
res(&mut env, &["Nothing x"]).unwrap_err().kind,
|
|
307
|
+
ErrorKind::Arity
|
|
308
|
+
);
|
|
309
|
+
assert_eq!(
|
|
310
|
+
res(&mut env, &["(x, x)"]).unwrap_err().kind,
|
|
311
|
+
ErrorKind::DuplicateVariable
|
|
312
|
+
);
|
|
313
|
+
assert_eq!(
|
|
314
|
+
res(&mut env, &["x", "Just x"]).unwrap_err().kind,
|
|
315
|
+
ErrorKind::DuplicateVariable
|
|
316
|
+
);
|
|
317
|
+
assert_eq!(
|
|
318
|
+
res(&mut env, &["Person { nome = n }"]).unwrap_err().kind,
|
|
319
|
+
ErrorKind::Field
|
|
320
|
+
);
|
|
321
|
+
assert_eq!(
|
|
322
|
+
res(&mut env, &["Just { value = v }"]).unwrap_err().kind,
|
|
323
|
+
ErrorKind::Field
|
|
324
|
+
);
|
|
325
|
+
assert_eq!(
|
|
326
|
+
res(&mut env, &["Just { .. }"]).unwrap_err().kind,
|
|
327
|
+
ErrorKind::Field
|
|
328
|
+
);
|
|
329
|
+
assert_eq!(
|
|
330
|
+
res(&mut env, &["Person { name = n, name = m }"])
|
|
331
|
+
.unwrap_err()
|
|
332
|
+
.kind,
|
|
333
|
+
ErrorKind::Field
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
}
|