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,256 @@
1
+ //! AST of the Haskell subset, plus rendering of patterns back to text (the
2
+ //! Ruby code generator hands patterns to the pattern compiler as strings).
3
+
4
+ use crate::core::ast::{Lit, RawPat};
5
+ use crate::core::parser::DataDecl;
6
+
7
+ #[derive(Clone, Debug, PartialEq)]
8
+ pub enum Literal {
9
+ Int(String),
10
+ Float(String),
11
+ Char(char),
12
+ Str(String),
13
+ }
14
+
15
+ #[derive(Clone, Debug, PartialEq)]
16
+ pub enum Expr {
17
+ Var(String),
18
+ Con(String),
19
+ Lit(Literal),
20
+ App(Box<Expr>, Vec<Expr>),
21
+ BinOp(String, Box<Expr>, Box<Expr>),
22
+ Neg(Box<Expr>),
23
+ If(Box<Expr>, Box<Expr>, Box<Expr>),
24
+ Case(Box<Expr>, Vec<Alt>),
25
+ Let(Vec<Decl>, Box<Expr>),
26
+ Lambda(Vec<RawPat>, Box<Expr>),
27
+ List(Vec<Expr>),
28
+ Tuple(Vec<Expr>),
29
+ Range {
30
+ from: Box<Expr>,
31
+ then: Option<Box<Expr>>,
32
+ to: Option<Box<Expr>>,
33
+ },
34
+ Comp(Box<Expr>, Vec<Qual>),
35
+ /// `(e op)`
36
+ SectionL(String, Box<Expr>),
37
+ /// `(op e)`
38
+ SectionR(String, Box<Expr>),
39
+ /// `(op)`
40
+ OpFun(String),
41
+ /// MultiWayIf: `if | quals -> e | quals -> e`
42
+ MultiIf(Vec<(Vec<Qual>, Expr)>),
43
+ /// Record construction `Con { f = e, ... }`
44
+ RecCon(String, Vec<(String, Expr)>),
45
+ /// Record update `e { f = e, ... }`
46
+ RecUpdate(Box<Expr>, Vec<(String, Expr)>),
47
+ }
48
+
49
+ #[derive(Clone, Debug, PartialEq)]
50
+ pub enum Qual {
51
+ Gen(RawPat, Expr),
52
+ Guard(Expr),
53
+ Let(Vec<Decl>),
54
+ }
55
+
56
+ /// A guarded right-hand side: each alternative is a list of qualifiers (a
57
+ /// boolean guard, a pattern guard `p <- e`, or `let` bindings) and a body.
58
+ #[derive(Clone, Debug, PartialEq)]
59
+ pub enum Rhs {
60
+ Plain(Expr),
61
+ Guarded(Vec<(Vec<Qual>, Expr)>),
62
+ }
63
+
64
+ #[derive(Clone, Debug, PartialEq)]
65
+ pub struct Alt {
66
+ pub pat: RawPat,
67
+ pub rhs: Rhs,
68
+ pub wheres: Vec<Decl>,
69
+ pub line: usize,
70
+ }
71
+
72
+ #[derive(Clone, Debug, PartialEq)]
73
+ pub struct Equation {
74
+ pub name: String,
75
+ pub pats: Vec<RawPat>,
76
+ pub rhs: Rhs,
77
+ pub wheres: Vec<Decl>,
78
+ pub line: usize,
79
+ }
80
+
81
+ #[derive(Clone, Debug, PartialEq)]
82
+ pub enum Decl {
83
+ Data {
84
+ decl: DataDecl,
85
+ deriving: Vec<String>,
86
+ line: usize,
87
+ },
88
+ Sig {
89
+ names: Vec<String>,
90
+ arity: usize,
91
+ line: usize,
92
+ },
93
+ Fun(Vec<Equation>),
94
+ PatBind {
95
+ pat: RawPat,
96
+ rhs: Rhs,
97
+ wheres: Vec<Decl>,
98
+ line: usize,
99
+ },
100
+ /// `import [qualified] M [as A] [hiding] [(items)]`
101
+ Import {
102
+ module: String,
103
+ qualified: bool,
104
+ alias: Option<String>,
105
+ hiding: bool,
106
+ /// Named items (functions, operators, types); `None` imports everything.
107
+ items: Option<Vec<String>>,
108
+ line: usize,
109
+ },
110
+ }
111
+
112
+ #[derive(Clone, Debug, PartialEq)]
113
+ pub struct Module {
114
+ pub name: Option<String>,
115
+ /// Names in the export list (functions, operators, types); `None` when
116
+ /// there is no list, meaning everything is exported.
117
+ pub exports: Option<Vec<String>>,
118
+ pub decls: Vec<Decl>,
119
+ }
120
+
121
+ /// Mangle a Haskell identifier into a name that is a valid Haskell *and* Ruby
122
+ /// identifier and cannot clash with Ruby keywords or methods: `x'` → `hs_x_q`.
123
+ pub fn mangle(name: &str) -> String {
124
+ let mut s = String::from("hs_");
125
+ for c in name.chars() {
126
+ if c == '\'' {
127
+ s.push_str("_q");
128
+ } else {
129
+ s.push(c);
130
+ }
131
+ }
132
+ s
133
+ }
134
+
135
+ /// Render a pattern as Haskell text with variables mangled, for the pattern
136
+ /// compiler.
137
+ pub fn render_pat(p: &RawPat) -> String {
138
+ render(p, false)
139
+ }
140
+
141
+ fn render(p: &RawPat, atomic: bool) -> String {
142
+ match p {
143
+ RawPat::Wild => "_".into(),
144
+ RawPat::Var(v) => mangle(v),
145
+ RawPat::As(v, inner) => format!("{}@{}", mangle(v), render(inner, true)),
146
+ RawPat::Lazy(inner) => format!("~{}", render(inner, true)),
147
+ RawPat::Bang(inner) => format!("!{}", render(inner, true)),
148
+ RawPat::Lit(l) => render_lit(l, atomic),
149
+ RawPat::Tuple(items) => format!(
150
+ "({})",
151
+ items
152
+ .iter()
153
+ .map(|i| render(i, false))
154
+ .collect::<Vec<_>>()
155
+ .join(", ")
156
+ ),
157
+ RawPat::List(items) => format!(
158
+ "[{}]",
159
+ items
160
+ .iter()
161
+ .map(|i| render(i, false))
162
+ .collect::<Vec<_>>()
163
+ .join(", ")
164
+ ),
165
+ RawPat::Cons(h, t) => format!("({}:{})", render(h, true), render(t, true)),
166
+ RawPat::Hash(fields) => format!(
167
+ "{{{}}}",
168
+ fields
169
+ .iter()
170
+ .map(|(k, p)| format!("{} = {}", k, render(p, false)))
171
+ .collect::<Vec<_>>()
172
+ .join(", ")
173
+ ),
174
+ RawPat::Con(name, args) if name.starts_with(':') => {
175
+ if args.len() == 2 {
176
+ format!(
177
+ "({} {} {})",
178
+ render(&args[0], true),
179
+ name,
180
+ render(&args[1], true)
181
+ )
182
+ } else if args.is_empty() {
183
+ format!("({})", name)
184
+ } else {
185
+ format!(
186
+ "(({}) {})",
187
+ name,
188
+ args.iter()
189
+ .map(|a| render(a, true))
190
+ .collect::<Vec<_>>()
191
+ .join(" ")
192
+ )
193
+ }
194
+ }
195
+ RawPat::Con(name, args) => {
196
+ if args.is_empty() {
197
+ name.clone()
198
+ } else {
199
+ let s = format!(
200
+ "{} {}",
201
+ name,
202
+ args.iter()
203
+ .map(|a| render(a, true))
204
+ .collect::<Vec<_>>()
205
+ .join(" ")
206
+ );
207
+ if atomic {
208
+ format!("({})", s)
209
+ } else {
210
+ s
211
+ }
212
+ }
213
+ }
214
+ RawPat::Record(name, fields, rest) => {
215
+ let mut parts: Vec<String> = fields
216
+ .iter()
217
+ .map(|(f, p)| format!("{} = {}", f, render(p, false)))
218
+ .collect();
219
+ if *rest {
220
+ parts.push("..".into());
221
+ }
222
+ format!("{} {{{}}}", name, parts.join(", "))
223
+ }
224
+ }
225
+ }
226
+
227
+ fn render_lit(l: &Lit, atomic: bool) -> String {
228
+ let s = l.to_string();
229
+ let negative = s.starts_with('-');
230
+ if negative && atomic {
231
+ format!("({})", s)
232
+ } else {
233
+ s
234
+ }
235
+ }
236
+
237
+ /// Variables bound by a pattern, in order of appearance (unmangled).
238
+ pub fn pat_vars(p: &RawPat, out: &mut Vec<String>) {
239
+ match p {
240
+ RawPat::Wild | RawPat::Lit(_) => {}
241
+ RawPat::Var(v) => out.push(v.clone()),
242
+ RawPat::As(v, inner) => {
243
+ out.push(v.clone());
244
+ pat_vars(inner, out);
245
+ }
246
+ RawPat::Lazy(inner) | RawPat::Bang(inner) => pat_vars(inner, out),
247
+ RawPat::Tuple(items) | RawPat::List(items) => items.iter().for_each(|i| pat_vars(i, out)),
248
+ RawPat::Cons(h, t) => {
249
+ pat_vars(h, out);
250
+ pat_vars(t, out);
251
+ }
252
+ RawPat::Con(_, args) => args.iter().for_each(|a| pat_vars(a, out)),
253
+ RawPat::Record(_, fields, _) => fields.iter().for_each(|(_, p)| pat_vars(p, out)),
254
+ RawPat::Hash(fields) => fields.iter().for_each(|(_, p)| pat_vars(p, out)),
255
+ }
256
+ }
@@ -0,0 +1,225 @@
1
+ //! JSON serialisation of the AST for the Ruby code generator.
2
+
3
+ use super::ast::*;
4
+ use crate::core::ast::RawPat;
5
+
6
+ fn esc(s: &str) -> String {
7
+ let mut out = String::with_capacity(s.len() + 2);
8
+ out.push('"');
9
+ for c in s.chars() {
10
+ match c {
11
+ '"' => out.push_str("\\\""),
12
+ '\\' => out.push_str("\\\\"),
13
+ '\n' => out.push_str("\\n"),
14
+ '\r' => out.push_str("\\r"),
15
+ '\t' => out.push_str("\\t"),
16
+ c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
17
+ c => out.push(c),
18
+ }
19
+ }
20
+ out.push('"');
21
+ out
22
+ }
23
+
24
+ fn arr(items: Vec<String>) -> String {
25
+ format!("[{}]", items.join(","))
26
+ }
27
+
28
+ fn pat(p: &RawPat) -> String {
29
+ let mut vars = Vec::new();
30
+ pat_vars(p, &mut vars);
31
+ format!(
32
+ "{{\"text\":{},\"vars\":{}}}",
33
+ esc(&render_pat(p)),
34
+ arr(vars.iter().map(|v| esc(v)).collect())
35
+ )
36
+ }
37
+
38
+ fn literal(l: &Literal) -> String {
39
+ match l {
40
+ Literal::Int(s) => format!("[\"int\",{}]", esc(s)),
41
+ Literal::Float(s) => format!("[\"float\",{}]", esc(s)),
42
+ Literal::Char(c) => format!("[\"char\",{}]", esc(&c.to_string())),
43
+ Literal::Str(s) => format!("[\"str\",{}]", esc(s)),
44
+ }
45
+ }
46
+
47
+ pub fn expr(e: &Expr) -> String {
48
+ match e {
49
+ Expr::Var(v) => format!("[\"var\",{}]", esc(v)),
50
+ Expr::RecCon(c, fields) => format!(
51
+ "[\"reccon\",{},{}]",
52
+ esc(c),
53
+ arr(fields.iter().map(field_bind).collect())
54
+ ),
55
+ Expr::RecUpdate(e, fields) => format!(
56
+ "[\"recupd\",{},{}]",
57
+ expr(e),
58
+ arr(fields.iter().map(field_bind).collect())
59
+ ),
60
+ Expr::Con(c) => format!("[\"con\",{}]", esc(c)),
61
+ Expr::Lit(l) => format!("[\"lit\",{}]", literal(l)),
62
+ Expr::App(f, args) => format!(
63
+ "[\"app\",{},{}]",
64
+ expr(f),
65
+ arr(args.iter().map(expr).collect())
66
+ ),
67
+ Expr::BinOp(op, l, r) => format!("[\"op\",{},{},{}]", esc(op), expr(l), expr(r)),
68
+ Expr::Neg(x) => format!("[\"neg\",{}]", expr(x)),
69
+ Expr::If(c, t, f) => format!("[\"if\",{},{},{}]", expr(c), expr(t), expr(f)),
70
+ Expr::Case(s, alts) => format!(
71
+ "[\"case\",{},{}]",
72
+ expr(s),
73
+ arr(alts.iter().map(alt).collect())
74
+ ),
75
+ Expr::Let(decls, body) => format!(
76
+ "[\"let\",{},{}]",
77
+ arr(decls.iter().map(decl).collect()),
78
+ expr(body)
79
+ ),
80
+ Expr::Lambda(pats, body) => format!(
81
+ "[\"lam\",{},{}]",
82
+ arr(pats.iter().map(pat).collect()),
83
+ expr(body)
84
+ ),
85
+ Expr::List(items) => format!("[\"list\",{}]", arr(items.iter().map(expr).collect())),
86
+ Expr::Tuple(items) => format!("[\"tuple\",{}]", arr(items.iter().map(expr).collect())),
87
+ Expr::Range { from, then, to } => format!(
88
+ "[\"range\",{},{},{}]",
89
+ expr(from),
90
+ then.as_ref()
91
+ .map(|t| expr(t))
92
+ .unwrap_or_else(|| "null".into()),
93
+ to.as_ref()
94
+ .map(|t| expr(t))
95
+ .unwrap_or_else(|| "null".into())
96
+ ),
97
+ Expr::Comp(body, quals) => format!(
98
+ "[\"comp\",{},{}]",
99
+ expr(body),
100
+ arr(quals.iter().map(qual).collect())
101
+ ),
102
+ Expr::SectionL(op, x) => format!("[\"section_l\",{},{}]", esc(op), expr(x)),
103
+ Expr::SectionR(op, x) => format!("[\"section_r\",{},{}]", esc(op), expr(x)),
104
+ Expr::OpFun(op) => format!("[\"opfun\",{}]", esc(op)),
105
+ Expr::MultiIf(arms) => format!(
106
+ "[\"multiif\",{}]",
107
+ arr(arms.iter().map(guarded_arm).collect())
108
+ ),
109
+ }
110
+ }
111
+
112
+ fn field_bind((f, e): &(String, Expr)) -> String {
113
+ format!("[{},{}]", esc(f), expr(e))
114
+ }
115
+
116
+ fn guarded_arm((quals, e): &(Vec<Qual>, Expr)) -> String {
117
+ format!("[{},{}]", arr(quals.iter().map(qual).collect()), expr(e))
118
+ }
119
+
120
+ fn qual(q: &Qual) -> String {
121
+ match q {
122
+ Qual::Gen(p, e) => format!("[\"gen\",{},{}]", pat(p), expr(e)),
123
+ Qual::Guard(e) => format!("[\"guard\",{}]", expr(e)),
124
+ Qual::Let(ds) => format!("[\"let\",{}]", arr(ds.iter().map(decl).collect())),
125
+ }
126
+ }
127
+
128
+ fn rhs(r: &Rhs) -> String {
129
+ match r {
130
+ Rhs::Plain(e) => format!("{{\"body\":{}}}", expr(e)),
131
+ Rhs::Guarded(gs) => format!(
132
+ "{{\"guards\":{}}}",
133
+ arr(gs.iter().map(guarded_arm).collect())
134
+ ),
135
+ }
136
+ }
137
+
138
+ fn alt(a: &Alt) -> String {
139
+ format!(
140
+ "{{\"pat\":{},\"rhs\":{},\"where\":{},\"line\":{}}}",
141
+ pat(&a.pat),
142
+ rhs(&a.rhs),
143
+ arr(a.wheres.iter().map(decl).collect()),
144
+ a.line
145
+ )
146
+ }
147
+
148
+ fn equation(e: &Equation) -> String {
149
+ format!(
150
+ "{{\"pats\":{},\"rhs\":{},\"where\":{},\"line\":{}}}",
151
+ arr(e.pats.iter().map(pat).collect()),
152
+ rhs(&e.rhs),
153
+ arr(e.wheres.iter().map(decl).collect()),
154
+ e.line
155
+ )
156
+ }
157
+
158
+ pub fn decl(d: &Decl) -> String {
159
+ match d {
160
+ Decl::Data { decl, deriving, line } => format!(
161
+ "{{\"kind\":\"data\",\"name\":{},\"tyvars\":{},\"cons\":{},\"deriving\":{},\"line\":{}}}",
162
+ esc(&decl.name),
163
+ arr(decl.tyvars.iter().map(|v| esc(v)).collect()),
164
+ arr(decl
165
+ .cons
166
+ .iter()
167
+ .map(|c| format!(
168
+ "{{\"name\":{},\"arity\":{},\"fields\":{}}}",
169
+ esc(&c.name),
170
+ c.arity,
171
+ c.fields.as_ref().map(|fs| arr(fs.iter().map(|f| esc(f)).collect())).unwrap_or_else(|| "null".into())
172
+ ))
173
+ .collect()),
174
+ arr(deriving.iter().map(|v| esc(v)).collect()),
175
+ line
176
+ ),
177
+ Decl::Sig { names, arity, line } => format!(
178
+ "{{\"kind\":\"sig\",\"names\":{},\"arity\":{},\"line\":{}}}",
179
+ arr(names.iter().map(|v| esc(v)).collect()),
180
+ arity,
181
+ line
182
+ ),
183
+ Decl::Fun(eqs) => format!(
184
+ "{{\"kind\":\"fun\",\"name\":{},\"arity\":{},\"equations\":{},\"line\":{}}}",
185
+ esc(&eqs[0].name),
186
+ eqs[0].pats.len(),
187
+ arr(eqs.iter().map(equation).collect()),
188
+ eqs[0].line
189
+ ),
190
+ Decl::PatBind { pat: p, rhs: r, wheres, line } => format!(
191
+ "{{\"kind\":\"bind\",\"pat\":{},\"rhs\":{},\"where\":{},\"line\":{}}}",
192
+ pat(p),
193
+ rhs(r),
194
+ arr(wheres.iter().map(decl).collect()),
195
+ line
196
+ ),
197
+ Decl::Import { module, qualified, alias, hiding, items, line } => format!(
198
+ "{{\"kind\":\"import\",\"module\":{},\"qualified\":{},\"as\":{},\"hiding\":{},\"items\":{},\"line\":{}}}",
199
+ esc(module),
200
+ qualified,
201
+ alias.as_ref().map(|a| esc(a)).unwrap_or_else(|| "null".into()),
202
+ hiding,
203
+ items
204
+ .as_ref()
205
+ .map(|is| arr(is.iter().map(|i| esc(i)).collect()))
206
+ .unwrap_or_else(|| "null".into()),
207
+ line
208
+ ),
209
+ }
210
+ }
211
+
212
+ pub fn module(m: &Module) -> String {
213
+ format!(
214
+ "{{\"name\":{},\"exports\":{},\"decls\":{}}}",
215
+ m.name
216
+ .as_ref()
217
+ .map(|n| esc(n))
218
+ .unwrap_or_else(|| "null".into()),
219
+ m.exports
220
+ .as_ref()
221
+ .map(|es| arr(es.iter().map(|e| esc(e)).collect()))
222
+ .unwrap_or_else(|| "null".into()),
223
+ arr(m.decls.iter().map(decl).collect())
224
+ )
225
+ }