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,1945 @@
|
|
|
1
|
+
//! Parser for the Haskell subset: declarations (data, signatures, function
|
|
2
|
+
//! equations with guards and `where`, pattern bindings) and expressions
|
|
3
|
+
//! (application, infix operators with Prelude fixities, sections, lambdas,
|
|
4
|
+
//! `if`, `case`, `let`, lists, ranges, comprehensions, tuples).
|
|
5
|
+
|
|
6
|
+
use super::ast::*;
|
|
7
|
+
use super::layout::layout;
|
|
8
|
+
use super::lexer::{tokenize, Tok, Token};
|
|
9
|
+
use crate::core::ast::{Lit, RawPat};
|
|
10
|
+
use crate::core::error::{CoreError, ErrorKind, Result};
|
|
11
|
+
use crate::core::parser::{ConDecl, DataDecl};
|
|
12
|
+
use std::collections::HashMap;
|
|
13
|
+
|
|
14
|
+
struct P {
|
|
15
|
+
toks: Vec<Token>,
|
|
16
|
+
i: usize,
|
|
17
|
+
/// Operator fixities declared in the module (`infixl 6 <+>`).
|
|
18
|
+
fixities: HashMap<String, (u8, Assoc)>,
|
|
19
|
+
/// Counter for generated names (`\case` parameters, tuple sections).
|
|
20
|
+
gensym: usize,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#[derive(Clone, Copy, PartialEq)]
|
|
24
|
+
enum Assoc {
|
|
25
|
+
Left,
|
|
26
|
+
Right,
|
|
27
|
+
None,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fn default_fixity(op: &str) -> (u8, Assoc) {
|
|
31
|
+
match op {
|
|
32
|
+
"." => (9, Assoc::Right),
|
|
33
|
+
"!!" => (9, Assoc::Left),
|
|
34
|
+
"^" | "^^" | "**" => (8, Assoc::Right),
|
|
35
|
+
"*" | "/" | "div" | "mod" | "rem" | "quot" => (7, Assoc::Left),
|
|
36
|
+
"+" | "-" => (6, Assoc::Left),
|
|
37
|
+
":" | "++" => (5, Assoc::Right),
|
|
38
|
+
"==" | "/=" | "<" | "<=" | ">" | ">=" | "elem" | "notElem" => (4, Assoc::None),
|
|
39
|
+
"&&" => (3, Assoc::Right),
|
|
40
|
+
"||" => (2, Assoc::Right),
|
|
41
|
+
">>" | ">>=" => (1, Assoc::Left),
|
|
42
|
+
"$" | "$!" | "seq" => (0, Assoc::Right),
|
|
43
|
+
_ => (9, Assoc::Left),
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
impl P {
|
|
48
|
+
fn fixity(&self, op: &str) -> (u8, Assoc) {
|
|
49
|
+
self.fixities
|
|
50
|
+
.get(op)
|
|
51
|
+
.copied()
|
|
52
|
+
.unwrap_or_else(|| default_fixity(op))
|
|
53
|
+
}
|
|
54
|
+
fn fresh(&mut self, prefix: &str) -> String {
|
|
55
|
+
self.gensym += 1;
|
|
56
|
+
format!("{}__{}", prefix, self.gensym)
|
|
57
|
+
}
|
|
58
|
+
fn peek(&self) -> &Tok {
|
|
59
|
+
&self.toks[self.i].tok
|
|
60
|
+
}
|
|
61
|
+
fn peek_at(&self, n: usize) -> &Tok {
|
|
62
|
+
let j = (self.i + n).min(self.toks.len() - 1);
|
|
63
|
+
&self.toks[j].tok
|
|
64
|
+
}
|
|
65
|
+
fn here(&self) -> (usize, usize) {
|
|
66
|
+
let t = &self.toks[self.i];
|
|
67
|
+
(t.line, t.col)
|
|
68
|
+
}
|
|
69
|
+
fn next(&mut self) -> Tok {
|
|
70
|
+
let t = self.toks[self.i].tok.clone();
|
|
71
|
+
if self.i < self.toks.len() - 1 {
|
|
72
|
+
self.i += 1;
|
|
73
|
+
}
|
|
74
|
+
t
|
|
75
|
+
}
|
|
76
|
+
fn err<T>(&self, msg: impl std::fmt::Display) -> Result<T> {
|
|
77
|
+
let (l, c) = self.here();
|
|
78
|
+
Err(CoreError::new(
|
|
79
|
+
ErrorKind::Syntax,
|
|
80
|
+
format!("{}:{}: {}", l, c, msg),
|
|
81
|
+
))
|
|
82
|
+
}
|
|
83
|
+
fn expect(&mut self, want: Tok, what: &str) -> Result<()> {
|
|
84
|
+
if *self.peek() == want {
|
|
85
|
+
self.next();
|
|
86
|
+
Ok(())
|
|
87
|
+
} else {
|
|
88
|
+
self.err(format!(
|
|
89
|
+
"expected {} but found {}",
|
|
90
|
+
what,
|
|
91
|
+
describe(self.peek())
|
|
92
|
+
))
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
fn at(&self, t: Tok) -> bool {
|
|
96
|
+
*self.peek() == t
|
|
97
|
+
}
|
|
98
|
+
fn is_open(&self) -> bool {
|
|
99
|
+
matches!(self.peek(), Tok::LBrace | Tok::VLBrace)
|
|
100
|
+
}
|
|
101
|
+
fn is_close(&self) -> bool {
|
|
102
|
+
matches!(self.peek(), Tok::RBrace | Tok::VRBrace)
|
|
103
|
+
}
|
|
104
|
+
fn is_semi(&self) -> bool {
|
|
105
|
+
matches!(self.peek(), Tok::Semi | Tok::VSemi)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
pub fn describe(t: &Tok) -> String {
|
|
110
|
+
match t {
|
|
111
|
+
Tok::VarId(v) => format!("'{}'", v),
|
|
112
|
+
Tok::ConId(c) => format!("'{}'", c),
|
|
113
|
+
Tok::VarSym(s) | Tok::ConSym(s) => format!("operator '{}'", s),
|
|
114
|
+
Tok::Int(s) | Tok::Float(s) => format!("literal {}", s),
|
|
115
|
+
Tok::Char(c) => format!("literal {:?}", c),
|
|
116
|
+
Tok::Str(s) => format!("literal {:?}", s),
|
|
117
|
+
Tok::VLBrace => "start of block".into(),
|
|
118
|
+
Tok::VRBrace => "end of block".into(),
|
|
119
|
+
Tok::VSemi => "end of line".into(),
|
|
120
|
+
Tok::Eof => "end of input".into(),
|
|
121
|
+
other => format!("{:?}", other).to_lowercase(),
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/// `Data.Map.Map` -> `Map`: a qualified constructor without its module.
|
|
126
|
+
fn unqualify(name: String) -> String {
|
|
127
|
+
match name.rfind('.') {
|
|
128
|
+
Some(i) => name[i + 1..].to_string(),
|
|
129
|
+
None => name,
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/// Collect `infix[lr] [prec] op, op ...` declarations anywhere in the token
|
|
134
|
+
/// stream: Haskell lets a fixity declaration follow the operator's uses.
|
|
135
|
+
fn collect_fixities(toks: &[Token]) -> Result<HashMap<String, (u8, Assoc)>> {
|
|
136
|
+
let mut out = HashMap::new();
|
|
137
|
+
let mut i = 0;
|
|
138
|
+
while i < toks.len() {
|
|
139
|
+
let assoc = match toks[i].tok {
|
|
140
|
+
Tok::Infix => Assoc::None,
|
|
141
|
+
Tok::Infixl => Assoc::Left,
|
|
142
|
+
Tok::Infixr => Assoc::Right,
|
|
143
|
+
_ => {
|
|
144
|
+
i += 1;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
let (line, col) = (toks[i].line, toks[i].col);
|
|
149
|
+
i += 1;
|
|
150
|
+
let mut prec = 9u8;
|
|
151
|
+
if let Tok::Int(n) = &toks[i].tok {
|
|
152
|
+
prec = n.parse::<u8>().ok().filter(|p| *p <= 9).ok_or_else(|| {
|
|
153
|
+
CoreError::new(
|
|
154
|
+
ErrorKind::Syntax,
|
|
155
|
+
format!("{}:{}: fixity precedence must be 0..9", line, col),
|
|
156
|
+
)
|
|
157
|
+
})?;
|
|
158
|
+
i += 1;
|
|
159
|
+
}
|
|
160
|
+
loop {
|
|
161
|
+
match &toks[i].tok {
|
|
162
|
+
Tok::VarSym(s) | Tok::ConSym(s) => {
|
|
163
|
+
out.insert(s.clone(), (prec, assoc));
|
|
164
|
+
i += 1;
|
|
165
|
+
}
|
|
166
|
+
Tok::Backtick => {
|
|
167
|
+
if let (Tok::VarId(v), Tok::Backtick) = (&toks[i + 1].tok, &toks[i + 2].tok) {
|
|
168
|
+
out.insert(v.clone(), (prec, assoc));
|
|
169
|
+
i += 3;
|
|
170
|
+
} else {
|
|
171
|
+
return Err(CoreError::new(
|
|
172
|
+
ErrorKind::Syntax,
|
|
173
|
+
format!("{}:{}: malformed fixity declaration", line, col),
|
|
174
|
+
));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
t => {
|
|
178
|
+
return Err(CoreError::new(
|
|
179
|
+
ErrorKind::Syntax,
|
|
180
|
+
format!(
|
|
181
|
+
"{}:{}: expected an operator in fixity declaration but found {}",
|
|
182
|
+
line,
|
|
183
|
+
col,
|
|
184
|
+
describe(t)
|
|
185
|
+
),
|
|
186
|
+
))
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if toks[i].tok == Tok::Comma {
|
|
190
|
+
i += 1;
|
|
191
|
+
} else {
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
Ok(out)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/// Parse a module (a whole source text).
|
|
200
|
+
pub fn parse_module(src: &str) -> Result<Module> {
|
|
201
|
+
let toks = layout(tokenize(src)?);
|
|
202
|
+
let fixities = collect_fixities(&toks)?;
|
|
203
|
+
let mut p = P {
|
|
204
|
+
toks,
|
|
205
|
+
i: 0,
|
|
206
|
+
fixities,
|
|
207
|
+
gensym: 0,
|
|
208
|
+
};
|
|
209
|
+
let mut name = None;
|
|
210
|
+
let mut exports = None;
|
|
211
|
+
if p.at(Tok::Module) {
|
|
212
|
+
p.next();
|
|
213
|
+
match p.next() {
|
|
214
|
+
Tok::ConId(n) => name = Some(n),
|
|
215
|
+
t => return p.err(format!("expected a module name but found {}", describe(&t))),
|
|
216
|
+
}
|
|
217
|
+
if p.at(Tok::LParen) {
|
|
218
|
+
exports = Some(p.item_list(true)?);
|
|
219
|
+
}
|
|
220
|
+
p.expect(Tok::Where, "'where' after module header")?;
|
|
221
|
+
}
|
|
222
|
+
let decls = p.block(|p| p.top_decl())?;
|
|
223
|
+
if !p.at(Tok::Eof) {
|
|
224
|
+
return p.err(format!("unexpected {}", describe(p.peek())));
|
|
225
|
+
}
|
|
226
|
+
Ok(Module {
|
|
227
|
+
name,
|
|
228
|
+
exports,
|
|
229
|
+
decls: group(decls, &p)?,
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/// Group consecutive equations of one function into a single `Decl::Fun`.
|
|
234
|
+
fn group(items: Vec<Decl>, p: &P) -> Result<Vec<Decl>> {
|
|
235
|
+
let mut out: Vec<Decl> = Vec::new();
|
|
236
|
+
for d in items {
|
|
237
|
+
match d {
|
|
238
|
+
Decl::Fun(mut eqs) => {
|
|
239
|
+
if let Some(Decl::Fun(prev)) = out.last_mut() {
|
|
240
|
+
if prev[0].name == eqs[0].name {
|
|
241
|
+
if prev[0].pats.len() != eqs[0].pats.len() {
|
|
242
|
+
return Err(CoreError::new(
|
|
243
|
+
ErrorKind::ClauseArity,
|
|
244
|
+
format!(
|
|
245
|
+
"{}: equations for '{}' have different numbers of arguments",
|
|
246
|
+
eqs[0].line, eqs[0].name
|
|
247
|
+
),
|
|
248
|
+
));
|
|
249
|
+
}
|
|
250
|
+
prev.append(&mut eqs);
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// a function defined again after other declarations is an error
|
|
255
|
+
for earlier in &out {
|
|
256
|
+
if let Decl::Fun(e) = earlier {
|
|
257
|
+
if e[0].name == eqs[0].name {
|
|
258
|
+
return Err(CoreError::new(
|
|
259
|
+
ErrorKind::Syntax,
|
|
260
|
+
format!(
|
|
261
|
+
"{}: equations for '{}' are not contiguous (first defined at line {})",
|
|
262
|
+
eqs[0].line, eqs[0].name, e[0].line
|
|
263
|
+
),
|
|
264
|
+
));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
out.push(Decl::Fun(eqs));
|
|
269
|
+
}
|
|
270
|
+
other => out.push(other),
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
let _ = p;
|
|
274
|
+
Ok(out)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
impl P {
|
|
278
|
+
/// `{ item ; item ; ... }` with explicit or layout braces.
|
|
279
|
+
fn block<T>(&mut self, mut item: impl FnMut(&mut P) -> Result<Option<T>>) -> Result<Vec<T>> {
|
|
280
|
+
if !self.is_open() {
|
|
281
|
+
return self.err(format!(
|
|
282
|
+
"expected a block but found {}",
|
|
283
|
+
describe(self.peek())
|
|
284
|
+
));
|
|
285
|
+
}
|
|
286
|
+
self.next();
|
|
287
|
+
let mut items = Vec::new();
|
|
288
|
+
loop {
|
|
289
|
+
while self.is_semi() {
|
|
290
|
+
self.next();
|
|
291
|
+
}
|
|
292
|
+
if self.is_close() {
|
|
293
|
+
self.next();
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
if self.at(Tok::Eof) {
|
|
297
|
+
return self.err("unexpected end of input inside a block");
|
|
298
|
+
}
|
|
299
|
+
if let Some(it) = item(self)? {
|
|
300
|
+
items.push(it);
|
|
301
|
+
}
|
|
302
|
+
if self.is_semi() {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
if self.is_close() {
|
|
306
|
+
self.next();
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
return self.err(format!("unexpected {}", describe(self.peek())));
|
|
310
|
+
}
|
|
311
|
+
Ok(items)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/// `( item, item, ... )`: an export or import list. Items are
|
|
315
|
+
/// variables, parenthesised operators, and type names optionally followed
|
|
316
|
+
/// by `(..)` or a constructor list; `module M` entries (exports) are
|
|
317
|
+
/// skipped. Returns the flat list of names.
|
|
318
|
+
fn item_list(&mut self, exports: bool) -> Result<Vec<String>> {
|
|
319
|
+
self.expect(Tok::LParen, "'('")?;
|
|
320
|
+
let mut names = Vec::new();
|
|
321
|
+
loop {
|
|
322
|
+
match self.peek().clone() {
|
|
323
|
+
Tok::RParen => {
|
|
324
|
+
self.next();
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
Tok::Comma => {
|
|
328
|
+
self.next();
|
|
329
|
+
}
|
|
330
|
+
Tok::VarId(v) => {
|
|
331
|
+
self.next();
|
|
332
|
+
names.push(v);
|
|
333
|
+
}
|
|
334
|
+
Tok::Module if exports => {
|
|
335
|
+
self.next();
|
|
336
|
+
match self.next() {
|
|
337
|
+
Tok::ConId(_) => {}
|
|
338
|
+
t => {
|
|
339
|
+
return self
|
|
340
|
+
.err(format!("expected a module name but found {}", describe(&t)))
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
Tok::ConId(c) => {
|
|
345
|
+
self.next();
|
|
346
|
+
names.push(unqualify(c));
|
|
347
|
+
if self.at(Tok::LParen) {
|
|
348
|
+
// `(..)` or `(C1, C2)` or `(f1, f2)`
|
|
349
|
+
self.next();
|
|
350
|
+
loop {
|
|
351
|
+
match self.next() {
|
|
352
|
+
Tok::RParen => break,
|
|
353
|
+
Tok::DotDot | Tok::Comma | Tok::ConId(_) | Tok::VarId(_) => {}
|
|
354
|
+
Tok::LParen => {
|
|
355
|
+
// `((:+:))`
|
|
356
|
+
self.next();
|
|
357
|
+
self.expect(Tok::RParen, "')'")?;
|
|
358
|
+
}
|
|
359
|
+
t => {
|
|
360
|
+
return self.err(format!(
|
|
361
|
+
"unexpected {} in constructor list",
|
|
362
|
+
describe(&t)
|
|
363
|
+
))
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
Tok::LParen => {
|
|
370
|
+
self.next();
|
|
371
|
+
match self.next() {
|
|
372
|
+
Tok::VarSym(s) | Tok::ConSym(s) => names.push(s),
|
|
373
|
+
t => {
|
|
374
|
+
return self
|
|
375
|
+
.err(format!("expected an operator but found {}", describe(&t)))
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
self.expect(Tok::RParen, "')' after operator")?;
|
|
379
|
+
}
|
|
380
|
+
Tok::Type => {
|
|
381
|
+
// `type T` (ExplicitNamespaces): take the name
|
|
382
|
+
self.next();
|
|
383
|
+
}
|
|
384
|
+
t => return self.err(format!("unexpected {} in export/import list", describe(&t))),
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
Ok(names)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/// `import [qualified] M [as A] [hiding] [(items)]`
|
|
391
|
+
fn import_decl(&mut self) -> Result<Decl> {
|
|
392
|
+
let (line, _) = self.here();
|
|
393
|
+
self.next(); // import
|
|
394
|
+
let mut qualified = false;
|
|
395
|
+
if matches!(self.peek(), Tok::VarId(v) if v == "qualified") {
|
|
396
|
+
qualified = true;
|
|
397
|
+
self.next();
|
|
398
|
+
}
|
|
399
|
+
let module = match self.next() {
|
|
400
|
+
Tok::ConId(m) => m,
|
|
401
|
+
t => return self.err(format!("expected a module name but found {}", describe(&t))),
|
|
402
|
+
};
|
|
403
|
+
// ImportQualifiedPost: `import M qualified`
|
|
404
|
+
if matches!(self.peek(), Tok::VarId(v) if v == "qualified") {
|
|
405
|
+
qualified = true;
|
|
406
|
+
self.next();
|
|
407
|
+
}
|
|
408
|
+
let mut alias = None;
|
|
409
|
+
if matches!(self.peek(), Tok::VarId(v) if v == "as") {
|
|
410
|
+
self.next();
|
|
411
|
+
match self.next() {
|
|
412
|
+
Tok::ConId(a) => alias = Some(a),
|
|
413
|
+
t => {
|
|
414
|
+
return self.err(format!(
|
|
415
|
+
"expected an alias after 'as' but found {}",
|
|
416
|
+
describe(&t)
|
|
417
|
+
))
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
let mut hiding = false;
|
|
422
|
+
if matches!(self.peek(), Tok::VarId(v) if v == "hiding") {
|
|
423
|
+
hiding = true;
|
|
424
|
+
self.next();
|
|
425
|
+
}
|
|
426
|
+
let items = if self.at(Tok::LParen) {
|
|
427
|
+
Some(self.item_list(false)?)
|
|
428
|
+
} else {
|
|
429
|
+
None
|
|
430
|
+
};
|
|
431
|
+
if hiding && items.is_none() {
|
|
432
|
+
return self.err("'hiding' needs a list of names");
|
|
433
|
+
}
|
|
434
|
+
Ok(Decl::Import {
|
|
435
|
+
module,
|
|
436
|
+
qualified,
|
|
437
|
+
alias,
|
|
438
|
+
hiding,
|
|
439
|
+
items,
|
|
440
|
+
line,
|
|
441
|
+
})
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
fn top_decl(&mut self) -> Result<Option<Decl>> {
|
|
445
|
+
match self.peek() {
|
|
446
|
+
Tok::Import => self.import_decl().map(Some),
|
|
447
|
+
Tok::Type | Tok::Class | Tok::Instance | Tok::Infix | Tok::Infixl | Tok::Infixr => {
|
|
448
|
+
let what = describe(self.peek());
|
|
449
|
+
if matches!(self.peek(), Tok::Class | Tok::Instance) {
|
|
450
|
+
return self.err(format!(
|
|
451
|
+
"{} declarations are not supported (type classes are out of scope)",
|
|
452
|
+
what
|
|
453
|
+
));
|
|
454
|
+
}
|
|
455
|
+
while !self.is_semi() && !self.is_close() && !self.at(Tok::Eof) {
|
|
456
|
+
if self.is_open() {
|
|
457
|
+
self.skip_block()?;
|
|
458
|
+
} else {
|
|
459
|
+
self.next();
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
Ok(None)
|
|
463
|
+
}
|
|
464
|
+
Tok::Data | Tok::Newtype => self.data_decl().map(Some),
|
|
465
|
+
_ => self.decl().map(Some),
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
fn skip_block(&mut self) -> Result<()> {
|
|
470
|
+
let mut depth = 0;
|
|
471
|
+
loop {
|
|
472
|
+
match self.peek() {
|
|
473
|
+
Tok::LBrace | Tok::VLBrace => depth += 1,
|
|
474
|
+
Tok::RBrace | Tok::VRBrace => {
|
|
475
|
+
depth -= 1;
|
|
476
|
+
if depth == 0 {
|
|
477
|
+
self.next();
|
|
478
|
+
return Ok(());
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
Tok::Eof => return self.err("unexpected end of input"),
|
|
482
|
+
_ => {}
|
|
483
|
+
}
|
|
484
|
+
self.next();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
fn data_decl(&mut self) -> Result<Decl> {
|
|
489
|
+
let (line, _) = self.here();
|
|
490
|
+
self.next(); // data / newtype
|
|
491
|
+
let name = match self.next() {
|
|
492
|
+
Tok::ConId(n) => unqualify(n),
|
|
493
|
+
t => return self.err(format!("expected a type name but found {}", describe(&t))),
|
|
494
|
+
};
|
|
495
|
+
let mut tyvars = Vec::new();
|
|
496
|
+
while let Tok::VarId(v) = self.peek() {
|
|
497
|
+
tyvars.push(v.clone());
|
|
498
|
+
self.next();
|
|
499
|
+
}
|
|
500
|
+
let mut cons = Vec::new();
|
|
501
|
+
if self.at(Tok::Equals) {
|
|
502
|
+
self.next();
|
|
503
|
+
loop {
|
|
504
|
+
cons.push(self.con_decl()?);
|
|
505
|
+
if self.at(Tok::Pipe) {
|
|
506
|
+
self.next();
|
|
507
|
+
} else {
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
let mut deriving = Vec::new();
|
|
513
|
+
if self.at(Tok::Deriving) {
|
|
514
|
+
self.next();
|
|
515
|
+
if self.at(Tok::LParen) {
|
|
516
|
+
self.next();
|
|
517
|
+
loop {
|
|
518
|
+
match self.next() {
|
|
519
|
+
Tok::ConId(c) => deriving.push(unqualify(c)),
|
|
520
|
+
Tok::RParen => break,
|
|
521
|
+
Tok::Comma => {}
|
|
522
|
+
t => {
|
|
523
|
+
return self
|
|
524
|
+
.err(format!("unexpected {} in deriving clause", describe(&t)))
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
} else {
|
|
529
|
+
match self.next() {
|
|
530
|
+
Tok::ConId(c) => deriving.push(unqualify(c)),
|
|
531
|
+
t => {
|
|
532
|
+
return self.err(format!("unexpected {} in deriving clause", describe(&t)))
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
if cons.is_empty() {
|
|
538
|
+
return self.err(format!("type '{}' has no constructors", name));
|
|
539
|
+
}
|
|
540
|
+
for (i, c) in cons.iter().enumerate() {
|
|
541
|
+
if cons[..i].iter().any(|d| d.name == c.name) {
|
|
542
|
+
return Err(CoreError::new(
|
|
543
|
+
ErrorKind::DataDeclaration,
|
|
544
|
+
format!("{}: constructor '{}' is declared twice", line, c.name),
|
|
545
|
+
));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
Ok(Decl::Data {
|
|
549
|
+
decl: DataDecl {
|
|
550
|
+
name,
|
|
551
|
+
tyvars,
|
|
552
|
+
cons,
|
|
553
|
+
deriving: deriving.clone(),
|
|
554
|
+
},
|
|
555
|
+
deriving,
|
|
556
|
+
line,
|
|
557
|
+
})
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
fn con_decl(&mut self) -> Result<ConDecl> {
|
|
561
|
+
// infix constructor `atype :op: atype`
|
|
562
|
+
let infix = match self.peek() {
|
|
563
|
+
Tok::ConId(_) => matches!(self.peek_at(1), Tok::ConSym(_)),
|
|
564
|
+
Tok::LParen if matches!(self.peek_at(1), Tok::ConSym(_)) => false,
|
|
565
|
+
_ => self.starts_atype(),
|
|
566
|
+
};
|
|
567
|
+
if infix {
|
|
568
|
+
self.atype()?;
|
|
569
|
+
let name = match self.next() {
|
|
570
|
+
Tok::ConSym(op) => op,
|
|
571
|
+
t => {
|
|
572
|
+
return self.err(format!(
|
|
573
|
+
"expected an infix constructor but found {}",
|
|
574
|
+
describe(&t)
|
|
575
|
+
))
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
self.atype()?;
|
|
579
|
+
return Ok(ConDecl {
|
|
580
|
+
name,
|
|
581
|
+
arity: 2,
|
|
582
|
+
fields: None,
|
|
583
|
+
types: Vec::new(),
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
let name = match self.next() {
|
|
587
|
+
Tok::ConId(n) => unqualify(n),
|
|
588
|
+
Tok::LParen => {
|
|
589
|
+
let op = match self.next() {
|
|
590
|
+
Tok::ConSym(op) => op,
|
|
591
|
+
t => {
|
|
592
|
+
return self.err(format!(
|
|
593
|
+
"expected a constructor operator but found {}",
|
|
594
|
+
describe(&t)
|
|
595
|
+
))
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
self.expect(Tok::RParen, "')' after constructor operator")?;
|
|
599
|
+
op
|
|
600
|
+
}
|
|
601
|
+
t => {
|
|
602
|
+
return self.err(format!(
|
|
603
|
+
"expected a constructor name but found {}",
|
|
604
|
+
describe(&t)
|
|
605
|
+
))
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
if self.at(Tok::LBrace) {
|
|
609
|
+
self.next();
|
|
610
|
+
let mut fields = Vec::new();
|
|
611
|
+
if self.at(Tok::RBrace) {
|
|
612
|
+
self.next();
|
|
613
|
+
return Ok(ConDecl {
|
|
614
|
+
name,
|
|
615
|
+
arity: 0,
|
|
616
|
+
fields: Some(fields),
|
|
617
|
+
types: Vec::new(),
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
loop {
|
|
621
|
+
let mut names = Vec::new();
|
|
622
|
+
loop {
|
|
623
|
+
match self.next() {
|
|
624
|
+
Tok::VarId(f) => names.push(f),
|
|
625
|
+
t => {
|
|
626
|
+
return self
|
|
627
|
+
.err(format!("expected a field name but found {}", describe(&t)))
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
match self.next() {
|
|
631
|
+
Tok::Comma => {}
|
|
632
|
+
Tok::DoubleColon => break,
|
|
633
|
+
t => return self.err(format!("expected '::' but found {}", describe(&t))),
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
self.skip_type_until(&[Tok::Comma, Tok::RBrace])?;
|
|
637
|
+
for f in names {
|
|
638
|
+
if fields.contains(&f) {
|
|
639
|
+
return self.err(format!("field '{}' is declared twice", f));
|
|
640
|
+
}
|
|
641
|
+
fields.push(f);
|
|
642
|
+
}
|
|
643
|
+
match self.next() {
|
|
644
|
+
Tok::Comma => {}
|
|
645
|
+
Tok::RBrace => break,
|
|
646
|
+
t => {
|
|
647
|
+
return self.err(format!("expected ',' or '}}' but found {}", describe(&t)))
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
let arity = fields.len();
|
|
652
|
+
return Ok(ConDecl {
|
|
653
|
+
name,
|
|
654
|
+
arity,
|
|
655
|
+
fields: Some(fields),
|
|
656
|
+
types: Vec::new(),
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
let mut arity = 0;
|
|
660
|
+
while self.starts_atype() {
|
|
661
|
+
self.atype()?;
|
|
662
|
+
arity += 1;
|
|
663
|
+
}
|
|
664
|
+
Ok(ConDecl {
|
|
665
|
+
name,
|
|
666
|
+
arity,
|
|
667
|
+
fields: None,
|
|
668
|
+
types: Vec::new(),
|
|
669
|
+
})
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
fn starts_atype(&self) -> bool {
|
|
673
|
+
matches!(
|
|
674
|
+
self.peek(),
|
|
675
|
+
Tok::ConId(_) | Tok::VarId(_) | Tok::LParen | Tok::LBracket | Tok::Bang
|
|
676
|
+
)
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/// Skip one atomic type (we keep no type information).
|
|
680
|
+
fn atype(&mut self) -> Result<()> {
|
|
681
|
+
match self.next() {
|
|
682
|
+
Tok::ConId(_) | Tok::VarId(_) => Ok(()),
|
|
683
|
+
Tok::Bang => self.atype(),
|
|
684
|
+
Tok::LParen => {
|
|
685
|
+
let mut depth = 1;
|
|
686
|
+
while depth > 0 {
|
|
687
|
+
match self.next() {
|
|
688
|
+
Tok::LParen => depth += 1,
|
|
689
|
+
Tok::RParen => depth -= 1,
|
|
690
|
+
Tok::Eof => return self.err("unterminated type"),
|
|
691
|
+
_ => {}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
Ok(())
|
|
695
|
+
}
|
|
696
|
+
Tok::LBracket => {
|
|
697
|
+
let mut depth = 1;
|
|
698
|
+
while depth > 0 {
|
|
699
|
+
match self.next() {
|
|
700
|
+
Tok::LBracket => depth += 1,
|
|
701
|
+
Tok::RBracket => depth -= 1,
|
|
702
|
+
Tok::Eof => return self.err("unterminated type"),
|
|
703
|
+
_ => {}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
Ok(())
|
|
707
|
+
}
|
|
708
|
+
t => self.err(format!("unexpected {} in type", describe(&t))),
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/// Skip a type until one of `stops` at depth 0; returns the number of
|
|
713
|
+
/// top-level `->` arrows seen (the arity of a signature).
|
|
714
|
+
fn skip_type_until(&mut self, stops: &[Tok]) -> Result<usize> {
|
|
715
|
+
let mut depth = 0;
|
|
716
|
+
let mut arrows = 0;
|
|
717
|
+
loop {
|
|
718
|
+
let t = self.peek().clone();
|
|
719
|
+
if depth == 0
|
|
720
|
+
&& (stops.contains(&t) || self.is_semi() || self.is_close() || t == Tok::Eof)
|
|
721
|
+
{
|
|
722
|
+
return Ok(arrows);
|
|
723
|
+
}
|
|
724
|
+
match t {
|
|
725
|
+
Tok::LParen | Tok::LBracket => depth += 1,
|
|
726
|
+
Tok::RParen | Tok::RBracket => depth -= 1,
|
|
727
|
+
Tok::RArrow if depth == 0 => arrows += 1,
|
|
728
|
+
_ => {}
|
|
729
|
+
}
|
|
730
|
+
self.next();
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/// `(op)` at the current position: the operator's name.
|
|
735
|
+
fn paren_op(&self) -> Option<String> {
|
|
736
|
+
if self.at(Tok::LParen) && self.peek_at(2) == &Tok::RParen {
|
|
737
|
+
if let Tok::VarSym(s) | Tok::ConSym(s) = self.peek_at(1) {
|
|
738
|
+
return Some(s.clone());
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
None
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/// A declaration inside any block: signature, equation or pattern binding.
|
|
745
|
+
fn decl(&mut self) -> Result<Decl> {
|
|
746
|
+
let (line, _) = self.here();
|
|
747
|
+
// operator in prefix form: `(<+>) :: ...` or `(<+>) a b = ...`
|
|
748
|
+
if let Some(op) = self.paren_op() {
|
|
749
|
+
self.next();
|
|
750
|
+
self.next();
|
|
751
|
+
self.next();
|
|
752
|
+
if self.at(Tok::DoubleColon) {
|
|
753
|
+
self.next();
|
|
754
|
+
let arity = self.skip_type_until(&[])?;
|
|
755
|
+
return Ok(Decl::Sig {
|
|
756
|
+
names: vec![op],
|
|
757
|
+
arity,
|
|
758
|
+
line,
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
let mut pats = Vec::new();
|
|
762
|
+
while self.starts_apat() {
|
|
763
|
+
pats.push(self.apat()?);
|
|
764
|
+
}
|
|
765
|
+
let (rhs, wheres) = self.rhs(Tok::Equals)?;
|
|
766
|
+
if pats.is_empty() {
|
|
767
|
+
// `(<+>) = someFunction`: a value of function type
|
|
768
|
+
return Ok(Decl::PatBind {
|
|
769
|
+
pat: RawPat::Var(op),
|
|
770
|
+
rhs,
|
|
771
|
+
wheres,
|
|
772
|
+
line,
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
return Ok(Decl::Fun(vec![Equation {
|
|
776
|
+
name: op,
|
|
777
|
+
pats,
|
|
778
|
+
rhs,
|
|
779
|
+
wheres,
|
|
780
|
+
line,
|
|
781
|
+
}]));
|
|
782
|
+
}
|
|
783
|
+
// signature: var (, var)* :: type
|
|
784
|
+
if let Tok::VarId(_) = self.peek() {
|
|
785
|
+
let mut j = 1;
|
|
786
|
+
while matches!(self.peek_at(j), Tok::Comma)
|
|
787
|
+
&& matches!(self.peek_at(j + 1), Tok::VarId(_))
|
|
788
|
+
{
|
|
789
|
+
j += 2;
|
|
790
|
+
}
|
|
791
|
+
if matches!(self.peek_at(j), Tok::DoubleColon) {
|
|
792
|
+
let mut names = Vec::new();
|
|
793
|
+
loop {
|
|
794
|
+
match self.next() {
|
|
795
|
+
Tok::VarId(v) => names.push(v),
|
|
796
|
+
Tok::Comma => {}
|
|
797
|
+
Tok::DoubleColon => break,
|
|
798
|
+
t => return self.err(format!("unexpected {} in signature", describe(&t))),
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
let arity = self.skip_type_until(&[])?;
|
|
802
|
+
return Ok(Decl::Sig { names, arity, line });
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
// function equation: var apat* rhs | infix: pat varop pat rhs
|
|
806
|
+
if let Tok::VarId(name) = self.peek().clone() {
|
|
807
|
+
let next = self.peek_at(1).clone();
|
|
808
|
+
if matches!(next, Tok::Equals | Tok::Pipe) {
|
|
809
|
+
// `x = e` : a simple pattern binding (a value)
|
|
810
|
+
self.next();
|
|
811
|
+
let (rhs, wheres) = self.rhs(Tok::Equals)?;
|
|
812
|
+
return Ok(Decl::PatBind {
|
|
813
|
+
pat: RawPat::Var(name),
|
|
814
|
+
rhs,
|
|
815
|
+
wheres,
|
|
816
|
+
line,
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
if self.starts_apat_at(1) {
|
|
820
|
+
self.next();
|
|
821
|
+
let mut pats = Vec::new();
|
|
822
|
+
while self.starts_apat() {
|
|
823
|
+
pats.push(self.apat()?);
|
|
824
|
+
}
|
|
825
|
+
if let Tok::VarSym(_) | Tok::Backtick = self.peek() {
|
|
826
|
+
// `f x <+> y = ...` is really an infix definition of `<+>`
|
|
827
|
+
// whose left operand is the pattern `f x`: not valid Haskell
|
|
828
|
+
return self.err(
|
|
829
|
+
"a function application cannot be the left operand of an operator definition",
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
let (rhs, wheres) = self.rhs(Tok::Equals)?;
|
|
833
|
+
return Ok(Decl::Fun(vec![Equation {
|
|
834
|
+
name,
|
|
835
|
+
pats,
|
|
836
|
+
rhs,
|
|
837
|
+
wheres,
|
|
838
|
+
line,
|
|
839
|
+
}]));
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
// pattern binding `pat = e`, or an infix definition `pat op pat = e`
|
|
843
|
+
let pat = self.pattern()?;
|
|
844
|
+
let infix_name = match self.peek().clone() {
|
|
845
|
+
Tok::VarSym(op) => {
|
|
846
|
+
self.next();
|
|
847
|
+
Some(op)
|
|
848
|
+
}
|
|
849
|
+
Tok::Backtick => {
|
|
850
|
+
self.next();
|
|
851
|
+
let name = match self.next() {
|
|
852
|
+
Tok::VarId(v) => v,
|
|
853
|
+
t => {
|
|
854
|
+
return self.err(format!(
|
|
855
|
+
"expected a function name in backticks but found {}",
|
|
856
|
+
describe(&t)
|
|
857
|
+
))
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
self.expect(Tok::Backtick, "closing backtick")?;
|
|
861
|
+
Some(name)
|
|
862
|
+
}
|
|
863
|
+
_ => None,
|
|
864
|
+
};
|
|
865
|
+
if let Some(name) = infix_name {
|
|
866
|
+
let right = self.pattern()?;
|
|
867
|
+
let (rhs, wheres) = self.rhs(Tok::Equals)?;
|
|
868
|
+
return Ok(Decl::Fun(vec![Equation {
|
|
869
|
+
name,
|
|
870
|
+
pats: vec![pat, right],
|
|
871
|
+
rhs,
|
|
872
|
+
wheres,
|
|
873
|
+
line,
|
|
874
|
+
}]));
|
|
875
|
+
}
|
|
876
|
+
let (rhs, wheres) = self.rhs(Tok::Equals)?;
|
|
877
|
+
Ok(Decl::PatBind {
|
|
878
|
+
pat,
|
|
879
|
+
rhs,
|
|
880
|
+
wheres,
|
|
881
|
+
line,
|
|
882
|
+
})
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/// `= e [where decls]` or guarded `| g = e ... [where decls]`; `sep` is
|
|
886
|
+
/// `=` for equations and `->` for case alternatives.
|
|
887
|
+
fn rhs(&mut self, sep: Tok) -> Result<(Rhs, Vec<Decl>)> {
|
|
888
|
+
let rhs = if self.at(Tok::Pipe) {
|
|
889
|
+
let mut guards = Vec::new();
|
|
890
|
+
while self.at(Tok::Pipe) {
|
|
891
|
+
self.next();
|
|
892
|
+
let quals = self.quals()?;
|
|
893
|
+
self.expect(sep.clone(), if sep == Tok::Equals { "'='" } else { "'->'" })?;
|
|
894
|
+
let e = self.expr()?;
|
|
895
|
+
guards.push((quals, e));
|
|
896
|
+
}
|
|
897
|
+
Rhs::Guarded(guards)
|
|
898
|
+
} else {
|
|
899
|
+
self.expect(sep.clone(), if sep == Tok::Equals { "'='" } else { "'->'" })?;
|
|
900
|
+
Rhs::Plain(self.expr()?)
|
|
901
|
+
};
|
|
902
|
+
let mut wheres = Vec::new();
|
|
903
|
+
if self.at(Tok::Where) {
|
|
904
|
+
self.next();
|
|
905
|
+
let items = self.block(|p| p.decl().map(Some))?;
|
|
906
|
+
wheres = group(items, self)?;
|
|
907
|
+
}
|
|
908
|
+
Ok((rhs, wheres))
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// ----------------------------------------------------------------- patterns
|
|
912
|
+
|
|
913
|
+
fn starts_apat(&self) -> bool {
|
|
914
|
+
self.starts_apat_at(0)
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
fn starts_apat_at(&self, n: usize) -> bool {
|
|
918
|
+
matches!(
|
|
919
|
+
self.peek_at(n),
|
|
920
|
+
Tok::VarId(_)
|
|
921
|
+
| Tok::ConId(_)
|
|
922
|
+
| Tok::Int(_)
|
|
923
|
+
| Tok::Float(_)
|
|
924
|
+
| Tok::Char(_)
|
|
925
|
+
| Tok::Str(_)
|
|
926
|
+
| Tok::Underscore
|
|
927
|
+
| Tok::LParen
|
|
928
|
+
| Tok::LBracket
|
|
929
|
+
| Tok::Tilde
|
|
930
|
+
| Tok::Bang
|
|
931
|
+
)
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
/// pattern := lpat (consym pattern)?
|
|
935
|
+
fn pattern(&mut self) -> Result<RawPat> {
|
|
936
|
+
let head = self.lpat()?;
|
|
937
|
+
if let Tok::ConSym(s) = self.peek().clone() {
|
|
938
|
+
self.next();
|
|
939
|
+
let tail = self.pattern()?;
|
|
940
|
+
if s == ":" {
|
|
941
|
+
return Ok(RawPat::Cons(Box::new(head), Box::new(tail)));
|
|
942
|
+
}
|
|
943
|
+
return Ok(RawPat::Con(s, vec![head, tail]));
|
|
944
|
+
}
|
|
945
|
+
Ok(head)
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
fn lpat(&mut self) -> Result<RawPat> {
|
|
949
|
+
match self.peek().clone() {
|
|
950
|
+
Tok::ConId(name) if !matches!(self.peek_at(1), Tok::LBrace) => {
|
|
951
|
+
let name = unqualify(name);
|
|
952
|
+
self.next();
|
|
953
|
+
let mut args = Vec::new();
|
|
954
|
+
while self.starts_apat() {
|
|
955
|
+
args.push(self.apat()?);
|
|
956
|
+
}
|
|
957
|
+
Ok(RawPat::Con(name, args))
|
|
958
|
+
}
|
|
959
|
+
// prefix use of an infix constructor: `(:+:) a b`
|
|
960
|
+
Tok::LParen
|
|
961
|
+
if matches!(self.peek_at(1), Tok::ConSym(_)) && self.peek_at(2) == &Tok::RParen =>
|
|
962
|
+
{
|
|
963
|
+
self.next();
|
|
964
|
+
let name = match self.next() {
|
|
965
|
+
Tok::ConSym(op) => op,
|
|
966
|
+
_ => unreachable!(),
|
|
967
|
+
};
|
|
968
|
+
self.next();
|
|
969
|
+
let mut args = Vec::new();
|
|
970
|
+
while self.starts_apat() {
|
|
971
|
+
args.push(self.apat()?);
|
|
972
|
+
}
|
|
973
|
+
Ok(RawPat::Con(name, args))
|
|
974
|
+
}
|
|
975
|
+
Tok::VarSym(s) if s == "-" => {
|
|
976
|
+
self.next();
|
|
977
|
+
match self.next() {
|
|
978
|
+
Tok::Int(n) => Ok(RawPat::Lit(crate::core::parser::int_lit(&format!(
|
|
979
|
+
"-{}",
|
|
980
|
+
n
|
|
981
|
+
)))),
|
|
982
|
+
Tok::Float(f) => {
|
|
983
|
+
let v: f64 = f
|
|
984
|
+
.parse()
|
|
985
|
+
.map_err(|_| self.err::<()>("bad float").unwrap_err())?;
|
|
986
|
+
Ok(RawPat::Lit(crate::core::parser::float_lit(-v)))
|
|
987
|
+
}
|
|
988
|
+
t => self.err(format!(
|
|
989
|
+
"expected a number after '-' in pattern, found {}",
|
|
990
|
+
describe(&t)
|
|
991
|
+
)),
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
_ => self.apat(),
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
fn apat(&mut self) -> Result<RawPat> {
|
|
999
|
+
match self.next() {
|
|
1000
|
+
Tok::Underscore => Ok(RawPat::Wild),
|
|
1001
|
+
Tok::VarId(v) => {
|
|
1002
|
+
if self.at(Tok::At) {
|
|
1003
|
+
self.next();
|
|
1004
|
+
let inner = self.apat()?;
|
|
1005
|
+
Ok(RawPat::As(v, Box::new(inner)))
|
|
1006
|
+
} else {
|
|
1007
|
+
Ok(RawPat::Var(v))
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
Tok::Tilde => Ok(RawPat::Lazy(Box::new(self.apat()?))),
|
|
1011
|
+
Tok::Bang => Ok(RawPat::Bang(Box::new(self.apat()?))),
|
|
1012
|
+
Tok::ConId(name) => {
|
|
1013
|
+
let name = unqualify(name);
|
|
1014
|
+
if self.at(Tok::LBrace) {
|
|
1015
|
+
self.next();
|
|
1016
|
+
self.record_pat(name)
|
|
1017
|
+
} else {
|
|
1018
|
+
Ok(RawPat::Con(name, vec![]))
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
Tok::Int(s) => Ok(RawPat::Lit(crate::core::parser::int_lit(&s))),
|
|
1022
|
+
Tok::Float(s) => {
|
|
1023
|
+
let v: f64 = s
|
|
1024
|
+
.parse()
|
|
1025
|
+
.map_err(|_| self.err::<()>("bad float").unwrap_err())?;
|
|
1026
|
+
Ok(RawPat::Lit(crate::core::parser::float_lit(v)))
|
|
1027
|
+
}
|
|
1028
|
+
Tok::Char(c) => Ok(RawPat::Lit(Lit::Char(c))),
|
|
1029
|
+
Tok::Str(s) => Ok(RawPat::List(
|
|
1030
|
+
s.chars().map(|c| RawPat::Lit(Lit::Char(c))).collect(),
|
|
1031
|
+
)),
|
|
1032
|
+
Tok::LParen => {
|
|
1033
|
+
if self.at(Tok::RParen) {
|
|
1034
|
+
self.next();
|
|
1035
|
+
return Ok(RawPat::Tuple(vec![]));
|
|
1036
|
+
}
|
|
1037
|
+
let first = self.pattern()?;
|
|
1038
|
+
if self.at(Tok::Comma) {
|
|
1039
|
+
let mut items = vec![first];
|
|
1040
|
+
while self.at(Tok::Comma) {
|
|
1041
|
+
self.next();
|
|
1042
|
+
items.push(self.pattern()?);
|
|
1043
|
+
}
|
|
1044
|
+
self.expect(Tok::RParen, "')'")?;
|
|
1045
|
+
Ok(RawPat::Tuple(items))
|
|
1046
|
+
} else {
|
|
1047
|
+
self.expect(Tok::RParen, "')'")?;
|
|
1048
|
+
Ok(first)
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
Tok::LBracket => {
|
|
1052
|
+
let mut items = Vec::new();
|
|
1053
|
+
if self.at(Tok::RBracket) {
|
|
1054
|
+
self.next();
|
|
1055
|
+
return Ok(RawPat::List(items));
|
|
1056
|
+
}
|
|
1057
|
+
loop {
|
|
1058
|
+
items.push(self.pattern()?);
|
|
1059
|
+
match self.next() {
|
|
1060
|
+
Tok::Comma => {}
|
|
1061
|
+
Tok::RBracket => break,
|
|
1062
|
+
t => {
|
|
1063
|
+
return self.err(format!(
|
|
1064
|
+
"expected ',' or ']' in list pattern, found {}",
|
|
1065
|
+
describe(&t)
|
|
1066
|
+
))
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
Ok(RawPat::List(items))
|
|
1071
|
+
}
|
|
1072
|
+
t => self.err(format!("unexpected {} in pattern", describe(&t))),
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
fn record_pat(&mut self, con: String) -> Result<RawPat> {
|
|
1077
|
+
let mut fields = Vec::new();
|
|
1078
|
+
let mut rest = false;
|
|
1079
|
+
if self.at(Tok::RBrace) {
|
|
1080
|
+
self.next();
|
|
1081
|
+
return Ok(RawPat::Record(con, fields, false));
|
|
1082
|
+
}
|
|
1083
|
+
loop {
|
|
1084
|
+
match self.next() {
|
|
1085
|
+
Tok::DotDot => {
|
|
1086
|
+
rest = true;
|
|
1087
|
+
self.expect(Tok::RBrace, "'}' after '..'")?;
|
|
1088
|
+
break;
|
|
1089
|
+
}
|
|
1090
|
+
Tok::VarId(f) => {
|
|
1091
|
+
if self.at(Tok::Equals) {
|
|
1092
|
+
self.next();
|
|
1093
|
+
let p = self.pattern()?;
|
|
1094
|
+
fields.push((f, p));
|
|
1095
|
+
} else {
|
|
1096
|
+
let v = f.clone();
|
|
1097
|
+
fields.push((f, RawPat::Var(v)));
|
|
1098
|
+
}
|
|
1099
|
+
match self.next() {
|
|
1100
|
+
Tok::Comma => {}
|
|
1101
|
+
Tok::RBrace => break,
|
|
1102
|
+
t => {
|
|
1103
|
+
return self.err(format!(
|
|
1104
|
+
"expected ',' or '}}' in record pattern, found {}",
|
|
1105
|
+
describe(&t)
|
|
1106
|
+
))
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
t => return self.err(format!("unexpected {} in record pattern", describe(&t))),
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
Ok(RawPat::Record(con, fields, rest))
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// -------------------------------------------------------------- expressions
|
|
1117
|
+
|
|
1118
|
+
pub fn expr(&mut self) -> Result<Expr> {
|
|
1119
|
+
let e = self.infix_expr(0)?;
|
|
1120
|
+
if self.at(Tok::DoubleColon) {
|
|
1121
|
+
// type annotation: skip
|
|
1122
|
+
self.next();
|
|
1123
|
+
self.skip_type_until(&[
|
|
1124
|
+
Tok::RParen,
|
|
1125
|
+
Tok::Comma,
|
|
1126
|
+
Tok::RBracket,
|
|
1127
|
+
Tok::Then,
|
|
1128
|
+
Tok::Else,
|
|
1129
|
+
Tok::Of,
|
|
1130
|
+
Tok::In,
|
|
1131
|
+
])?;
|
|
1132
|
+
}
|
|
1133
|
+
Ok(e)
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
fn peek_op(&self) -> Option<(String, usize)> {
|
|
1137
|
+
match self.peek() {
|
|
1138
|
+
Tok::VarSym(s) | Tok::ConSym(s) => Some((s.clone(), 1)),
|
|
1139
|
+
Tok::Backtick => {
|
|
1140
|
+
if let Tok::VarId(v) = self.peek_at(1) {
|
|
1141
|
+
if matches!(self.peek_at(2), Tok::Backtick) {
|
|
1142
|
+
return Some((v.clone(), 3));
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
None
|
|
1146
|
+
}
|
|
1147
|
+
_ => None,
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
/// Precedence climbing over `lexp (op lexp)*`.
|
|
1152
|
+
fn infix_expr(&mut self, min_prec: u8) -> Result<Expr> {
|
|
1153
|
+
let mut lhs = if let Tok::VarSym(s) = self.peek() {
|
|
1154
|
+
if s == "-" {
|
|
1155
|
+
self.next();
|
|
1156
|
+
let operand = self.infix_expr(7)?;
|
|
1157
|
+
Expr::Neg(Box::new(operand))
|
|
1158
|
+
} else {
|
|
1159
|
+
self.lexp()?
|
|
1160
|
+
}
|
|
1161
|
+
} else {
|
|
1162
|
+
self.lexp()?
|
|
1163
|
+
};
|
|
1164
|
+
while let Some((op, width)) = self.peek_op() {
|
|
1165
|
+
// `(e op)`: the operator belongs to a left section, not to us
|
|
1166
|
+
if self.peek_at(width) == &Tok::RParen {
|
|
1167
|
+
break;
|
|
1168
|
+
}
|
|
1169
|
+
let (prec, assoc) = self.fixity(&op);
|
|
1170
|
+
if prec < min_prec {
|
|
1171
|
+
break;
|
|
1172
|
+
}
|
|
1173
|
+
// consume the operator tokens
|
|
1174
|
+
for _ in 0..width {
|
|
1175
|
+
self.next();
|
|
1176
|
+
}
|
|
1177
|
+
let next_min = match assoc {
|
|
1178
|
+
Assoc::Left | Assoc::None => prec + 1,
|
|
1179
|
+
Assoc::Right => prec,
|
|
1180
|
+
};
|
|
1181
|
+
let rhs = self.infix_expr(next_min)?;
|
|
1182
|
+
lhs = Expr::BinOp(op, Box::new(lhs), Box::new(rhs));
|
|
1183
|
+
}
|
|
1184
|
+
Ok(lhs)
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
fn lexp(&mut self) -> Result<Expr> {
|
|
1188
|
+
match self.peek() {
|
|
1189
|
+
Tok::Backslash if self.peek_at(1) == &Tok::Case => {
|
|
1190
|
+
// LambdaCase: `\case alts` == `\x -> case x of alts`
|
|
1191
|
+
self.next();
|
|
1192
|
+
self.next();
|
|
1193
|
+
let alts = self.block(|p| p.alt().map(Some))?;
|
|
1194
|
+
if alts.is_empty() {
|
|
1195
|
+
return self.err("\\case needs at least one alternative");
|
|
1196
|
+
}
|
|
1197
|
+
let v = self.fresh("lc");
|
|
1198
|
+
Ok(Expr::Lambda(
|
|
1199
|
+
vec![RawPat::Var(v.clone())],
|
|
1200
|
+
Box::new(Expr::Case(Box::new(Expr::Var(v)), alts)),
|
|
1201
|
+
))
|
|
1202
|
+
}
|
|
1203
|
+
Tok::Backslash => {
|
|
1204
|
+
self.next();
|
|
1205
|
+
let mut pats = Vec::new();
|
|
1206
|
+
while self.starts_apat() {
|
|
1207
|
+
pats.push(self.apat()?);
|
|
1208
|
+
}
|
|
1209
|
+
if pats.is_empty() {
|
|
1210
|
+
return self.err("a lambda needs at least one parameter");
|
|
1211
|
+
}
|
|
1212
|
+
self.expect(Tok::RArrow, "'->' in lambda")?;
|
|
1213
|
+
let body = self.expr()?;
|
|
1214
|
+
Ok(Expr::Lambda(pats, Box::new(body)))
|
|
1215
|
+
}
|
|
1216
|
+
Tok::Let => {
|
|
1217
|
+
self.next();
|
|
1218
|
+
let items = self.block(|p| p.decl().map(Some))?;
|
|
1219
|
+
let decls = group(items, self)?;
|
|
1220
|
+
self.expect(Tok::In, "'in' after let bindings")?;
|
|
1221
|
+
let body = self.expr()?;
|
|
1222
|
+
Ok(Expr::Let(decls, Box::new(body)))
|
|
1223
|
+
}
|
|
1224
|
+
Tok::If if self.peek_at(1) == &Tok::Pipe => {
|
|
1225
|
+
// MultiWayIf: `if | quals -> e | quals -> e ...`
|
|
1226
|
+
self.next();
|
|
1227
|
+
let mut arms = Vec::new();
|
|
1228
|
+
while self.at(Tok::Pipe) {
|
|
1229
|
+
self.next();
|
|
1230
|
+
let quals = self.quals()?;
|
|
1231
|
+
self.expect(Tok::RArrow, "'->' in multi-way if")?;
|
|
1232
|
+
let e = self.expr()?;
|
|
1233
|
+
arms.push((quals, e));
|
|
1234
|
+
while self.is_semi() && self.peek_at(1) == &Tok::Pipe {
|
|
1235
|
+
self.next();
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
Ok(Expr::MultiIf(arms))
|
|
1239
|
+
}
|
|
1240
|
+
Tok::If => {
|
|
1241
|
+
self.next();
|
|
1242
|
+
let c = self.expr()?;
|
|
1243
|
+
while self.is_semi() {
|
|
1244
|
+
self.next();
|
|
1245
|
+
}
|
|
1246
|
+
self.expect(Tok::Then, "'then'")?;
|
|
1247
|
+
let t = self.expr()?;
|
|
1248
|
+
while self.is_semi() {
|
|
1249
|
+
self.next();
|
|
1250
|
+
}
|
|
1251
|
+
self.expect(Tok::Else, "'else'")?;
|
|
1252
|
+
let e = self.expr()?;
|
|
1253
|
+
Ok(Expr::If(Box::new(c), Box::new(t), Box::new(e)))
|
|
1254
|
+
}
|
|
1255
|
+
Tok::Case => {
|
|
1256
|
+
self.next();
|
|
1257
|
+
let scrut = self.expr()?;
|
|
1258
|
+
self.expect(Tok::Of, "'of'")?;
|
|
1259
|
+
let alts = self.block(|p| p.alt().map(Some))?;
|
|
1260
|
+
if alts.is_empty() {
|
|
1261
|
+
return self.err("a case expression needs at least one alternative");
|
|
1262
|
+
}
|
|
1263
|
+
Ok(Expr::Case(Box::new(scrut), alts))
|
|
1264
|
+
}
|
|
1265
|
+
Tok::Do => self.err("'do' blocks are not supported (monads are out of scope)"),
|
|
1266
|
+
_ => self.fexp(),
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
/// Comma-separated qualifiers (guards, `pat <- e`, `let`).
|
|
1271
|
+
fn quals(&mut self) -> Result<Vec<Qual>> {
|
|
1272
|
+
let mut quals = Vec::new();
|
|
1273
|
+
loop {
|
|
1274
|
+
quals.push(self.qual()?);
|
|
1275
|
+
if self.at(Tok::Comma) {
|
|
1276
|
+
self.next();
|
|
1277
|
+
} else {
|
|
1278
|
+
break;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
Ok(quals)
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
fn alt(&mut self) -> Result<Alt> {
|
|
1285
|
+
let (line, _) = self.here();
|
|
1286
|
+
let pat = self.pattern()?;
|
|
1287
|
+
let (rhs, wheres) = self.rhs(Tok::RArrow)?;
|
|
1288
|
+
Ok(Alt {
|
|
1289
|
+
pat,
|
|
1290
|
+
rhs,
|
|
1291
|
+
wheres,
|
|
1292
|
+
line,
|
|
1293
|
+
})
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
fn starts_aexp(&self) -> bool {
|
|
1297
|
+
matches!(
|
|
1298
|
+
self.peek(),
|
|
1299
|
+
Tok::VarId(_)
|
|
1300
|
+
| Tok::ConId(_)
|
|
1301
|
+
| Tok::Int(_)
|
|
1302
|
+
| Tok::Float(_)
|
|
1303
|
+
| Tok::Char(_)
|
|
1304
|
+
| Tok::Str(_)
|
|
1305
|
+
| Tok::LParen
|
|
1306
|
+
| Tok::LBracket
|
|
1307
|
+
)
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
/// Function application: `aexp aexp*`.
|
|
1311
|
+
fn fexp(&mut self) -> Result<Expr> {
|
|
1312
|
+
let f = self.aexp_record()?;
|
|
1313
|
+
let mut args = Vec::new();
|
|
1314
|
+
while self.starts_aexp() {
|
|
1315
|
+
args.push(self.aexp_record()?);
|
|
1316
|
+
}
|
|
1317
|
+
if args.is_empty() {
|
|
1318
|
+
Ok(f)
|
|
1319
|
+
} else {
|
|
1320
|
+
Ok(Expr::App(Box::new(f), args))
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
/// An atomic expression optionally followed by record braces:
|
|
1325
|
+
/// `Con { f = e }` constructs, `e { f = e }` updates.
|
|
1326
|
+
fn aexp_record(&mut self) -> Result<Expr> {
|
|
1327
|
+
let mut e = self.aexp()?;
|
|
1328
|
+
while self.at(Tok::LBrace) {
|
|
1329
|
+
self.next();
|
|
1330
|
+
let mut fields = Vec::new();
|
|
1331
|
+
if !self.at(Tok::RBrace) {
|
|
1332
|
+
loop {
|
|
1333
|
+
let f = match self.next() {
|
|
1334
|
+
Tok::VarId(f) => f,
|
|
1335
|
+
t => {
|
|
1336
|
+
return self
|
|
1337
|
+
.err(format!("expected a field name but found {}", describe(&t)))
|
|
1338
|
+
}
|
|
1339
|
+
};
|
|
1340
|
+
let v = if self.at(Tok::Equals) {
|
|
1341
|
+
self.next();
|
|
1342
|
+
self.expr()?
|
|
1343
|
+
} else {
|
|
1344
|
+
Expr::Var(f.clone()) // NamedFieldPuns
|
|
1345
|
+
};
|
|
1346
|
+
fields.push((f, v));
|
|
1347
|
+
match self.next() {
|
|
1348
|
+
Tok::Comma => {}
|
|
1349
|
+
Tok::RBrace => break,
|
|
1350
|
+
t => {
|
|
1351
|
+
return self.err(format!(
|
|
1352
|
+
"expected ',' or '}}' in record syntax but found {}",
|
|
1353
|
+
describe(&t)
|
|
1354
|
+
))
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
} else {
|
|
1359
|
+
self.next();
|
|
1360
|
+
}
|
|
1361
|
+
e = match e {
|
|
1362
|
+
Expr::Con(c) => Expr::RecCon(c, fields),
|
|
1363
|
+
other => Expr::RecUpdate(Box::new(other), fields),
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
Ok(e)
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
fn aexp(&mut self) -> Result<Expr> {
|
|
1370
|
+
match self.next() {
|
|
1371
|
+
Tok::VarId(v) => Ok(Expr::Var(v)),
|
|
1372
|
+
Tok::ConId(c) => Ok(Expr::Con(unqualify(c))),
|
|
1373
|
+
Tok::Int(s) => Ok(Expr::Lit(Literal::Int(s))),
|
|
1374
|
+
Tok::Float(s) => Ok(Expr::Lit(Literal::Float(s))),
|
|
1375
|
+
Tok::Char(c) => Ok(Expr::Lit(Literal::Char(c))),
|
|
1376
|
+
Tok::Str(s) => Ok(Expr::Lit(Literal::Str(s))),
|
|
1377
|
+
Tok::LParen => self.paren(),
|
|
1378
|
+
Tok::LBracket => self.bracket(),
|
|
1379
|
+
Tok::Underscore => self.err("'_' is not an expression"),
|
|
1380
|
+
t => self.err(format!("unexpected {} in expression", describe(&t))),
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
/// After `(`: unit, parenthesised expression, tuple, section, or `(op)`.
|
|
1385
|
+
fn paren(&mut self) -> Result<Expr> {
|
|
1386
|
+
if self.at(Tok::RParen) {
|
|
1387
|
+
self.next();
|
|
1388
|
+
return Ok(Expr::Tuple(vec![]));
|
|
1389
|
+
}
|
|
1390
|
+
// (op) or right section (op e), but `(- e)` is negation
|
|
1391
|
+
if let Some((op, width)) = self.peek_op() {
|
|
1392
|
+
let is_minus = op == "-";
|
|
1393
|
+
let after_op = self.peek_at(width).clone();
|
|
1394
|
+
if after_op == Tok::RParen {
|
|
1395
|
+
for _ in 0..width {
|
|
1396
|
+
self.next();
|
|
1397
|
+
}
|
|
1398
|
+
self.next();
|
|
1399
|
+
return Ok(Expr::OpFun(op));
|
|
1400
|
+
}
|
|
1401
|
+
if !is_minus {
|
|
1402
|
+
for _ in 0..width {
|
|
1403
|
+
self.next();
|
|
1404
|
+
}
|
|
1405
|
+
let (prec, _) = self.fixity(&op);
|
|
1406
|
+
let e = self.infix_expr(prec)?;
|
|
1407
|
+
self.expect(Tok::RParen, "')' to close section")?;
|
|
1408
|
+
return Ok(Expr::SectionR(op, Box::new(e)));
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
// TupleSections: `(,x)`, `(x,)`, `(,,)` ...
|
|
1412
|
+
if self.at(Tok::Comma) {
|
|
1413
|
+
return self.tuple_section(None);
|
|
1414
|
+
}
|
|
1415
|
+
let first = self.expr()?;
|
|
1416
|
+
// left section (e op)
|
|
1417
|
+
if let Some((op, width)) = self.peek_op() {
|
|
1418
|
+
if self.peek_at(width) == &Tok::RParen {
|
|
1419
|
+
for _ in 0..width {
|
|
1420
|
+
self.next();
|
|
1421
|
+
}
|
|
1422
|
+
self.next();
|
|
1423
|
+
return Ok(Expr::SectionL(op, Box::new(first)));
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
if self.at(Tok::Comma) {
|
|
1427
|
+
return self.tuple_section(Some(first));
|
|
1428
|
+
}
|
|
1429
|
+
self.expect(Tok::RParen, "')'")?;
|
|
1430
|
+
Ok(first)
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
/// The rest of a tuple after its first slot (`first`, or `None` when the
|
|
1434
|
+
/// first slot is empty). Empty slots make the tuple a function of the
|
|
1435
|
+
/// missing components (TupleSections); with no empty slot it is a tuple.
|
|
1436
|
+
fn tuple_section(&mut self, first: Option<Expr>) -> Result<Expr> {
|
|
1437
|
+
let mut slots: Vec<Option<Expr>> = vec![first];
|
|
1438
|
+
while self.at(Tok::Comma) {
|
|
1439
|
+
self.next();
|
|
1440
|
+
if self.at(Tok::Comma) || self.at(Tok::RParen) {
|
|
1441
|
+
slots.push(None);
|
|
1442
|
+
} else {
|
|
1443
|
+
slots.push(Some(self.expr()?));
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
self.expect(Tok::RParen, "')' to close tuple")?;
|
|
1447
|
+
if slots.iter().all(|s| s.is_some()) {
|
|
1448
|
+
return Ok(Expr::Tuple(slots.into_iter().map(|s| s.unwrap()).collect()));
|
|
1449
|
+
}
|
|
1450
|
+
let mut params = Vec::new();
|
|
1451
|
+
let items = slots
|
|
1452
|
+
.into_iter()
|
|
1453
|
+
.map(|s| match s {
|
|
1454
|
+
Some(e) => e,
|
|
1455
|
+
None => {
|
|
1456
|
+
let v = self.fresh("ts");
|
|
1457
|
+
params.push(RawPat::Var(v.clone()));
|
|
1458
|
+
Expr::Var(v)
|
|
1459
|
+
}
|
|
1460
|
+
})
|
|
1461
|
+
.collect();
|
|
1462
|
+
Ok(Expr::Lambda(params, Box::new(Expr::Tuple(items))))
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
/// After `[`: list, range or comprehension.
|
|
1466
|
+
fn bracket(&mut self) -> Result<Expr> {
|
|
1467
|
+
if self.at(Tok::RBracket) {
|
|
1468
|
+
self.next();
|
|
1469
|
+
return Ok(Expr::List(vec![]));
|
|
1470
|
+
}
|
|
1471
|
+
let first = self.expr()?;
|
|
1472
|
+
match self.peek() {
|
|
1473
|
+
Tok::DotDot => {
|
|
1474
|
+
self.next();
|
|
1475
|
+
let to = if self.at(Tok::RBracket) {
|
|
1476
|
+
None
|
|
1477
|
+
} else {
|
|
1478
|
+
Some(Box::new(self.expr()?))
|
|
1479
|
+
};
|
|
1480
|
+
self.expect(Tok::RBracket, "']'")?;
|
|
1481
|
+
Ok(Expr::Range {
|
|
1482
|
+
from: Box::new(first),
|
|
1483
|
+
then: None,
|
|
1484
|
+
to,
|
|
1485
|
+
})
|
|
1486
|
+
}
|
|
1487
|
+
Tok::Comma => {
|
|
1488
|
+
self.next();
|
|
1489
|
+
let second = self.expr()?;
|
|
1490
|
+
if self.at(Tok::DotDot) {
|
|
1491
|
+
self.next();
|
|
1492
|
+
let to = if self.at(Tok::RBracket) {
|
|
1493
|
+
None
|
|
1494
|
+
} else {
|
|
1495
|
+
Some(Box::new(self.expr()?))
|
|
1496
|
+
};
|
|
1497
|
+
self.expect(Tok::RBracket, "']'")?;
|
|
1498
|
+
return Ok(Expr::Range {
|
|
1499
|
+
from: Box::new(first),
|
|
1500
|
+
then: Some(Box::new(second)),
|
|
1501
|
+
to,
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
let mut items = vec![first, second];
|
|
1505
|
+
while self.at(Tok::Comma) {
|
|
1506
|
+
self.next();
|
|
1507
|
+
items.push(self.expr()?);
|
|
1508
|
+
}
|
|
1509
|
+
self.expect(Tok::RBracket, "']'")?;
|
|
1510
|
+
Ok(Expr::List(items))
|
|
1511
|
+
}
|
|
1512
|
+
Tok::Pipe => {
|
|
1513
|
+
self.next();
|
|
1514
|
+
let mut quals = Vec::new();
|
|
1515
|
+
loop {
|
|
1516
|
+
quals.push(self.qual()?);
|
|
1517
|
+
if self.at(Tok::Comma) {
|
|
1518
|
+
self.next();
|
|
1519
|
+
} else {
|
|
1520
|
+
break;
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
self.expect(Tok::RBracket, "']' to close comprehension")?;
|
|
1524
|
+
Ok(Expr::Comp(Box::new(first), quals))
|
|
1525
|
+
}
|
|
1526
|
+
_ => {
|
|
1527
|
+
self.expect(Tok::RBracket, "']'")?;
|
|
1528
|
+
Ok(Expr::List(vec![first]))
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
fn qual(&mut self) -> Result<Qual> {
|
|
1534
|
+
if self.at(Tok::Let) {
|
|
1535
|
+
self.next();
|
|
1536
|
+
let items = self.block(|p| p.decl().map(Some))?;
|
|
1537
|
+
return Ok(Qual::Let(group(items, self)?));
|
|
1538
|
+
}
|
|
1539
|
+
// generator `pat <- e`: try to find `<-` before the next `,` / `]` at depth 0
|
|
1540
|
+
let save = self.i;
|
|
1541
|
+
if self.starts_apat() {
|
|
1542
|
+
if let Ok(pat) = self.pattern() {
|
|
1543
|
+
if self.at(Tok::LArrow) {
|
|
1544
|
+
self.next();
|
|
1545
|
+
let e = self.expr()?;
|
|
1546
|
+
return Ok(Qual::Gen(pat, e));
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
self.i = save;
|
|
1551
|
+
Ok(Qual::Guard(self.expr()?))
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
#[cfg(test)]
|
|
1556
|
+
mod tests {
|
|
1557
|
+
use super::*;
|
|
1558
|
+
|
|
1559
|
+
fn parse(src: &str) -> Module {
|
|
1560
|
+
parse_module(src).unwrap_or_else(|e| panic!("{}: {}", e.kind as u8, e.message))
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
fn fun<'a>(m: &'a Module, name: &str) -> &'a Vec<Equation> {
|
|
1564
|
+
m.decls
|
|
1565
|
+
.iter()
|
|
1566
|
+
.find_map(|d| match d {
|
|
1567
|
+
Decl::Fun(eqs) if eqs[0].name == name => Some(eqs),
|
|
1568
|
+
_ => None,
|
|
1569
|
+
})
|
|
1570
|
+
.unwrap_or_else(|| panic!("no function {}", name))
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
#[test]
|
|
1574
|
+
fn equations_guards_where() {
|
|
1575
|
+
let m = parse(
|
|
1576
|
+
"module Geometry where\n\
|
|
1577
|
+
data Shape = Circle Double | Rect Double Double deriving (Show, Eq)\n\
|
|
1578
|
+
area :: Shape -> Double\n\
|
|
1579
|
+
area (Circle r) = pi * r * r\n\
|
|
1580
|
+
area (Rect w h) = w * h\n\
|
|
1581
|
+
describe s\n | area s > 100 = \"big\"\n | otherwise = \"small\"\n\
|
|
1582
|
+
sumTo n = go n 0\n where go 0 acc = acc\n go k acc = go (k - 1) (acc + k)\n",
|
|
1583
|
+
);
|
|
1584
|
+
assert_eq!(m.name.as_deref(), Some("Geometry"));
|
|
1585
|
+
assert!(
|
|
1586
|
+
matches!(&m.decls[0], Decl::Data { deriving, .. } if deriving == &vec!["Show".to_string(), "Eq".to_string()])
|
|
1587
|
+
);
|
|
1588
|
+
assert!(
|
|
1589
|
+
matches!(&m.decls[1], Decl::Sig { names, arity: 1, .. } if names == &vec!["area".to_string()])
|
|
1590
|
+
);
|
|
1591
|
+
let area = fun(&m, "area");
|
|
1592
|
+
assert_eq!(area.len(), 2);
|
|
1593
|
+
assert_eq!(render_pat(&area[0].pats[0]), "Circle hs_r");
|
|
1594
|
+
let describe = fun(&m, "describe");
|
|
1595
|
+
assert!(matches!(describe[0].rhs, Rhs::Guarded(ref g) if g.len() == 2));
|
|
1596
|
+
let sum_to = fun(&m, "sumTo");
|
|
1597
|
+
assert_eq!(sum_to[0].wheres.len(), 1);
|
|
1598
|
+
if let Decl::Fun(go) = &sum_to[0].wheres[0] {
|
|
1599
|
+
assert_eq!(go.len(), 2);
|
|
1600
|
+
assert_eq!(render_pat(&go[1].pats[0]), "hs_k");
|
|
1601
|
+
} else {
|
|
1602
|
+
panic!("where should hold a function");
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
#[test]
|
|
1607
|
+
fn expressions() {
|
|
1608
|
+
let m = parse("f x = x + 2 * 3 - 1\ng = (+ 1) . (* 2) $ 3\nh = [1, 2] ++ [x * 2 | x <- [1..10], even x]\nk = \\a b -> if a then (a, b) else (b, a)\nl = let y = 1; z = 2 in y + z\nm xs = case xs of\n [] -> 0\n (y:_) | y > 0 -> y\n | otherwise -> 0\nn = negate (-5) `div` 2\no = [1, 3 ..]\np = 'c' : \"ab\"\nq = (subtract 1) 5\n");
|
|
1609
|
+
let f = fun(&m, "f");
|
|
1610
|
+
// x + ((2 * 3) - 1)? No: + and - are both infixl 6: ((x + (2*3)) - 1)
|
|
1611
|
+
assert_eq!(
|
|
1612
|
+
f[0].rhs,
|
|
1613
|
+
Rhs::Plain(Expr::BinOp(
|
|
1614
|
+
"-".into(),
|
|
1615
|
+
Box::new(Expr::BinOp(
|
|
1616
|
+
"+".into(),
|
|
1617
|
+
Box::new(Expr::Var("x".into())),
|
|
1618
|
+
Box::new(Expr::BinOp(
|
|
1619
|
+
"*".into(),
|
|
1620
|
+
Box::new(Expr::Lit(Literal::Int("2".into()))),
|
|
1621
|
+
Box::new(Expr::Lit(Literal::Int("3".into())))
|
|
1622
|
+
))
|
|
1623
|
+
)),
|
|
1624
|
+
Box::new(Expr::Lit(Literal::Int("1".into())))
|
|
1625
|
+
))
|
|
1626
|
+
);
|
|
1627
|
+
let g = m
|
|
1628
|
+
.decls
|
|
1629
|
+
.iter()
|
|
1630
|
+
.find_map(|d| {
|
|
1631
|
+
if let Decl::PatBind {
|
|
1632
|
+
pat: RawPat::Var(n),
|
|
1633
|
+
rhs,
|
|
1634
|
+
..
|
|
1635
|
+
} = d
|
|
1636
|
+
{
|
|
1637
|
+
(n == "g").then_some(rhs)
|
|
1638
|
+
} else {
|
|
1639
|
+
None
|
|
1640
|
+
}
|
|
1641
|
+
})
|
|
1642
|
+
.unwrap();
|
|
1643
|
+
// `.` binds tighter than `$`: ((+1) . (*2)) $ 3
|
|
1644
|
+
assert!(matches!(g, Rhs::Plain(Expr::BinOp(op, _, _)) if op == "$"));
|
|
1645
|
+
let h = m
|
|
1646
|
+
.decls
|
|
1647
|
+
.iter()
|
|
1648
|
+
.find_map(|d| {
|
|
1649
|
+
if let Decl::PatBind {
|
|
1650
|
+
pat: RawPat::Var(n),
|
|
1651
|
+
rhs,
|
|
1652
|
+
..
|
|
1653
|
+
} = d
|
|
1654
|
+
{
|
|
1655
|
+
(n == "h").then_some(rhs)
|
|
1656
|
+
} else {
|
|
1657
|
+
None
|
|
1658
|
+
}
|
|
1659
|
+
})
|
|
1660
|
+
.unwrap();
|
|
1661
|
+
assert!(
|
|
1662
|
+
matches!(h, Rhs::Plain(Expr::BinOp(op, _, r)) if op == "++" && matches!(**r, Expr::Comp(_, ref q) if q.len() == 2))
|
|
1663
|
+
);
|
|
1664
|
+
let m_fn = fun(&m, "m");
|
|
1665
|
+
assert!(
|
|
1666
|
+
matches!(&m_fn[0].rhs, Rhs::Plain(Expr::Case(_, alts)) if alts.len() == 2 && matches!(alts[1].rhs, Rhs::Guarded(ref g) if g.len() == 2))
|
|
1667
|
+
);
|
|
1668
|
+
let o = m
|
|
1669
|
+
.decls
|
|
1670
|
+
.iter()
|
|
1671
|
+
.find_map(|d| {
|
|
1672
|
+
if let Decl::PatBind {
|
|
1673
|
+
pat: RawPat::Var(n),
|
|
1674
|
+
rhs,
|
|
1675
|
+
..
|
|
1676
|
+
} = d
|
|
1677
|
+
{
|
|
1678
|
+
(n == "o").then_some(rhs)
|
|
1679
|
+
} else {
|
|
1680
|
+
None
|
|
1681
|
+
}
|
|
1682
|
+
})
|
|
1683
|
+
.unwrap();
|
|
1684
|
+
assert!(matches!(
|
|
1685
|
+
o,
|
|
1686
|
+
Rhs::Plain(Expr::Range {
|
|
1687
|
+
then: Some(_),
|
|
1688
|
+
to: None,
|
|
1689
|
+
..
|
|
1690
|
+
})
|
|
1691
|
+
));
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
#[test]
|
|
1695
|
+
fn errors() {
|
|
1696
|
+
assert!(parse_module("f x = ").is_err());
|
|
1697
|
+
assert!(parse_module("f x = case x of").is_err());
|
|
1698
|
+
assert!(parse_module("class Foo a where\n foo :: a").is_err());
|
|
1699
|
+
assert!(parse_module("f = do\n x").is_err());
|
|
1700
|
+
assert!(parse_module("f 0 = 1\nf x y = 2").is_err());
|
|
1701
|
+
assert!(parse_module("f 0 = 1\ng = 2\nf x = 3").is_err());
|
|
1702
|
+
let e = parse_module("f = (1 +").unwrap_err();
|
|
1703
|
+
assert!(e.message.starts_with("1:"), "{}", e.message);
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
#[test]
|
|
1707
|
+
fn operators_fixities_and_infix_definitions() {
|
|
1708
|
+
let m = parse(
|
|
1709
|
+
"infixl 6 <+>\n\
|
|
1710
|
+
infixr 5 `cons'`\n\
|
|
1711
|
+
(<+>) :: Int -> Int -> Int\n\
|
|
1712
|
+
a <+> b = a + b * 2\n\
|
|
1713
|
+
(<->) a b = a - b\n\
|
|
1714
|
+
x `cons'` xs = x : xs\n\
|
|
1715
|
+
f = 1 <+> 2 <+> 3 <-> 4\n\
|
|
1716
|
+
g = 1 `cons'` 2 `cons'` []\n",
|
|
1717
|
+
);
|
|
1718
|
+
assert_eq!(fun(&m, "<+>")[0].pats.len(), 2);
|
|
1719
|
+
assert_eq!(fun(&m, "<->")[0].pats.len(), 2);
|
|
1720
|
+
assert_eq!(fun(&m, "cons'")[0].pats.len(), 2);
|
|
1721
|
+
// `<+>` is infixl 6 (binds tighter than the default-9 `<->`? no: `<->`
|
|
1722
|
+
// defaults to infixl 9, so `3 <-> 4` groups first)
|
|
1723
|
+
let f = m.decls.iter().find_map(|d| match d {
|
|
1724
|
+
Decl::PatBind {
|
|
1725
|
+
pat: RawPat::Var(n),
|
|
1726
|
+
rhs: Rhs::Plain(e),
|
|
1727
|
+
..
|
|
1728
|
+
} if n == "f" => Some(e.clone()),
|
|
1729
|
+
_ => None,
|
|
1730
|
+
});
|
|
1731
|
+
match f.unwrap() {
|
|
1732
|
+
Expr::BinOp(op, l, r) => {
|
|
1733
|
+
assert_eq!(op, "<+>");
|
|
1734
|
+
assert!(matches!(*l, Expr::BinOp(ref o, ..) if o == "<+>"));
|
|
1735
|
+
assert!(matches!(*r, Expr::BinOp(ref o, ..) if o == "<->"));
|
|
1736
|
+
}
|
|
1737
|
+
other => panic!("unexpected {:?}", other),
|
|
1738
|
+
}
|
|
1739
|
+
// infixr: `1 `cons'` (2 `cons'` [])`
|
|
1740
|
+
let g = m.decls.iter().find_map(|d| match d {
|
|
1741
|
+
Decl::PatBind {
|
|
1742
|
+
pat: RawPat::Var(n),
|
|
1743
|
+
rhs: Rhs::Plain(e),
|
|
1744
|
+
..
|
|
1745
|
+
} if n == "g" => Some(e.clone()),
|
|
1746
|
+
_ => None,
|
|
1747
|
+
});
|
|
1748
|
+
match g.unwrap() {
|
|
1749
|
+
Expr::BinOp(op, _, r) => {
|
|
1750
|
+
assert_eq!(op, "cons'");
|
|
1751
|
+
assert!(matches!(*r, Expr::BinOp(ref o, ..) if o == "cons'"));
|
|
1752
|
+
}
|
|
1753
|
+
other => panic!("unexpected {:?}", other),
|
|
1754
|
+
}
|
|
1755
|
+
assert!(parse_module("infixl 10 <+>\nf = 1").is_err());
|
|
1756
|
+
assert!(parse_module("f x <+> y = 1").is_err());
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
#[test]
|
|
1760
|
+
fn infix_constructors() {
|
|
1761
|
+
let m = parse(
|
|
1762
|
+
"data C = Double :+: Double | (:*:) Int Int\nre (a :+: _) = a\nmk a b = a :+: b\n",
|
|
1763
|
+
);
|
|
1764
|
+
match &m.decls[0] {
|
|
1765
|
+
Decl::Data { decl, .. } => {
|
|
1766
|
+
assert_eq!(decl.cons[0].name, ":+:");
|
|
1767
|
+
assert_eq!(decl.cons[0].arity, 2);
|
|
1768
|
+
assert_eq!(decl.cons[1].name, ":*:");
|
|
1769
|
+
}
|
|
1770
|
+
other => panic!("unexpected {:?}", other),
|
|
1771
|
+
}
|
|
1772
|
+
assert_eq!(render_pat(&fun(&m, "re")[0].pats[0]), "(hs_a :+: _)");
|
|
1773
|
+
assert!(matches!(&fun(&m, "mk")[0].rhs, Rhs::Plain(Expr::BinOp(op, ..)) if op == ":+:"));
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
#[test]
|
|
1777
|
+
fn pattern_guards_multiway_if_lambda_case_tuple_sections() {
|
|
1778
|
+
let m = parse(
|
|
1779
|
+
"f m k\n | Just v <- lookup k m, v > 0 = v\n | let w = k * 2, w > 10 = w\n | otherwise = 0\n\
|
|
1780
|
+
g x = if | x < 0 -> \"neg\"\n | x == 0 -> \"zero\"\n | otherwise -> \"pos\"\n\
|
|
1781
|
+
h = \\case\n 0 -> \"z\"\n _ -> \"nz\"\n\
|
|
1782
|
+
p = (,1)\n\
|
|
1783
|
+
q = (1,,3)\n",
|
|
1784
|
+
);
|
|
1785
|
+
match &fun(&m, "f")[0].rhs {
|
|
1786
|
+
Rhs::Guarded(arms) => {
|
|
1787
|
+
assert_eq!(arms.len(), 3);
|
|
1788
|
+
assert!(matches!(&arms[0].0[0], Qual::Gen(RawPat::Con(c, _), _) if c == "Just"));
|
|
1789
|
+
assert!(matches!(&arms[0].0[1], Qual::Guard(_)));
|
|
1790
|
+
assert!(matches!(&arms[1].0[0], Qual::Let(_)));
|
|
1791
|
+
}
|
|
1792
|
+
other => panic!("unexpected {:?}", other),
|
|
1793
|
+
}
|
|
1794
|
+
assert!(matches!(&fun(&m, "g")[0].rhs, Rhs::Plain(Expr::MultiIf(arms)) if arms.len() == 3));
|
|
1795
|
+
let h = m
|
|
1796
|
+
.decls
|
|
1797
|
+
.iter()
|
|
1798
|
+
.find_map(|d| match d {
|
|
1799
|
+
Decl::PatBind {
|
|
1800
|
+
pat: RawPat::Var(n),
|
|
1801
|
+
rhs: Rhs::Plain(e),
|
|
1802
|
+
..
|
|
1803
|
+
} if n == "h" => Some(e.clone()),
|
|
1804
|
+
_ => None,
|
|
1805
|
+
})
|
|
1806
|
+
.unwrap();
|
|
1807
|
+
assert!(
|
|
1808
|
+
matches!(h, Expr::Lambda(ref ps, ref body) if ps.len() == 1 && matches!(**body, Expr::Case(..)))
|
|
1809
|
+
);
|
|
1810
|
+
let p = m
|
|
1811
|
+
.decls
|
|
1812
|
+
.iter()
|
|
1813
|
+
.find_map(|d| match d {
|
|
1814
|
+
Decl::PatBind {
|
|
1815
|
+
pat: RawPat::Var(n),
|
|
1816
|
+
rhs: Rhs::Plain(e),
|
|
1817
|
+
..
|
|
1818
|
+
} if n == "p" => Some(e.clone()),
|
|
1819
|
+
_ => None,
|
|
1820
|
+
})
|
|
1821
|
+
.unwrap();
|
|
1822
|
+
assert!(
|
|
1823
|
+
matches!(p, Expr::Lambda(ref ps, ref body) if ps.len() == 1 && matches!(**body, Expr::Tuple(ref items) if items.len() == 2))
|
|
1824
|
+
);
|
|
1825
|
+
let q = m
|
|
1826
|
+
.decls
|
|
1827
|
+
.iter()
|
|
1828
|
+
.find_map(|d| match d {
|
|
1829
|
+
Decl::PatBind {
|
|
1830
|
+
pat: RawPat::Var(n),
|
|
1831
|
+
rhs: Rhs::Plain(e),
|
|
1832
|
+
..
|
|
1833
|
+
} if n == "q" => Some(e.clone()),
|
|
1834
|
+
_ => None,
|
|
1835
|
+
})
|
|
1836
|
+
.unwrap();
|
|
1837
|
+
assert!(matches!(q, Expr::Lambda(ref ps, _) if ps.len() == 1));
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
#[test]
|
|
1841
|
+
fn left_and_right_sections() {
|
|
1842
|
+
let m = parse("f = map (3 <+>) xs\ng = map (<+> 3) xs\nh = (1 + 2 <+>)\n");
|
|
1843
|
+
let body = |n: &str| {
|
|
1844
|
+
m.decls
|
|
1845
|
+
.iter()
|
|
1846
|
+
.find_map(|d| match d {
|
|
1847
|
+
Decl::PatBind {
|
|
1848
|
+
pat: RawPat::Var(v),
|
|
1849
|
+
rhs: Rhs::Plain(e),
|
|
1850
|
+
..
|
|
1851
|
+
} if v == n => Some(e.clone()),
|
|
1852
|
+
_ => None,
|
|
1853
|
+
})
|
|
1854
|
+
.unwrap()
|
|
1855
|
+
};
|
|
1856
|
+
assert!(
|
|
1857
|
+
matches!(body("f"), Expr::App(_, args) if matches!(&args[0], Expr::SectionL(op, _) if op == "<+>"))
|
|
1858
|
+
);
|
|
1859
|
+
assert!(
|
|
1860
|
+
matches!(body("g"), Expr::App(_, args) if matches!(&args[0], Expr::SectionR(op, _) if op == "<+>"))
|
|
1861
|
+
);
|
|
1862
|
+
assert!(
|
|
1863
|
+
matches!(body("h"), Expr::SectionL(op, inner) if op == "<+>" && matches!(*inner, Expr::BinOp(..)))
|
|
1864
|
+
);
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
#[test]
|
|
1868
|
+
fn record_construction_and_update() {
|
|
1869
|
+
let m = parse("mk = P { nm = \"a\", yrs = 1 }\nolder p = p { yrs = yrs p + 1 }\npun nm = P { nm, yrs = 0 }\n");
|
|
1870
|
+
let body = |n: &str| {
|
|
1871
|
+
m.decls
|
|
1872
|
+
.iter()
|
|
1873
|
+
.find_map(|d| match d {
|
|
1874
|
+
Decl::PatBind {
|
|
1875
|
+
pat: RawPat::Var(v),
|
|
1876
|
+
rhs: Rhs::Plain(e),
|
|
1877
|
+
..
|
|
1878
|
+
} if v == n => Some(e.clone()),
|
|
1879
|
+
Decl::Fun(eqs) if eqs[0].name == n => match &eqs[0].rhs {
|
|
1880
|
+
Rhs::Plain(e) => Some(e.clone()),
|
|
1881
|
+
_ => None,
|
|
1882
|
+
},
|
|
1883
|
+
_ => None,
|
|
1884
|
+
})
|
|
1885
|
+
.unwrap()
|
|
1886
|
+
};
|
|
1887
|
+
assert!(matches!(body("mk"), Expr::RecCon(c, fs) if c == "P" && fs.len() == 2));
|
|
1888
|
+
assert!(
|
|
1889
|
+
matches!(body("older"), Expr::RecUpdate(_, fs) if fs.len() == 1 && fs[0].0 == "yrs")
|
|
1890
|
+
);
|
|
1891
|
+
assert!(
|
|
1892
|
+
matches!(body("pun"), Expr::RecCon(_, fs) if matches!(&fs[0].1, Expr::Var(v) if v == "nm"))
|
|
1893
|
+
);
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
#[test]
|
|
1897
|
+
fn imports_and_exports() {
|
|
1898
|
+
let m = parse(
|
|
1899
|
+
"module M (f, (<+>), T(..), module X) where\n\
|
|
1900
|
+
import Data.List (sortBy, (\\\\))\n\
|
|
1901
|
+
import qualified Data.Map as Map hiding (foldr)\n\
|
|
1902
|
+
import Geometry\n\
|
|
1903
|
+
f = 1\n",
|
|
1904
|
+
);
|
|
1905
|
+
assert_eq!(
|
|
1906
|
+
m.exports,
|
|
1907
|
+
Some(vec!["f".to_string(), "<+>".to_string(), "T".to_string()])
|
|
1908
|
+
);
|
|
1909
|
+
match &m.decls[0] {
|
|
1910
|
+
Decl::Import {
|
|
1911
|
+
module,
|
|
1912
|
+
items,
|
|
1913
|
+
qualified,
|
|
1914
|
+
..
|
|
1915
|
+
} => {
|
|
1916
|
+
assert_eq!(module, "Data.List");
|
|
1917
|
+
assert_eq!(
|
|
1918
|
+
items.as_ref().unwrap(),
|
|
1919
|
+
&vec!["sortBy".to_string(), "\\\\".to_string()]
|
|
1920
|
+
);
|
|
1921
|
+
assert!(!qualified);
|
|
1922
|
+
}
|
|
1923
|
+
other => panic!("unexpected {:?}", other),
|
|
1924
|
+
}
|
|
1925
|
+
match &m.decls[1] {
|
|
1926
|
+
Decl::Import {
|
|
1927
|
+
module,
|
|
1928
|
+
alias,
|
|
1929
|
+
qualified,
|
|
1930
|
+
hiding,
|
|
1931
|
+
items,
|
|
1932
|
+
..
|
|
1933
|
+
} => {
|
|
1934
|
+
assert_eq!(module, "Data.Map");
|
|
1935
|
+
assert_eq!(alias.as_deref(), Some("Map"));
|
|
1936
|
+
assert!(*qualified && *hiding);
|
|
1937
|
+
assert_eq!(items.as_ref().unwrap(), &vec!["foldr".to_string()]);
|
|
1938
|
+
}
|
|
1939
|
+
other => panic!("unexpected {:?}", other),
|
|
1940
|
+
}
|
|
1941
|
+
assert!(
|
|
1942
|
+
matches!(&m.decls[2], Decl::Import { module, items: None, .. } if module == "Geometry")
|
|
1943
|
+
);
|
|
1944
|
+
}
|
|
1945
|
+
}
|