haskell_match 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +98 -0
  3. data/LICENSE-APACHE +202 -0
  4. data/LICENSE-MIT +21 -0
  5. data/README.md +1484 -0
  6. data/ext/haskell_match/Cargo.lock +33 -0
  7. data/ext/haskell_match/Cargo.toml +22 -0
  8. data/ext/haskell_match/extconf.rb +41 -0
  9. data/ext/haskell_match/src/core/ast.rs +190 -0
  10. data/ext/haskell_match/src/core/error.rs +52 -0
  11. data/ext/haskell_match/src/core/exhaust.rs +699 -0
  12. data/ext/haskell_match/src/core/hs/ast.rs +256 -0
  13. data/ext/haskell_match/src/core/hs/json.rs +225 -0
  14. data/ext/haskell_match/src/core/hs/layout.rs +346 -0
  15. data/ext/haskell_match/src/core/hs/lexer.rs +688 -0
  16. data/ext/haskell_match/src/core/hs/mod.rs +14 -0
  17. data/ext/haskell_match/src/core/hs/parser.rs +1945 -0
  18. data/ext/haskell_match/src/core/lexer.rs +590 -0
  19. data/ext/haskell_match/src/core/mod.rs +19 -0
  20. data/ext/haskell_match/src/core/parser.rs +1116 -0
  21. data/ext/haskell_match/src/core/pretty.rs +373 -0
  22. data/ext/haskell_match/src/core/resolve.rs +336 -0
  23. data/ext/haskell_match/src/core/tree.rs +921 -0
  24. data/ext/haskell_match/src/core/typecheck.rs +226 -0
  25. data/ext/haskell_match/src/core/types.rs +404 -0
  26. data/ext/haskell_match/src/lib.rs +19 -0
  27. data/ext/haskell_match/src/ruby/mod.rs +1195 -0
  28. data/ext/haskell_match/src/ruby/runtime.rs +1045 -0
  29. data/lib/haskell_match/binding_plan.rb +84 -0
  30. data/lib/haskell_match/case_of.rb +71 -0
  31. data/lib/haskell_match/clauses.rb +354 -0
  32. data/lib/haskell_match/data.rb +417 -0
  33. data/lib/haskell_match/deep_call.rb +98 -0
  34. data/lib/haskell_match/deriving.rb +130 -0
  35. data/lib/haskell_match/dsl.rb +71 -0
  36. data/lib/haskell_match/errors.rb +85 -0
  37. data/lib/haskell_match/field_types.rb +140 -0
  38. data/lib/haskell_match/function.rb +240 -0
  39. data/lib/haskell_match/haskell/compiler.rb +961 -0
  40. data/lib/haskell_match/haskell.rb +326 -0
  41. data/lib/haskell_match/inspect.rb +45 -0
  42. data/lib/haskell_match/lazy_list.rb +210 -0
  43. data/lib/haskell_match/native_loader.rb +64 -0
  44. data/lib/haskell_match/pattern.rb +75 -0
  45. data/lib/haskell_match/pattern_ast.rb +394 -0
  46. data/lib/haskell_match/prelude.rb +448 -0
  47. data/lib/haskell_match/scope.rb +44 -0
  48. data/lib/haskell_match/version.rb +5 -0
  49. data/lib/haskell_match.rb +41 -0
  50. metadata +124 -0
@@ -0,0 +1,346 @@
1
+ //! The Haskell 2010 layout algorithm (report §10.3), simplified: implicit
2
+ //! blocks after `where`, `let`, `do` and `of`, closed by indentation, by
3
+ //! `in`, or when a closing bracket of an enclosing explicit construct is met.
4
+
5
+ use super::lexer::{Tok, Token};
6
+
7
+ enum Ctx {
8
+ Explicit,
9
+ /// column of the block, whether `let` opened it (so `in` may close it),
10
+ /// and whether the current item of a let block has already seen its `=`
11
+ Implicit(usize, bool, bool),
12
+ }
13
+
14
+ fn opens_block(t: &Tok) -> bool {
15
+ matches!(t, Tok::Where | Tok::Let | Tok::Do | Tok::Of)
16
+ }
17
+
18
+ pub fn layout(tokens: Vec<Token>) -> Vec<Token> {
19
+ let mut out: Vec<Token> = Vec::with_capacity(tokens.len() * 2);
20
+ let mut ctx: Vec<Ctx> = Vec::new();
21
+ let mut i = 0;
22
+ let n = tokens.len();
23
+ let mut pending_open: Option<bool> = None; // Some(is_let) after a layout keyword
24
+ let mut last_line = 0usize;
25
+ // the whole module is an implicit block unless it starts with `module` or `{`
26
+ if !matches!(
27
+ tokens.first().map(|t| &t.tok),
28
+ Some(Tok::Module) | Some(Tok::LBrace)
29
+ ) {
30
+ if let Some(first) = tokens.first() {
31
+ if first.tok != Tok::Eof {
32
+ ctx.push(Ctx::Implicit(first.col, false, false));
33
+ out.push(Token {
34
+ tok: Tok::VLBrace,
35
+ line: first.line,
36
+ col: first.col,
37
+ });
38
+ last_line = first.line;
39
+ }
40
+ }
41
+ }
42
+ // explicit-bracket depth tracking so a `)` / `]` / `,` can close implicit blocks
43
+ let mut bracket_stack: Vec<usize> = Vec::new(); // ctx depth at each open bracket
44
+ while i < n {
45
+ let t = &tokens[i];
46
+ if t.tok == Tok::Eof {
47
+ // close everything
48
+ while let Some(c) = ctx.pop() {
49
+ if let Ctx::Implicit(..) = c {
50
+ out.push(Token {
51
+ tok: Tok::VRBrace,
52
+ line: t.line,
53
+ col: t.col,
54
+ });
55
+ }
56
+ }
57
+ out.push(t.clone());
58
+ break;
59
+ }
60
+ if let Some(is_let) = pending_open.take() {
61
+ if t.tok == Tok::LBrace {
62
+ ctx.push(Ctx::Explicit);
63
+ out.push(t.clone());
64
+ last_line = t.line;
65
+ i += 1;
66
+ continue;
67
+ }
68
+ // open an implicit block at this token's column
69
+ let enclosing = ctx.iter().rev().find_map(|c| {
70
+ if let Ctx::Implicit(m, _, _) = c {
71
+ Some(*m)
72
+ } else {
73
+ None
74
+ }
75
+ });
76
+ if enclosing.map(|m| t.col > m).unwrap_or(true) {
77
+ ctx.push(Ctx::Implicit(t.col, is_let, false));
78
+ out.push(Token {
79
+ tok: Tok::VLBrace,
80
+ line: t.line,
81
+ col: t.col,
82
+ });
83
+ } else {
84
+ // empty block: `{}` then fall through to normal processing
85
+ out.push(Token {
86
+ tok: Tok::VLBrace,
87
+ line: t.line,
88
+ col: t.col,
89
+ });
90
+ out.push(Token {
91
+ tok: Tok::VRBrace,
92
+ line: t.line,
93
+ col: t.col,
94
+ });
95
+ }
96
+ last_line = t.line;
97
+ // the token itself is processed below without the new-line rule
98
+ emit(&mut out, &mut ctx, &mut bracket_stack, t, &mut pending_open);
99
+ i += 1;
100
+ continue;
101
+ }
102
+ // new line: compare indentation with the innermost implicit context
103
+ if t.line > last_line {
104
+ loop {
105
+ match ctx.last() {
106
+ Some(Ctx::Implicit(m, _, _)) if t.col < *m => {
107
+ ctx.pop();
108
+ out.push(Token {
109
+ tok: Tok::VRBrace,
110
+ line: t.line,
111
+ col: t.col,
112
+ });
113
+ }
114
+ Some(Ctx::Implicit(m, _, _)) if t.col == *m => {
115
+ // a new item in the block, unless the token continues an expression
116
+ if !matches!(t.tok, Tok::Then | Tok::Else | Tok::Of | Tok::In) {
117
+ out.push(Token {
118
+ tok: Tok::VSemi,
119
+ line: t.line,
120
+ col: t.col,
121
+ });
122
+ if let Some(Ctx::Implicit(_, _, eq)) = ctx.last_mut() {
123
+ *eq = false;
124
+ }
125
+ }
126
+ break;
127
+ }
128
+ _ => break,
129
+ }
130
+ }
131
+ last_line = t.line;
132
+ }
133
+ // `\case` (LambdaCase) opens an alternatives block like `of`
134
+ let lambda_case =
135
+ t.tok == Tok::Case && out.last().map(|x| x.tok == Tok::Backslash).unwrap_or(false);
136
+ emit(&mut out, &mut ctx, &mut bracket_stack, t, &mut pending_open);
137
+ if lambda_case {
138
+ pending_open = Some(false);
139
+ }
140
+ i += 1;
141
+ }
142
+ out
143
+ }
144
+
145
+ fn emit(
146
+ out: &mut Vec<Token>,
147
+ ctx: &mut Vec<Ctx>,
148
+ brackets: &mut Vec<usize>,
149
+ t: &Token,
150
+ pending_open: &mut Option<bool>,
151
+ ) {
152
+ match &t.tok {
153
+ Tok::Equals => {
154
+ // Inside a `let` block each binding has one `=`; a second one
155
+ // before the next binding means the block was a guard qualifier
156
+ // (`| let y = f x = y`) and the block ends here (the report's
157
+ // parse-error(t) rule, for this one case).
158
+ let inside_block = brackets.last().map(|d| ctx.len() > *d).unwrap_or(true);
159
+ if let Some(Ctx::Implicit(_, true, eq_seen)) = ctx.last_mut() {
160
+ if *eq_seen && inside_block {
161
+ ctx.pop();
162
+ out.push(Token {
163
+ tok: Tok::VRBrace,
164
+ line: t.line,
165
+ col: t.col,
166
+ });
167
+ } else {
168
+ *eq_seen = true;
169
+ }
170
+ }
171
+ out.push(t.clone());
172
+ }
173
+ Tok::Semi => {
174
+ if let Some(Ctx::Implicit(_, _, eq)) = ctx.last_mut() {
175
+ *eq = false;
176
+ }
177
+ out.push(t.clone());
178
+ }
179
+ Tok::In => {
180
+ // `let ... in`: close the implicit let block if it is still open
181
+ // (the indentation rule may already have closed it)
182
+ if let Some(Ctx::Implicit(_, true, _)) = ctx.last() {
183
+ if brackets.last().map(|d| ctx.len() > *d).unwrap_or(true) {
184
+ ctx.pop();
185
+ out.push(Token {
186
+ tok: Tok::VRBrace,
187
+ line: t.line,
188
+ col: t.col,
189
+ });
190
+ }
191
+ }
192
+ out.push(t.clone());
193
+ }
194
+ Tok::LParen | Tok::LBracket => {
195
+ brackets.push(ctx.len());
196
+ out.push(t.clone());
197
+ }
198
+ Tok::RParen | Tok::RBracket => {
199
+ if let Some(depth) = brackets.pop() {
200
+ while ctx.len() > depth {
201
+ if let Some(Ctx::Implicit(..)) = ctx.pop() {
202
+ out.push(Token {
203
+ tok: Tok::VRBrace,
204
+ line: t.line,
205
+ col: t.col,
206
+ });
207
+ }
208
+ }
209
+ }
210
+ out.push(t.clone());
211
+ }
212
+ Tok::Comma => {
213
+ // a comma belonging to an enclosing bracket closes implicit blocks opened inside it
214
+ if let Some(depth) = brackets.last() {
215
+ while ctx.len() > *depth {
216
+ if let Some(Ctx::Implicit(..)) = ctx.pop() {
217
+ out.push(Token {
218
+ tok: Tok::VRBrace,
219
+ line: t.line,
220
+ col: t.col,
221
+ });
222
+ }
223
+ }
224
+ } else if let Some(Ctx::Implicit(_, true, _)) = ctx.last() {
225
+ // `| let y = e, cond`: a comma ends a let block used as a guard
226
+ ctx.pop();
227
+ out.push(Token {
228
+ tok: Tok::VRBrace,
229
+ line: t.line,
230
+ col: t.col,
231
+ });
232
+ }
233
+ out.push(t.clone());
234
+ }
235
+ Tok::LBrace => {
236
+ ctx.push(Ctx::Explicit);
237
+ out.push(t.clone());
238
+ }
239
+ Tok::RBrace => {
240
+ // close implicit blocks opened inside the explicit one
241
+ while let Some(Ctx::Implicit(..)) = ctx.last() {
242
+ ctx.pop();
243
+ out.push(Token {
244
+ tok: Tok::VRBrace,
245
+ line: t.line,
246
+ col: t.col,
247
+ });
248
+ }
249
+ if let Some(Ctx::Explicit) = ctx.last() {
250
+ ctx.pop();
251
+ }
252
+ out.push(t.clone());
253
+ }
254
+ Tok::Then | Tok::Else | Tok::Of => {
255
+ // `case e of` inside a `let`/`where` item on the same line is fine;
256
+ // `then`/`else` never close blocks here (handled by indentation)
257
+ out.push(t.clone());
258
+ if t.tok == Tok::Of {
259
+ *pending_open = Some(false);
260
+ }
261
+ }
262
+ tok => {
263
+ out.push(t.clone());
264
+ if opens_block(tok) {
265
+ *pending_open = Some(matches!(tok, Tok::Let));
266
+ }
267
+ }
268
+ }
269
+ }
270
+
271
+ #[cfg(test)]
272
+ mod tests {
273
+ use super::*;
274
+ use crate::core::hs::lexer::tokenize;
275
+
276
+ fn show(src: &str) -> String {
277
+ layout(tokenize(src).unwrap())
278
+ .into_iter()
279
+ .map(|t| match t.tok {
280
+ Tok::VLBrace => "{".to_string(),
281
+ Tok::VRBrace => "}".to_string(),
282
+ Tok::VSemi => ";".to_string(),
283
+ Tok::Eof => "".to_string(),
284
+ Tok::VarId(v) => v,
285
+ Tok::ConId(c) => c,
286
+ Tok::VarSym(s) | Tok::ConSym(s) => s,
287
+ Tok::Int(s) => s,
288
+ Tok::Equals => "=".into(),
289
+ Tok::Where => "where".into(),
290
+ Tok::Let => "let".into(),
291
+ Tok::In => "in".into(),
292
+ Tok::Case => "case".into(),
293
+ Tok::Of => "of".into(),
294
+ Tok::RArrow => "->".into(),
295
+ Tok::LParen => "(".into(),
296
+ Tok::RParen => ")".into(),
297
+ Tok::Pipe => "|".into(),
298
+ other => format!("{:?}", other),
299
+ })
300
+ .filter(|s| !s.is_empty())
301
+ .collect::<Vec<_>>()
302
+ .join(" ")
303
+ }
304
+
305
+ #[test]
306
+ fn top_level_items_and_where() {
307
+ let src = "f x = y\n where y = x\n z = 1\ng = 2\n";
308
+ assert_eq!(show(src), "{ f x = y where { y = x ; z = 1 } ; g = 2 }");
309
+ }
310
+
311
+ #[test]
312
+ fn let_in_and_case() {
313
+ assert_eq!(show("f = let a = 1 in a"), "{ f = let { a = 1 } in a }");
314
+ assert_eq!(
315
+ show("f x = case x of\n 0 -> 1\n n -> n\ng = 3"),
316
+ "{ f x = case x of { 0 -> 1 ; n -> n } ; g = 3 }"
317
+ );
318
+ assert_eq!(
319
+ show("f = (let a = 1 in a) + 1"),
320
+ "{ f = ( let { a = 1 } in a ) + 1 }"
321
+ );
322
+ }
323
+
324
+ #[test]
325
+ fn multi_line_let_with_in_on_its_own_line() {
326
+ let src = "f x = let y = x\n z = y\n in y + z\ng = 1\n";
327
+ assert_eq!(
328
+ show(src),
329
+ "{ f x = let { y = x ; z = y } in y + z ; g = 1 }"
330
+ );
331
+ }
332
+
333
+ #[test]
334
+ fn let_guards_close_at_comma_or_second_equals() {
335
+ assert_eq!(
336
+ show("f x\n | let y = x, y > 1 = y\n | let z = x = z\n"),
337
+ "{ f x | let { y = x } Comma y > 1 = y | let { z = x } = z }"
338
+ );
339
+ }
340
+
341
+ #[test]
342
+ fn guards_continue_an_equation() {
343
+ let src = "sign n\n | n > 0 = 1\n | otherwise = 0\n";
344
+ assert_eq!(show(src), "{ sign n | n > 0 = 1 | otherwise = 0 }");
345
+ }
346
+ }