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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +98 -0
  3. data/LICENSE-APACHE +202 -0
  4. data/LICENSE-MIT +21 -0
  5. data/README.md +1484 -0
  6. data/ext/haskell_match/Cargo.lock +33 -0
  7. data/ext/haskell_match/Cargo.toml +22 -0
  8. data/ext/haskell_match/extconf.rb +41 -0
  9. data/ext/haskell_match/src/core/ast.rs +190 -0
  10. data/ext/haskell_match/src/core/error.rs +52 -0
  11. data/ext/haskell_match/src/core/exhaust.rs +699 -0
  12. data/ext/haskell_match/src/core/hs/ast.rs +256 -0
  13. data/ext/haskell_match/src/core/hs/json.rs +225 -0
  14. data/ext/haskell_match/src/core/hs/layout.rs +346 -0
  15. data/ext/haskell_match/src/core/hs/lexer.rs +688 -0
  16. data/ext/haskell_match/src/core/hs/mod.rs +14 -0
  17. data/ext/haskell_match/src/core/hs/parser.rs +1945 -0
  18. data/ext/haskell_match/src/core/lexer.rs +590 -0
  19. data/ext/haskell_match/src/core/mod.rs +19 -0
  20. data/ext/haskell_match/src/core/parser.rs +1116 -0
  21. data/ext/haskell_match/src/core/pretty.rs +373 -0
  22. data/ext/haskell_match/src/core/resolve.rs +336 -0
  23. data/ext/haskell_match/src/core/tree.rs +921 -0
  24. data/ext/haskell_match/src/core/typecheck.rs +226 -0
  25. data/ext/haskell_match/src/core/types.rs +404 -0
  26. data/ext/haskell_match/src/lib.rs +19 -0
  27. data/ext/haskell_match/src/ruby/mod.rs +1195 -0
  28. data/ext/haskell_match/src/ruby/runtime.rs +1045 -0
  29. data/lib/haskell_match/binding_plan.rb +84 -0
  30. data/lib/haskell_match/case_of.rb +71 -0
  31. data/lib/haskell_match/clauses.rb +354 -0
  32. data/lib/haskell_match/data.rb +417 -0
  33. data/lib/haskell_match/deep_call.rb +98 -0
  34. data/lib/haskell_match/deriving.rb +130 -0
  35. data/lib/haskell_match/dsl.rb +71 -0
  36. data/lib/haskell_match/errors.rb +85 -0
  37. data/lib/haskell_match/field_types.rb +140 -0
  38. data/lib/haskell_match/function.rb +240 -0
  39. data/lib/haskell_match/haskell/compiler.rb +961 -0
  40. data/lib/haskell_match/haskell.rb +326 -0
  41. data/lib/haskell_match/inspect.rb +45 -0
  42. data/lib/haskell_match/lazy_list.rb +210 -0
  43. data/lib/haskell_match/native_loader.rb +64 -0
  44. data/lib/haskell_match/pattern.rb +75 -0
  45. data/lib/haskell_match/pattern_ast.rb +394 -0
  46. data/lib/haskell_match/prelude.rb +448 -0
  47. data/lib/haskell_match/scope.rb +44 -0
  48. data/lib/haskell_match/version.rb +5 -0
  49. data/lib/haskell_match.rb +41 -0
  50. 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(&notes.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
+ }