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,1116 @@
|
|
|
1
|
+
//! Recursive-descent parsers for Haskell patterns and `data` declarations.
|
|
2
|
+
//!
|
|
3
|
+
//! Pattern grammar (a faithful subset of Haskell 2010 plus a few extensions):
|
|
4
|
+
//!
|
|
5
|
+
//! ```text
|
|
6
|
+
//! pattern := lpat (':' pattern)? -- cons, right associative
|
|
7
|
+
//! lpat := '-' number
|
|
8
|
+
//! | ConId apat+ -- constructor application
|
|
9
|
+
//! | apat
|
|
10
|
+
//! apat := var ('@' apat)? -- variable / as-pattern
|
|
11
|
+
//! | '~' apat | '!' apat -- lazy / bang
|
|
12
|
+
//! | ConId -- nullary constructor
|
|
13
|
+
//! | ConId '{' fpat, ... [..] '}' -- record pattern
|
|
14
|
+
//! | literal -- 1 1.5 'c' :sym
|
|
15
|
+
//! | string -- "abc" = ['a', 'b', 'c']
|
|
16
|
+
//! | '_'
|
|
17
|
+
//! | '(' ')' | '(' pattern ')' | '(' pattern ',' pattern ... ')'
|
|
18
|
+
//! | '[' ']' | '[' pattern ',' ... ']'
|
|
19
|
+
//! fpat := var '=' pattern | var -- NamedFieldPuns
|
|
20
|
+
//! ```
|
|
21
|
+
|
|
22
|
+
use super::ast::{HKey, Lit, RawPat};
|
|
23
|
+
use super::error::{CoreError, ErrorKind, Result};
|
|
24
|
+
use super::lexer::{tokenize, Tok, Token};
|
|
25
|
+
|
|
26
|
+
struct P {
|
|
27
|
+
toks: Vec<Token>,
|
|
28
|
+
i: usize,
|
|
29
|
+
src: String,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
impl P {
|
|
33
|
+
fn peek(&self) -> Option<&Tok> {
|
|
34
|
+
self.toks.get(self.i).map(|t| &t.tok)
|
|
35
|
+
}
|
|
36
|
+
fn peek_at(&self, n: usize) -> Option<&Tok> {
|
|
37
|
+
self.toks.get(self.i + n).map(|t| &t.tok)
|
|
38
|
+
}
|
|
39
|
+
fn next(&mut self) -> Option<Tok> {
|
|
40
|
+
let t = self.toks.get(self.i).map(|t| t.tok.clone());
|
|
41
|
+
if t.is_some() {
|
|
42
|
+
self.i += 1;
|
|
43
|
+
}
|
|
44
|
+
t
|
|
45
|
+
}
|
|
46
|
+
fn pos(&self) -> usize {
|
|
47
|
+
self.toks
|
|
48
|
+
.get(self.i)
|
|
49
|
+
.map(|t| t.pos + 1)
|
|
50
|
+
.unwrap_or_else(|| self.src.chars().count() + 1)
|
|
51
|
+
}
|
|
52
|
+
fn expect(&mut self, want: Tok, what: &str) -> Result<()> {
|
|
53
|
+
match self.next() {
|
|
54
|
+
Some(ref t) if *t == want => Ok(()),
|
|
55
|
+
Some(t) => Err(self.err(format!("expected {} but found {}", what, describe(&t)))),
|
|
56
|
+
None => Err(self.err(format!("expected {} but reached end of pattern", what))),
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
fn err(&self, msg: String) -> CoreError {
|
|
60
|
+
let at = if self.i > 0 && self.i <= self.toks.len() {
|
|
61
|
+
self.toks[self.i - 1].pos + 1
|
|
62
|
+
} else {
|
|
63
|
+
self.pos()
|
|
64
|
+
};
|
|
65
|
+
CoreError::syntax(format!("{} (column {} in {:?})", msg, at, self.src))
|
|
66
|
+
}
|
|
67
|
+
fn err_here(&self, msg: String) -> CoreError {
|
|
68
|
+
CoreError::syntax(format!("{} (column {} in {:?})", msg, self.pos(), self.src))
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fn describe(t: &Tok) -> String {
|
|
73
|
+
match t {
|
|
74
|
+
Tok::LParen => "'('".into(),
|
|
75
|
+
Tok::RParen => "')'".into(),
|
|
76
|
+
Tok::LBracket => "'['".into(),
|
|
77
|
+
Tok::RBracket => "']'".into(),
|
|
78
|
+
Tok::LBrace => "'{'".into(),
|
|
79
|
+
Tok::RBrace => "'}'".into(),
|
|
80
|
+
Tok::Comma => "','".into(),
|
|
81
|
+
Tok::Colon => "':'".into(),
|
|
82
|
+
Tok::ConSym(s) => format!("'{}'", s),
|
|
83
|
+
Tok::DoubleColon => "'::'".into(),
|
|
84
|
+
Tok::Equals => "'='".into(),
|
|
85
|
+
Tok::At => "'@'".into(),
|
|
86
|
+
Tok::Tilde => "'~'".into(),
|
|
87
|
+
Tok::Bang => "'!'".into(),
|
|
88
|
+
Tok::Minus => "'-'".into(),
|
|
89
|
+
Tok::Pipe => "'|'".into(),
|
|
90
|
+
Tok::Arrow => "'->'".into(),
|
|
91
|
+
Tok::DotDot => "'..'".into(),
|
|
92
|
+
Tok::Underscore => "'_'".into(),
|
|
93
|
+
Tok::VarId(v) => format!("variable '{}'", v),
|
|
94
|
+
Tok::ConId(c) => format!("constructor '{}'", c),
|
|
95
|
+
Tok::Int(s) | Tok::Float(s) => format!("literal {}", s),
|
|
96
|
+
Tok::Str(s) => format!("literal {:?}", s),
|
|
97
|
+
Tok::Char(s) => format!("literal '{}'", s),
|
|
98
|
+
Tok::Sym(s) => format!("literal :{}", s),
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// Parse one complete pattern.
|
|
103
|
+
pub fn parse_pattern(src: &str) -> Result<RawPat> {
|
|
104
|
+
let toks = tokenize(src)?;
|
|
105
|
+
let mut p = P {
|
|
106
|
+
toks,
|
|
107
|
+
i: 0,
|
|
108
|
+
src: src.to_string(),
|
|
109
|
+
};
|
|
110
|
+
if p.peek().is_none() {
|
|
111
|
+
return Err(CoreError::syntax("empty pattern"));
|
|
112
|
+
}
|
|
113
|
+
let pat = pattern(&mut p)?;
|
|
114
|
+
if let Some(t) = p.peek() {
|
|
115
|
+
return Err(p.err_here(format!("unexpected {} after pattern", describe(t))));
|
|
116
|
+
}
|
|
117
|
+
Ok(pat)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
fn pattern(p: &mut P) -> Result<RawPat> {
|
|
121
|
+
let head = lpat(p)?;
|
|
122
|
+
if p.peek() == Some(&Tok::Colon) {
|
|
123
|
+
p.next();
|
|
124
|
+
if p.peek().is_none() {
|
|
125
|
+
return Err(p.err("expected a pattern after ':'".into()));
|
|
126
|
+
}
|
|
127
|
+
let tail = pattern(p)?;
|
|
128
|
+
return Ok(RawPat::Cons(Box::new(head), Box::new(tail)));
|
|
129
|
+
}
|
|
130
|
+
// infix constructor: `l :+: r` (chains associate to the right)
|
|
131
|
+
if let Some(Tok::ConSym(op)) = p.peek().cloned() {
|
|
132
|
+
p.next();
|
|
133
|
+
if p.peek().is_none() {
|
|
134
|
+
return Err(p.err(format!("expected a pattern after '{}'", op)));
|
|
135
|
+
}
|
|
136
|
+
let rhs = pattern(p)?;
|
|
137
|
+
return Ok(RawPat::Con(op, vec![head, rhs]));
|
|
138
|
+
}
|
|
139
|
+
Ok(head)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
fn starts_apat(t: Option<&Tok>) -> bool {
|
|
143
|
+
matches!(
|
|
144
|
+
t,
|
|
145
|
+
Some(Tok::VarId(_))
|
|
146
|
+
| Some(Tok::ConId(_))
|
|
147
|
+
| Some(Tok::Int(_))
|
|
148
|
+
| Some(Tok::Float(_))
|
|
149
|
+
| Some(Tok::Str(_))
|
|
150
|
+
| Some(Tok::Char(_))
|
|
151
|
+
| Some(Tok::Sym(_))
|
|
152
|
+
| Some(Tok::LBrace)
|
|
153
|
+
| Some(Tok::Underscore)
|
|
154
|
+
| Some(Tok::LParen)
|
|
155
|
+
| Some(Tok::LBracket)
|
|
156
|
+
| Some(Tok::Tilde)
|
|
157
|
+
| Some(Tok::Bang)
|
|
158
|
+
| Some(Tok::Minus)
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
fn lpat(p: &mut P) -> Result<RawPat> {
|
|
163
|
+
match p.peek() {
|
|
164
|
+
Some(Tok::ConId(_)) if p.peek_at(1) != Some(&Tok::LBrace) => {
|
|
165
|
+
let name = match p.next() {
|
|
166
|
+
Some(Tok::ConId(n)) => n,
|
|
167
|
+
_ => unreachable!(),
|
|
168
|
+
};
|
|
169
|
+
let mut args = Vec::new();
|
|
170
|
+
while starts_apat(p.peek()) {
|
|
171
|
+
args.push(apat(p)?);
|
|
172
|
+
}
|
|
173
|
+
Ok(RawPat::Con(name, args))
|
|
174
|
+
}
|
|
175
|
+
// prefix use of an infix constructor: `(:+:) a b`
|
|
176
|
+
Some(Tok::LParen)
|
|
177
|
+
if matches!(p.peek_at(1), Some(Tok::ConSym(_)))
|
|
178
|
+
&& p.peek_at(2) == Some(&Tok::RParen) =>
|
|
179
|
+
{
|
|
180
|
+
p.next();
|
|
181
|
+
let name = match p.next() {
|
|
182
|
+
Some(Tok::ConSym(n)) => n,
|
|
183
|
+
_ => unreachable!(),
|
|
184
|
+
};
|
|
185
|
+
p.next();
|
|
186
|
+
let mut args = Vec::new();
|
|
187
|
+
while starts_apat(p.peek()) {
|
|
188
|
+
args.push(apat(p)?);
|
|
189
|
+
}
|
|
190
|
+
Ok(RawPat::Con(name, args))
|
|
191
|
+
}
|
|
192
|
+
_ => apat(p),
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
fn negative_literal(p: &mut P) -> Result<RawPat> {
|
|
197
|
+
// '-' already consumed
|
|
198
|
+
match p.next() {
|
|
199
|
+
Some(Tok::Int(s)) => Ok(RawPat::Lit(int_lit(&format!("-{}", s)))),
|
|
200
|
+
Some(Tok::Float(s)) => {
|
|
201
|
+
let f: f64 = s
|
|
202
|
+
.parse()
|
|
203
|
+
.map_err(|_| p.err(format!("malformed float literal {}", s)))?;
|
|
204
|
+
Ok(RawPat::Lit(float_lit(-f)))
|
|
205
|
+
}
|
|
206
|
+
Some(t) => Err(p.err(format!(
|
|
207
|
+
"expected a numeric literal after '-' but found {}",
|
|
208
|
+
describe(&t)
|
|
209
|
+
))),
|
|
210
|
+
None => Err(p.err("expected a numeric literal after '-'".into())),
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
pub(crate) fn int_lit(text: &str) -> Lit {
|
|
215
|
+
match text.parse::<i64>() {
|
|
216
|
+
Ok(i) => Lit::Int(i),
|
|
217
|
+
Err(_) => {
|
|
218
|
+
// canonicalise: strip leading zeros / plus sign
|
|
219
|
+
let (neg, digits) = match text.strip_prefix('-') {
|
|
220
|
+
Some(d) => (true, d),
|
|
221
|
+
None => (false, text),
|
|
222
|
+
};
|
|
223
|
+
let digits = digits.trim_start_matches('0');
|
|
224
|
+
let digits = if digits.is_empty() { "0" } else { digits };
|
|
225
|
+
if neg {
|
|
226
|
+
Lit::Big(format!("-{}", digits))
|
|
227
|
+
} else {
|
|
228
|
+
Lit::Big(digits.to_string())
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
pub(crate) fn float_lit(f: f64) -> Lit {
|
|
235
|
+
// `0.0` and `0` denote the same value (Ruby: 0 == 0.0), so integral floats
|
|
236
|
+
// that fit in an i64 are canonicalised to integers.
|
|
237
|
+
if f.fract() == 0.0 && f.is_finite() && f.abs() < 9.0e15 {
|
|
238
|
+
Lit::Int(f as i64)
|
|
239
|
+
} else {
|
|
240
|
+
Lit::Float(f)
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
fn apat(p: &mut P) -> Result<RawPat> {
|
|
245
|
+
let tok = match p.next() {
|
|
246
|
+
Some(t) => t,
|
|
247
|
+
None => return Err(p.err("expected a pattern but reached end of input".into())),
|
|
248
|
+
};
|
|
249
|
+
match tok {
|
|
250
|
+
Tok::Underscore => Ok(RawPat::Wild),
|
|
251
|
+
Tok::VarId(v) => {
|
|
252
|
+
if p.peek() == Some(&Tok::At) {
|
|
253
|
+
p.next();
|
|
254
|
+
if !starts_apat(p.peek()) {
|
|
255
|
+
return Err(p.err(format!("expected a pattern after '{}@'", v)));
|
|
256
|
+
}
|
|
257
|
+
let inner = apat(p)?;
|
|
258
|
+
Ok(RawPat::As(v, Box::new(inner)))
|
|
259
|
+
} else {
|
|
260
|
+
Ok(RawPat::Var(v))
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
Tok::Tilde => {
|
|
264
|
+
if !starts_apat(p.peek()) {
|
|
265
|
+
return Err(p.err("expected a pattern after '~'".into()));
|
|
266
|
+
}
|
|
267
|
+
Ok(RawPat::Lazy(Box::new(apat(p)?)))
|
|
268
|
+
}
|
|
269
|
+
Tok::Bang => {
|
|
270
|
+
if !starts_apat(p.peek()) {
|
|
271
|
+
return Err(p.err("expected a pattern after '!'".into()));
|
|
272
|
+
}
|
|
273
|
+
Ok(RawPat::Bang(Box::new(apat(p)?)))
|
|
274
|
+
}
|
|
275
|
+
Tok::Minus => negative_literal(p),
|
|
276
|
+
Tok::ConId(name) => {
|
|
277
|
+
if p.peek() == Some(&Tok::LBrace) {
|
|
278
|
+
p.next();
|
|
279
|
+
record_fields(p, name)
|
|
280
|
+
} else {
|
|
281
|
+
Ok(RawPat::Con(name, vec![]))
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
Tok::Int(s) => Ok(RawPat::Lit(int_lit(&s))),
|
|
285
|
+
Tok::Float(s) => {
|
|
286
|
+
let f: f64 = s
|
|
287
|
+
.parse()
|
|
288
|
+
.map_err(|_| p.err(format!("malformed float literal {}", s)))?;
|
|
289
|
+
Ok(RawPat::Lit(float_lit(f)))
|
|
290
|
+
}
|
|
291
|
+
// `"abc"` is `['a', 'b', 'c']`, as `String = [Char]` in Haskell
|
|
292
|
+
Tok::Str(s) => Ok(RawPat::List(
|
|
293
|
+
s.chars().map(|c| RawPat::Lit(Lit::Char(c))).collect(),
|
|
294
|
+
)),
|
|
295
|
+
Tok::Char(s) => Ok(RawPat::Lit(Lit::Char(
|
|
296
|
+
s.chars().next().expect("lexer checked length"),
|
|
297
|
+
))),
|
|
298
|
+
Tok::Sym(s) => Ok(RawPat::Lit(Lit::Sym(s))),
|
|
299
|
+
Tok::LBrace => hash_fields(p),
|
|
300
|
+
Tok::LParen => {
|
|
301
|
+
if p.peek() == Some(&Tok::RParen) {
|
|
302
|
+
p.next();
|
|
303
|
+
return Ok(RawPat::Tuple(vec![]));
|
|
304
|
+
}
|
|
305
|
+
let first = pattern(p)?;
|
|
306
|
+
if p.peek() == Some(&Tok::Comma) {
|
|
307
|
+
let mut items = vec![first];
|
|
308
|
+
while p.peek() == Some(&Tok::Comma) {
|
|
309
|
+
p.next();
|
|
310
|
+
items.push(pattern(p)?);
|
|
311
|
+
}
|
|
312
|
+
p.expect(Tok::RParen, "')' to close tuple pattern")?;
|
|
313
|
+
Ok(RawPat::Tuple(items))
|
|
314
|
+
} else {
|
|
315
|
+
p.expect(Tok::RParen, "')'")?;
|
|
316
|
+
Ok(first)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
Tok::LBracket => {
|
|
320
|
+
let mut items = Vec::new();
|
|
321
|
+
if p.peek() == Some(&Tok::RBracket) {
|
|
322
|
+
p.next();
|
|
323
|
+
return Ok(RawPat::List(items));
|
|
324
|
+
}
|
|
325
|
+
loop {
|
|
326
|
+
items.push(pattern(p)?);
|
|
327
|
+
match p.next() {
|
|
328
|
+
Some(Tok::Comma) => continue,
|
|
329
|
+
Some(Tok::RBracket) => break,
|
|
330
|
+
Some(t) => {
|
|
331
|
+
return Err(p.err(format!(
|
|
332
|
+
"expected ',' or ']' in list pattern but found {}",
|
|
333
|
+
describe(&t)
|
|
334
|
+
)))
|
|
335
|
+
}
|
|
336
|
+
None => return Err(p.err("unterminated list pattern".into())),
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
Ok(RawPat::List(items))
|
|
340
|
+
}
|
|
341
|
+
other => Err(p.err(format!("unexpected {} in pattern", describe(&other)))),
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/// `{ key = pat, "str" = pat, .. }` after the opening brace: a Hash pattern.
|
|
346
|
+
/// Keys are bare lower-case names or `:symbols` (Symbol keys) or string
|
|
347
|
+
/// literals (String keys); `..` is accepted (Hash patterns are always open).
|
|
348
|
+
fn hash_fields(p: &mut P) -> Result<RawPat> {
|
|
349
|
+
let mut fields: Vec<(HKey, RawPat)> = Vec::new();
|
|
350
|
+
if p.peek() == Some(&Tok::RBrace) {
|
|
351
|
+
p.next();
|
|
352
|
+
return Ok(RawPat::Hash(fields));
|
|
353
|
+
}
|
|
354
|
+
loop {
|
|
355
|
+
let key = match p.next() {
|
|
356
|
+
Some(Tok::VarId(k)) => HKey::Sym(k),
|
|
357
|
+
Some(Tok::Sym(k)) => HKey::Sym(k),
|
|
358
|
+
Some(Tok::Str(k)) => HKey::Str(k),
|
|
359
|
+
Some(Tok::DotDot) => {
|
|
360
|
+
p.expect(Tok::RBrace, "'}' after '..' in Hash pattern")?;
|
|
361
|
+
return Ok(RawPat::Hash(fields));
|
|
362
|
+
}
|
|
363
|
+
Some(t) => {
|
|
364
|
+
return Err(p.err(format!(
|
|
365
|
+
"expected a key (name, :symbol or \"string\") in Hash pattern but found {}",
|
|
366
|
+
describe(&t)
|
|
367
|
+
)))
|
|
368
|
+
}
|
|
369
|
+
None => return Err(p.err("unterminated Hash pattern".into())),
|
|
370
|
+
};
|
|
371
|
+
if fields.iter().any(|(k, _)| *k == key) {
|
|
372
|
+
return Err(CoreError::new(
|
|
373
|
+
ErrorKind::Field,
|
|
374
|
+
format!("key {} appears twice in a Hash pattern", key),
|
|
375
|
+
));
|
|
376
|
+
}
|
|
377
|
+
p.expect(Tok::Equals, "'=' after a Hash pattern key")?;
|
|
378
|
+
let pat = pattern(p)?;
|
|
379
|
+
fields.push((key, pat));
|
|
380
|
+
match p.next() {
|
|
381
|
+
Some(Tok::Comma) => continue,
|
|
382
|
+
Some(Tok::RBrace) => break,
|
|
383
|
+
Some(t) => {
|
|
384
|
+
return Err(p.err(format!(
|
|
385
|
+
"expected ',' or '}}' in Hash pattern but found {}",
|
|
386
|
+
describe(&t)
|
|
387
|
+
)))
|
|
388
|
+
}
|
|
389
|
+
None => return Err(p.err("unterminated Hash pattern".into())),
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
Ok(RawPat::Hash(fields))
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
fn record_fields(p: &mut P, con: String) -> Result<RawPat> {
|
|
396
|
+
// '{' already consumed
|
|
397
|
+
let mut fields: Vec<(String, RawPat)> = Vec::new();
|
|
398
|
+
let mut wildcard = false;
|
|
399
|
+
if p.peek() == Some(&Tok::RBrace) {
|
|
400
|
+
p.next();
|
|
401
|
+
return Ok(RawPat::Record(con, fields, false));
|
|
402
|
+
}
|
|
403
|
+
loop {
|
|
404
|
+
match p.next() {
|
|
405
|
+
Some(Tok::DotDot) => {
|
|
406
|
+
wildcard = true;
|
|
407
|
+
match p.next() {
|
|
408
|
+
Some(Tok::RBrace) => break,
|
|
409
|
+
_ => return Err(p.err("'..' must be the last item in a record pattern".into())),
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
Some(Tok::VarId(f)) => {
|
|
413
|
+
if fields.iter().any(|(g, _)| *g == f) {
|
|
414
|
+
return Err(CoreError::new(
|
|
415
|
+
ErrorKind::Field,
|
|
416
|
+
format!("duplicate field '{}' in record pattern for '{}'", f, con),
|
|
417
|
+
));
|
|
418
|
+
}
|
|
419
|
+
if p.peek() == Some(&Tok::Equals) {
|
|
420
|
+
p.next();
|
|
421
|
+
let pat = pattern(p)?;
|
|
422
|
+
fields.push((f, pat));
|
|
423
|
+
} else {
|
|
424
|
+
// NamedFieldPuns: `Con { field }` binds `field`
|
|
425
|
+
let v = f.clone();
|
|
426
|
+
fields.push((f, RawPat::Var(v)));
|
|
427
|
+
}
|
|
428
|
+
match p.next() {
|
|
429
|
+
Some(Tok::Comma) => continue,
|
|
430
|
+
Some(Tok::RBrace) => break,
|
|
431
|
+
Some(t) => {
|
|
432
|
+
return Err(p.err(format!(
|
|
433
|
+
"expected ',' or '}}' in record pattern but found {}",
|
|
434
|
+
describe(&t)
|
|
435
|
+
)))
|
|
436
|
+
}
|
|
437
|
+
None => return Err(p.err("unterminated record pattern".into())),
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
Some(t) => {
|
|
441
|
+
return Err(p.err(format!(
|
|
442
|
+
"expected a field name in record pattern for '{}' but found {}",
|
|
443
|
+
con,
|
|
444
|
+
describe(&t)
|
|
445
|
+
)))
|
|
446
|
+
}
|
|
447
|
+
None => return Err(p.err("unterminated record pattern".into())),
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
Ok(RawPat::Record(con, fields, wildcard))
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// ---------------------------------------------------------------------------
|
|
454
|
+
// data declarations
|
|
455
|
+
// ---------------------------------------------------------------------------
|
|
456
|
+
|
|
457
|
+
#[derive(Clone, Debug, PartialEq)]
|
|
458
|
+
pub struct ConDecl {
|
|
459
|
+
pub name: String,
|
|
460
|
+
pub arity: usize,
|
|
461
|
+
/// Field names for record syntax; `None` for positional constructors.
|
|
462
|
+
pub fields: Option<Vec<String>>,
|
|
463
|
+
/// The declared type of each field, as written (one per field).
|
|
464
|
+
pub types: Vec<String>,
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
#[derive(Clone, Debug, PartialEq)]
|
|
468
|
+
pub struct DataDecl {
|
|
469
|
+
pub name: String,
|
|
470
|
+
pub tyvars: Vec<String>,
|
|
471
|
+
pub cons: Vec<ConDecl>,
|
|
472
|
+
/// Classes named in the `deriving` clause.
|
|
473
|
+
pub deriving: Vec<String>,
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
fn derr(p: &P, msg: String) -> CoreError {
|
|
477
|
+
CoreError::new(
|
|
478
|
+
ErrorKind::DataDeclaration,
|
|
479
|
+
format!("{} (column {} in {:?})", msg, p.pos(), p.src),
|
|
480
|
+
)
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/// Parse `data T a b = C1 a | C2 { f :: b } | C3 deriving (...)`.
|
|
484
|
+
/// The leading `data`/`newtype` keyword and the `deriving` clause are optional.
|
|
485
|
+
pub fn parse_data(src: &str) -> Result<DataDecl> {
|
|
486
|
+
let toks = tokenize(src).map_err(|e| CoreError::new(ErrorKind::DataDeclaration, e.message))?;
|
|
487
|
+
let mut p = P {
|
|
488
|
+
toks,
|
|
489
|
+
i: 0,
|
|
490
|
+
src: src.to_string(),
|
|
491
|
+
};
|
|
492
|
+
if matches!(p.peek(), Some(Tok::VarId(k)) if k == "data" || k == "newtype") {
|
|
493
|
+
p.next();
|
|
494
|
+
}
|
|
495
|
+
let name = match p.next() {
|
|
496
|
+
Some(Tok::ConId(n)) => n,
|
|
497
|
+
Some(t) => {
|
|
498
|
+
return Err(derr(
|
|
499
|
+
&p,
|
|
500
|
+
format!("expected a type name but found {}", describe(&t)),
|
|
501
|
+
))
|
|
502
|
+
}
|
|
503
|
+
None => return Err(derr(&p, "empty data declaration".into())),
|
|
504
|
+
};
|
|
505
|
+
let mut tyvars = Vec::new();
|
|
506
|
+
while let Some(Tok::VarId(v)) = p.peek() {
|
|
507
|
+
let v = v.clone();
|
|
508
|
+
p.next();
|
|
509
|
+
tyvars.push(v);
|
|
510
|
+
}
|
|
511
|
+
match p.next() {
|
|
512
|
+
Some(Tok::Equals) => {}
|
|
513
|
+
Some(t) => {
|
|
514
|
+
return Err(derr(
|
|
515
|
+
&p,
|
|
516
|
+
format!("expected '=' after type name but found {}", describe(&t)),
|
|
517
|
+
))
|
|
518
|
+
}
|
|
519
|
+
None => {
|
|
520
|
+
return Err(derr(
|
|
521
|
+
&p,
|
|
522
|
+
format!("type '{}' has no constructors (expected '=')", name),
|
|
523
|
+
))
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
let mut cons = Vec::new();
|
|
527
|
+
let mut deriving = Vec::new();
|
|
528
|
+
loop {
|
|
529
|
+
cons.push(con_decl(&mut p)?);
|
|
530
|
+
match p.peek() {
|
|
531
|
+
Some(Tok::Pipe) => {
|
|
532
|
+
p.next();
|
|
533
|
+
}
|
|
534
|
+
Some(Tok::VarId(k)) if k == "deriving" => {
|
|
535
|
+
p.next();
|
|
536
|
+
deriving = deriving_clause(&mut p)?;
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
None => break,
|
|
540
|
+
Some(t) => {
|
|
541
|
+
return Err(derr(
|
|
542
|
+
&p,
|
|
543
|
+
format!("unexpected {} in data declaration", describe(t)),
|
|
544
|
+
));
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
// duplicate constructor names
|
|
549
|
+
for (i, c) in cons.iter().enumerate() {
|
|
550
|
+
if cons[..i].iter().any(|d| d.name == c.name) {
|
|
551
|
+
return Err(CoreError::new(
|
|
552
|
+
ErrorKind::DataDeclaration,
|
|
553
|
+
format!(
|
|
554
|
+
"constructor '{}' is declared twice in type '{}'",
|
|
555
|
+
c.name, name
|
|
556
|
+
),
|
|
557
|
+
));
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
Ok(DataDecl {
|
|
561
|
+
name,
|
|
562
|
+
tyvars,
|
|
563
|
+
cons,
|
|
564
|
+
deriving,
|
|
565
|
+
})
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
fn con_decl(p: &mut P) -> Result<ConDecl> {
|
|
569
|
+
// infix constructor: `atype :op: atype`, e.g. `Double :+: Double`
|
|
570
|
+
let infix = match p.peek() {
|
|
571
|
+
Some(Tok::ConId(_)) => matches!(p.peek_at(1), Some(Tok::ConSym(_))),
|
|
572
|
+
Some(Tok::LParen) if matches!(p.peek_at(1), Some(Tok::ConSym(_))) => false,
|
|
573
|
+
Some(t) if starts_atype(Some(t)) => true,
|
|
574
|
+
_ => false,
|
|
575
|
+
};
|
|
576
|
+
if infix {
|
|
577
|
+
let from = p.i;
|
|
578
|
+
atype(p)?;
|
|
579
|
+
let left = span_text(p, from);
|
|
580
|
+
let name = match p.next() {
|
|
581
|
+
Some(Tok::ConSym(op)) => op,
|
|
582
|
+
Some(t) => {
|
|
583
|
+
return Err(derr(
|
|
584
|
+
p,
|
|
585
|
+
format!("expected an infix constructor but found {}", describe(&t)),
|
|
586
|
+
))
|
|
587
|
+
}
|
|
588
|
+
None => return Err(derr(p, "expected an infix constructor".into())),
|
|
589
|
+
};
|
|
590
|
+
let from = p.i;
|
|
591
|
+
atype(p)?;
|
|
592
|
+
let right = span_text(p, from);
|
|
593
|
+
return Ok(ConDecl {
|
|
594
|
+
name,
|
|
595
|
+
arity: 2,
|
|
596
|
+
fields: None,
|
|
597
|
+
types: vec![left, right],
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
let name = match p.next() {
|
|
601
|
+
Some(Tok::ConId(n)) => n,
|
|
602
|
+
// `(:+:) a b`: an infix constructor declared in prefix form
|
|
603
|
+
Some(Tok::LParen) => {
|
|
604
|
+
let op = match p.next() {
|
|
605
|
+
Some(Tok::ConSym(op)) => op,
|
|
606
|
+
Some(t) => {
|
|
607
|
+
return Err(derr(
|
|
608
|
+
p,
|
|
609
|
+
format!("expected a constructor operator but found {}", describe(&t)),
|
|
610
|
+
))
|
|
611
|
+
}
|
|
612
|
+
None => return Err(derr(p, "expected a constructor operator".into())),
|
|
613
|
+
};
|
|
614
|
+
match p.next() {
|
|
615
|
+
Some(Tok::RParen) => op,
|
|
616
|
+
_ => return Err(derr(p, "expected ')' after constructor operator".into())),
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
Some(t) => {
|
|
620
|
+
return Err(derr(
|
|
621
|
+
p,
|
|
622
|
+
format!("expected a constructor name but found {}", describe(&t)),
|
|
623
|
+
))
|
|
624
|
+
}
|
|
625
|
+
None => return Err(derr(p, "expected a constructor name".into())),
|
|
626
|
+
};
|
|
627
|
+
if p.peek() == Some(&Tok::LBrace) {
|
|
628
|
+
p.next();
|
|
629
|
+
let mut fields: Vec<String> = Vec::new();
|
|
630
|
+
if p.peek() == Some(&Tok::RBrace) {
|
|
631
|
+
p.next();
|
|
632
|
+
return Ok(ConDecl {
|
|
633
|
+
name,
|
|
634
|
+
arity: 0,
|
|
635
|
+
fields: Some(fields),
|
|
636
|
+
types: Vec::new(),
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
let mut types: Vec<String> = Vec::new();
|
|
640
|
+
loop {
|
|
641
|
+
// f1, f2 :: Type
|
|
642
|
+
let mut names = Vec::new();
|
|
643
|
+
loop {
|
|
644
|
+
match p.next() {
|
|
645
|
+
Some(Tok::VarId(f)) => names.push(f),
|
|
646
|
+
Some(t) => {
|
|
647
|
+
return Err(derr(
|
|
648
|
+
p,
|
|
649
|
+
format!("expected a field name but found {}", describe(&t)),
|
|
650
|
+
))
|
|
651
|
+
}
|
|
652
|
+
None => return Err(derr(p, "unterminated record declaration".into())),
|
|
653
|
+
}
|
|
654
|
+
match p.next() {
|
|
655
|
+
Some(Tok::Comma) => continue,
|
|
656
|
+
Some(Tok::DoubleColon) => break,
|
|
657
|
+
Some(t) => {
|
|
658
|
+
return Err(derr(
|
|
659
|
+
p,
|
|
660
|
+
format!("expected '::' after field name but found {}", describe(&t)),
|
|
661
|
+
))
|
|
662
|
+
}
|
|
663
|
+
None => return Err(derr(p, "unterminated record declaration".into())),
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
let from = p.i;
|
|
667
|
+
type_expr(p)?;
|
|
668
|
+
let ty_text = span_text(p, from);
|
|
669
|
+
for f in names {
|
|
670
|
+
types.push(ty_text.clone());
|
|
671
|
+
if fields.contains(&f) {
|
|
672
|
+
return Err(CoreError::new(
|
|
673
|
+
ErrorKind::DataDeclaration,
|
|
674
|
+
format!("field '{}' is declared twice in constructor '{}'", f, name),
|
|
675
|
+
));
|
|
676
|
+
}
|
|
677
|
+
fields.push(f);
|
|
678
|
+
}
|
|
679
|
+
match p.next() {
|
|
680
|
+
Some(Tok::Comma) => continue,
|
|
681
|
+
Some(Tok::RBrace) => break,
|
|
682
|
+
Some(t) => {
|
|
683
|
+
return Err(derr(
|
|
684
|
+
p,
|
|
685
|
+
format!("expected ',' or '}}' but found {}", describe(&t)),
|
|
686
|
+
))
|
|
687
|
+
}
|
|
688
|
+
None => return Err(derr(p, "unterminated record declaration".into())),
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
let arity = fields.len();
|
|
692
|
+
return Ok(ConDecl {
|
|
693
|
+
name,
|
|
694
|
+
arity,
|
|
695
|
+
fields: Some(fields),
|
|
696
|
+
types,
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
let mut arity = 0;
|
|
700
|
+
let mut types = Vec::new();
|
|
701
|
+
while starts_atype(p.peek()) {
|
|
702
|
+
let from = p.i;
|
|
703
|
+
atype(p)?;
|
|
704
|
+
types.push(span_text(p, from));
|
|
705
|
+
arity += 1;
|
|
706
|
+
}
|
|
707
|
+
Ok(ConDecl {
|
|
708
|
+
name,
|
|
709
|
+
arity,
|
|
710
|
+
fields: None,
|
|
711
|
+
types,
|
|
712
|
+
})
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/// `deriving Show` or `deriving (Eq, Ord, Show)`.
|
|
716
|
+
fn deriving_clause(p: &mut P) -> Result<Vec<String>> {
|
|
717
|
+
let mut out = Vec::new();
|
|
718
|
+
match p.next() {
|
|
719
|
+
Some(Tok::ConId(c)) => out.push(c),
|
|
720
|
+
Some(Tok::LParen) => {
|
|
721
|
+
if p.peek() == Some(&Tok::RParen) {
|
|
722
|
+
p.next();
|
|
723
|
+
} else {
|
|
724
|
+
loop {
|
|
725
|
+
match p.next() {
|
|
726
|
+
Some(Tok::ConId(c)) => out.push(c),
|
|
727
|
+
Some(t) => {
|
|
728
|
+
return Err(derr(
|
|
729
|
+
p,
|
|
730
|
+
format!(
|
|
731
|
+
"expected a class name in deriving but found {}",
|
|
732
|
+
describe(&t)
|
|
733
|
+
),
|
|
734
|
+
))
|
|
735
|
+
}
|
|
736
|
+
None => return Err(derr(p, "unterminated deriving clause".into())),
|
|
737
|
+
}
|
|
738
|
+
match p.next() {
|
|
739
|
+
Some(Tok::Comma) => continue,
|
|
740
|
+
Some(Tok::RParen) => break,
|
|
741
|
+
Some(t) => {
|
|
742
|
+
return Err(derr(
|
|
743
|
+
p,
|
|
744
|
+
format!(
|
|
745
|
+
"expected ',' or ')' in deriving but found {}",
|
|
746
|
+
describe(&t)
|
|
747
|
+
),
|
|
748
|
+
))
|
|
749
|
+
}
|
|
750
|
+
None => return Err(derr(p, "unterminated deriving clause".into())),
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
Some(t) => {
|
|
756
|
+
return Err(derr(
|
|
757
|
+
p,
|
|
758
|
+
format!(
|
|
759
|
+
"expected a class name after deriving but found {}",
|
|
760
|
+
describe(&t)
|
|
761
|
+
),
|
|
762
|
+
))
|
|
763
|
+
}
|
|
764
|
+
None => return Err(derr(p, "expected a class name after deriving".into())),
|
|
765
|
+
}
|
|
766
|
+
if let Some(t) = p.peek() {
|
|
767
|
+
return Err(derr(
|
|
768
|
+
p,
|
|
769
|
+
format!("unexpected {} after deriving clause", describe(t)),
|
|
770
|
+
));
|
|
771
|
+
}
|
|
772
|
+
Ok(out)
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/// The source text spanned by tokens `from..p.i` (for field types).
|
|
776
|
+
fn span_text(p: &P, from: usize) -> String {
|
|
777
|
+
let start = p.toks[from].pos;
|
|
778
|
+
let end = p
|
|
779
|
+
.toks
|
|
780
|
+
.get(p.i)
|
|
781
|
+
.map(|t| t.pos)
|
|
782
|
+
.unwrap_or(p.src.chars().count());
|
|
783
|
+
p.src
|
|
784
|
+
.chars()
|
|
785
|
+
.skip(start)
|
|
786
|
+
.take(end - start)
|
|
787
|
+
.collect::<String>()
|
|
788
|
+
.trim()
|
|
789
|
+
.to_string()
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
fn starts_atype(t: Option<&Tok>) -> bool {
|
|
793
|
+
matches!(
|
|
794
|
+
t,
|
|
795
|
+
Some(Tok::ConId(_))
|
|
796
|
+
| Some(Tok::VarId(_))
|
|
797
|
+
| Some(Tok::LParen)
|
|
798
|
+
| Some(Tok::LBracket)
|
|
799
|
+
| Some(Tok::Bang)
|
|
800
|
+
) && !matches!(t, Some(Tok::VarId(k)) if k == "deriving")
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
fn type_expr(p: &mut P) -> Result<()> {
|
|
804
|
+
if !starts_atype(p.peek()) {
|
|
805
|
+
return Err(derr(p, "expected a type".into()));
|
|
806
|
+
}
|
|
807
|
+
while starts_atype(p.peek()) {
|
|
808
|
+
atype(p)?;
|
|
809
|
+
}
|
|
810
|
+
if p.peek() == Some(&Tok::Arrow) {
|
|
811
|
+
p.next();
|
|
812
|
+
type_expr(p)?;
|
|
813
|
+
}
|
|
814
|
+
Ok(())
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
fn atype(p: &mut P) -> Result<()> {
|
|
818
|
+
match p.next() {
|
|
819
|
+
Some(Tok::ConId(_)) | Some(Tok::VarId(_)) => Ok(()),
|
|
820
|
+
Some(Tok::Bang) => atype(p),
|
|
821
|
+
Some(Tok::LParen) => {
|
|
822
|
+
if p.peek() == Some(&Tok::RParen) {
|
|
823
|
+
p.next();
|
|
824
|
+
return Ok(());
|
|
825
|
+
}
|
|
826
|
+
type_expr(p)?;
|
|
827
|
+
while p.peek() == Some(&Tok::Comma) {
|
|
828
|
+
p.next();
|
|
829
|
+
type_expr(p)?;
|
|
830
|
+
}
|
|
831
|
+
match p.next() {
|
|
832
|
+
Some(Tok::RParen) => Ok(()),
|
|
833
|
+
_ => Err(derr(p, "expected ')' in type".into())),
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
Some(Tok::LBracket) => {
|
|
837
|
+
type_expr(p)?;
|
|
838
|
+
match p.next() {
|
|
839
|
+
Some(Tok::RBracket) => Ok(()),
|
|
840
|
+
_ => Err(derr(p, "expected ']' in type".into())),
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
Some(t) => Err(derr(p, format!("unexpected {} in type", describe(&t)))),
|
|
844
|
+
None => Err(derr(p, "unexpected end of type".into())),
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
#[cfg(test)]
|
|
849
|
+
mod tests {
|
|
850
|
+
use super::*;
|
|
851
|
+
use RawPat::*;
|
|
852
|
+
|
|
853
|
+
fn v(s: &str) -> RawPat {
|
|
854
|
+
Var(s.into())
|
|
855
|
+
}
|
|
856
|
+
fn con(s: &str, args: Vec<RawPat>) -> RawPat {
|
|
857
|
+
Con(s.into(), args)
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
#[test]
|
|
861
|
+
fn simple_patterns() {
|
|
862
|
+
assert_eq!(parse_pattern("_").unwrap(), Wild);
|
|
863
|
+
assert_eq!(parse_pattern("x").unwrap(), v("x"));
|
|
864
|
+
assert_eq!(parse_pattern("Nothing").unwrap(), con("Nothing", vec![]));
|
|
865
|
+
assert_eq!(parse_pattern("Just x").unwrap(), con("Just", vec![v("x")]));
|
|
866
|
+
assert_eq!(
|
|
867
|
+
parse_pattern("Just (Just x)").unwrap(),
|
|
868
|
+
con("Just", vec![con("Just", vec![v("x")])])
|
|
869
|
+
);
|
|
870
|
+
assert_eq!(
|
|
871
|
+
parse_pattern("Pair a Nothing").unwrap(),
|
|
872
|
+
con("Pair", vec![v("a"), con("Nothing", vec![])])
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
#[test]
|
|
877
|
+
fn lists_and_cons() {
|
|
878
|
+
assert_eq!(parse_pattern("[]").unwrap(), List(vec![]));
|
|
879
|
+
assert_eq!(parse_pattern("[a, b]").unwrap(), List(vec![v("a"), v("b")]));
|
|
880
|
+
assert_eq!(
|
|
881
|
+
parse_pattern("(x:xs)").unwrap(),
|
|
882
|
+
Cons(Box::new(v("x")), Box::new(v("xs")))
|
|
883
|
+
);
|
|
884
|
+
assert_eq!(
|
|
885
|
+
parse_pattern("x:y:rest").unwrap(),
|
|
886
|
+
Cons(
|
|
887
|
+
Box::new(v("x")),
|
|
888
|
+
Box::new(Cons(Box::new(v("y")), Box::new(v("rest"))))
|
|
889
|
+
)
|
|
890
|
+
);
|
|
891
|
+
assert_eq!(
|
|
892
|
+
parse_pattern("(Just x : xs)").unwrap(),
|
|
893
|
+
Cons(Box::new(con("Just", vec![v("x")])), Box::new(v("xs")))
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
#[test]
|
|
898
|
+
fn tuples_unit_as_lazy_bang() {
|
|
899
|
+
assert_eq!(parse_pattern("()").unwrap(), Tuple(vec![]));
|
|
900
|
+
assert_eq!(
|
|
901
|
+
parse_pattern("(a, b, _)").unwrap(),
|
|
902
|
+
Tuple(vec![v("a"), v("b"), Wild])
|
|
903
|
+
);
|
|
904
|
+
assert_eq!(parse_pattern("(a)").unwrap(), v("a"));
|
|
905
|
+
assert_eq!(
|
|
906
|
+
parse_pattern("all@(x:_)").unwrap(),
|
|
907
|
+
As(
|
|
908
|
+
"all".into(),
|
|
909
|
+
Box::new(Cons(Box::new(v("x")), Box::new(Wild)))
|
|
910
|
+
)
|
|
911
|
+
);
|
|
912
|
+
assert_eq!(
|
|
913
|
+
parse_pattern("~(a, b)").unwrap(),
|
|
914
|
+
Lazy(Box::new(Tuple(vec![v("a"), v("b")])))
|
|
915
|
+
);
|
|
916
|
+
assert_eq!(parse_pattern("!x").unwrap(), Bang(Box::new(v("x"))));
|
|
917
|
+
assert_eq!(
|
|
918
|
+
parse_pattern("Just !x").unwrap(),
|
|
919
|
+
con("Just", vec![Bang(Box::new(v("x")))])
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
#[test]
|
|
924
|
+
fn literals() {
|
|
925
|
+
assert_eq!(parse_pattern("0").unwrap(), Lit(super::Lit::Int(0)));
|
|
926
|
+
assert_eq!(parse_pattern("-1").unwrap(), Lit(super::Lit::Int(-1)));
|
|
927
|
+
assert_eq!(parse_pattern("(-1)").unwrap(), Lit(super::Lit::Int(-1)));
|
|
928
|
+
assert_eq!(
|
|
929
|
+
parse_pattern("Just (-2)").unwrap(),
|
|
930
|
+
con("Just", vec![Lit(super::Lit::Int(-2))])
|
|
931
|
+
);
|
|
932
|
+
assert_eq!(parse_pattern("1.5").unwrap(), Lit(super::Lit::Float(1.5)));
|
|
933
|
+
assert_eq!(parse_pattern("2.0").unwrap(), Lit(super::Lit::Int(2)));
|
|
934
|
+
assert_eq!(
|
|
935
|
+
parse_pattern("\"hi\"").unwrap(),
|
|
936
|
+
List(vec![Lit(super::Lit::Char('h')), Lit(super::Lit::Char('i'))])
|
|
937
|
+
);
|
|
938
|
+
assert_eq!(parse_pattern("\"\"").unwrap(), List(vec![]));
|
|
939
|
+
assert_eq!(parse_pattern("'c'").unwrap(), Lit(super::Lit::Char('c')));
|
|
940
|
+
assert_eq!(
|
|
941
|
+
parse_pattern(":ok").unwrap(),
|
|
942
|
+
Lit(super::Lit::Sym("ok".into()))
|
|
943
|
+
);
|
|
944
|
+
assert_eq!(
|
|
945
|
+
parse_pattern("123456789012345678901234567890").unwrap(),
|
|
946
|
+
Lit(super::Lit::Big("123456789012345678901234567890".into()))
|
|
947
|
+
);
|
|
948
|
+
assert_eq!(parse_pattern("0x10").unwrap(), Lit(super::Lit::Int(16)));
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
#[test]
|
|
952
|
+
fn records() {
|
|
953
|
+
assert_eq!(
|
|
954
|
+
parse_pattern("Person { name = n, age }").unwrap(),
|
|
955
|
+
Record(
|
|
956
|
+
"Person".into(),
|
|
957
|
+
vec![("name".into(), v("n")), ("age".into(), v("age"))],
|
|
958
|
+
false
|
|
959
|
+
)
|
|
960
|
+
);
|
|
961
|
+
assert_eq!(
|
|
962
|
+
parse_pattern("Person {}").unwrap(),
|
|
963
|
+
Record("Person".into(), vec![], false)
|
|
964
|
+
);
|
|
965
|
+
assert_eq!(
|
|
966
|
+
parse_pattern("Person { name = _, .. }").unwrap(),
|
|
967
|
+
Record("Person".into(), vec![("name".into(), Wild)], true)
|
|
968
|
+
);
|
|
969
|
+
assert_eq!(
|
|
970
|
+
parse_pattern("Just Person { .. }").unwrap(),
|
|
971
|
+
con("Just", vec![Record("Person".into(), vec![], true)])
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
#[test]
|
|
976
|
+
fn syntax_errors() {
|
|
977
|
+
for bad in [
|
|
978
|
+
"",
|
|
979
|
+
"(",
|
|
980
|
+
")",
|
|
981
|
+
"Just (",
|
|
982
|
+
"[a,",
|
|
983
|
+
"x@",
|
|
984
|
+
"(a,",
|
|
985
|
+
"Just x)",
|
|
986
|
+
"a b",
|
|
987
|
+
"~",
|
|
988
|
+
"P { name }}",
|
|
989
|
+
"P { .. , x }",
|
|
990
|
+
"P { = x }",
|
|
991
|
+
"-",
|
|
992
|
+
"-x",
|
|
993
|
+
"x :",
|
|
994
|
+
"1 2",
|
|
995
|
+
] {
|
|
996
|
+
let r = parse_pattern(bad);
|
|
997
|
+
assert!(r.is_err(), "expected error for {:?}, got {:?}", bad, r);
|
|
998
|
+
assert_eq!(r.unwrap_err().kind, ErrorKind::Syntax, "kind for {:?}", bad);
|
|
999
|
+
}
|
|
1000
|
+
assert_eq!(
|
|
1001
|
+
parse_pattern("P { a = 1, a = 2 }").unwrap_err().kind,
|
|
1002
|
+
ErrorKind::Field
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
#[test]
|
|
1007
|
+
fn infix_constructor_patterns_and_decls() {
|
|
1008
|
+
assert_eq!(
|
|
1009
|
+
parse_pattern("a :+: b").unwrap(),
|
|
1010
|
+
con(":+:", vec![v("a"), v("b")])
|
|
1011
|
+
);
|
|
1012
|
+
assert_eq!(
|
|
1013
|
+
parse_pattern("(:+:) a b").unwrap(),
|
|
1014
|
+
con(":+:", vec![v("a"), v("b")])
|
|
1015
|
+
);
|
|
1016
|
+
assert_eq!(
|
|
1017
|
+
parse_pattern("Just (x :| xs)").unwrap(),
|
|
1018
|
+
con("Just", vec![con(":|", vec![v("x"), v("xs")])])
|
|
1019
|
+
);
|
|
1020
|
+
let d = parse_data("C = Double :+: Double | (:*:) Int Int | Plain").unwrap();
|
|
1021
|
+
assert_eq!(d.cons[0].name, ":+:");
|
|
1022
|
+
assert_eq!(
|
|
1023
|
+
d.cons[0].types,
|
|
1024
|
+
vec!["Double".to_string(), "Double".to_string()]
|
|
1025
|
+
);
|
|
1026
|
+
assert_eq!(d.cons[1].name, ":*:");
|
|
1027
|
+
assert_eq!(d.cons[1].arity, 2);
|
|
1028
|
+
assert_eq!(d.cons[2].arity, 0);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
#[test]
|
|
1032
|
+
fn data_declarations() {
|
|
1033
|
+
let d = parse_data("data Maybe a = Nothing | Just a").unwrap();
|
|
1034
|
+
assert_eq!(d.name, "Maybe");
|
|
1035
|
+
assert_eq!(d.tyvars, vec!["a".to_string()]);
|
|
1036
|
+
assert_eq!(
|
|
1037
|
+
d.cons,
|
|
1038
|
+
vec![
|
|
1039
|
+
ConDecl {
|
|
1040
|
+
name: "Nothing".into(),
|
|
1041
|
+
arity: 0,
|
|
1042
|
+
fields: None,
|
|
1043
|
+
types: vec![],
|
|
1044
|
+
},
|
|
1045
|
+
ConDecl {
|
|
1046
|
+
name: "Just".into(),
|
|
1047
|
+
arity: 1,
|
|
1048
|
+
fields: None,
|
|
1049
|
+
types: vec!["a".into()],
|
|
1050
|
+
},
|
|
1051
|
+
]
|
|
1052
|
+
);
|
|
1053
|
+
assert!(d.deriving.is_empty());
|
|
1054
|
+
|
|
1055
|
+
let d = parse_data("Shape = Circle Double | Rect Double Double | Poly [(Double, Double)] deriving (Show, Eq)").unwrap();
|
|
1056
|
+
assert_eq!(
|
|
1057
|
+
d.cons.iter().map(|c| c.arity).collect::<Vec<_>>(),
|
|
1058
|
+
vec![1, 2, 1]
|
|
1059
|
+
);
|
|
1060
|
+
assert_eq!(d.cons[2].types, vec!["[(Double, Double)]".to_string()]);
|
|
1061
|
+
assert_eq!(d.deriving, vec!["Show".to_string(), "Eq".to_string()]);
|
|
1062
|
+
let d = parse_data("P = P { name :: String, age, score :: Int } deriving Ord").unwrap();
|
|
1063
|
+
assert_eq!(
|
|
1064
|
+
d.cons[0].types,
|
|
1065
|
+
vec!["String".to_string(), "Int".to_string(), "Int".to_string()]
|
|
1066
|
+
);
|
|
1067
|
+
assert_eq!(d.deriving, vec!["Ord".to_string()]);
|
|
1068
|
+
assert!(parse_data("P = P deriving (Eq").is_err());
|
|
1069
|
+
|
|
1070
|
+
let d = parse_data("Tree a = Leaf | Node (Tree a) a (Tree a)").unwrap();
|
|
1071
|
+
assert_eq!(d.cons[1].arity, 3);
|
|
1072
|
+
|
|
1073
|
+
let d =
|
|
1074
|
+
parse_data("data Person = Person { name :: String, age, shoe :: Int } | Anon").unwrap();
|
|
1075
|
+
assert_eq!(
|
|
1076
|
+
d.cons[0].fields,
|
|
1077
|
+
Some(vec!["name".into(), "age".into(), "shoe".into()])
|
|
1078
|
+
);
|
|
1079
|
+
assert_eq!(d.cons[0].arity, 3);
|
|
1080
|
+
assert_eq!(d.cons[1].arity, 0);
|
|
1081
|
+
|
|
1082
|
+
let d = parse_data("newtype Wrap = Wrap { unwrap :: Int -> Int }").unwrap();
|
|
1083
|
+
assert_eq!(d.cons[0].arity, 1);
|
|
1084
|
+
|
|
1085
|
+
let d = parse_data("data Strict = S !Int {-# UNPACK #-} !Double").unwrap();
|
|
1086
|
+
assert_eq!(d.cons[0].arity, 2);
|
|
1087
|
+
|
|
1088
|
+
let d = parse_data("Unit = Unit ()").unwrap();
|
|
1089
|
+
assert_eq!(d.cons[0].arity, 1);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
#[test]
|
|
1093
|
+
fn data_errors() {
|
|
1094
|
+
for bad in [
|
|
1095
|
+
"",
|
|
1096
|
+
"data",
|
|
1097
|
+
"Maybe a",
|
|
1098
|
+
"Maybe = ",
|
|
1099
|
+
"Maybe = Just a |",
|
|
1100
|
+
"maybe = X",
|
|
1101
|
+
"T = A | A",
|
|
1102
|
+
"T = R { a :: Int, a :: Int }",
|
|
1103
|
+
"T = A ) B",
|
|
1104
|
+
"T = R { a Int }",
|
|
1105
|
+
] {
|
|
1106
|
+
let r = parse_data(bad);
|
|
1107
|
+
assert!(r.is_err(), "expected error for {:?}, got {:?}", bad, r);
|
|
1108
|
+
assert_eq!(
|
|
1109
|
+
r.unwrap_err().kind,
|
|
1110
|
+
ErrorKind::DataDeclaration,
|
|
1111
|
+
"kind for {:?}",
|
|
1112
|
+
bad
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|