1use std::collections::{BTreeMap, HashMap};
2use std::convert::TryInto;
3
4use crate::operations::{BinOp, OpKind};
5use crate::semantics::{nze, Hir, HirKind, Nir, NirKind, NzEnv, VarEnv};
6use crate::syntax::Const::Type;
7use crate::syntax::{
8 Const, Expr, ExprKind, InterpolatedText, InterpolatedTextContents, Label,
9 NaiveDouble, NumKind, Span, UnspannedExpr, V,
10};
11use crate::{Ctxt, Parsed};
12
13#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
15pub enum Builtin {
16 Bool,
17 Natural,
18 Integer,
19 Double,
20 Text,
21 List,
22 Optional,
23 OptionalNone,
24 NaturalBuild,
25 NaturalFold,
26 NaturalIsZero,
27 NaturalEven,
28 NaturalOdd,
29 NaturalToInteger,
30 NaturalShow,
31 NaturalSubtract,
32 IntegerToDouble,
33 IntegerShow,
34 IntegerNegate,
35 IntegerClamp,
36 DoubleShow,
37 ListBuild,
38 ListFold,
39 ListLength,
40 ListHead,
41 ListLast,
42 ListIndexed,
43 ListReverse,
44 TextShow,
45 TextReplace,
46}
47
48impl Builtin {
49 pub fn parse(s: &str) -> Option<Self> {
50 use Builtin::*;
51 match s {
52 "Bool" => Some(Bool),
53 "Natural" => Some(Natural),
54 "Integer" => Some(Integer),
55 "Double" => Some(Double),
56 "Text" => Some(Text),
57 "List" => Some(List),
58 "Optional" => Some(Optional),
59 "None" => Some(OptionalNone),
60 "Natural/build" => Some(NaturalBuild),
61 "Natural/fold" => Some(NaturalFold),
62 "Natural/isZero" => Some(NaturalIsZero),
63 "Natural/even" => Some(NaturalEven),
64 "Natural/odd" => Some(NaturalOdd),
65 "Natural/toInteger" => Some(NaturalToInteger),
66 "Natural/show" => Some(NaturalShow),
67 "Natural/subtract" => Some(NaturalSubtract),
68 "Integer/toDouble" => Some(IntegerToDouble),
69 "Integer/show" => Some(IntegerShow),
70 "Integer/negate" => Some(IntegerNegate),
71 "Integer/clamp" => Some(IntegerClamp),
72 "Double/show" => Some(DoubleShow),
73 "List/build" => Some(ListBuild),
74 "List/fold" => Some(ListFold),
75 "List/length" => Some(ListLength),
76 "List/head" => Some(ListHead),
77 "List/last" => Some(ListLast),
78 "List/indexed" => Some(ListIndexed),
79 "List/reverse" => Some(ListReverse),
80 "Text/show" => Some(TextShow),
81 "Text/replace" => Some(TextReplace),
82 _ => None,
83 }
84 }
85}
86
87#[derive(Debug, Clone)]
90pub struct BuiltinClosure<'cx> {
91 env: NzEnv<'cx>,
92 b: Builtin,
93 args: Vec<Nir<'cx>>,
95}
96
97impl<'cx> BuiltinClosure<'cx> {
98 pub fn new(b: Builtin, env: NzEnv<'cx>) -> NirKind<'cx> {
99 apply_builtin(b, Vec::new(), env)
100 }
101 pub fn apply(&self, a: Nir<'cx>) -> NirKind<'cx> {
102 use std::iter::once;
103 let args = self.args.iter().cloned().chain(once(a)).collect();
104 apply_builtin(self.b, args, self.env.clone())
105 }
106 pub fn to_hirkind(&self, venv: VarEnv) -> HirKind<'cx> {
107 HirKind::Expr(self.args.iter().fold(
108 ExprKind::Builtin(self.b),
109 |acc, v| {
110 ExprKind::Op(OpKind::App(
111 Hir::new(HirKind::Expr(acc), Span::Artificial),
112 v.to_hir(venv),
113 ))
114 },
115 ))
116 }
117}
118
119pub fn rc(x: UnspannedExpr) -> Expr {
120 Expr::new(x, Span::Artificial)
121}
122
123macro_rules! make_type {
125 (Type) => { rc(ExprKind::Const(Const::Type)) };
126 (Bool) => { rc(ExprKind::Builtin(Builtin::Bool)) };
127 (Natural) => { rc(ExprKind::Builtin(Builtin::Natural)) };
128 (Integer) => { rc(ExprKind::Builtin(Builtin::Integer)) };
129 (Double) => { rc(ExprKind::Builtin(Builtin::Double)) };
130 (Text) => { rc(ExprKind::Builtin(Builtin::Text)) };
131 ($var:ident) => {
132 rc(ExprKind::Var(V(stringify!($var).into(), 0)))
133 };
134 (Optional $ty:ident) => {
135 rc(ExprKind::Op(OpKind::App(
136 rc(ExprKind::Builtin(Builtin::Optional)),
137 make_type!($ty)
138 )))
139 };
140 (List $($rest:tt)*) => {
141 rc(ExprKind::Op(OpKind::App(
142 rc(ExprKind::Builtin(Builtin::List)),
143 make_type!($($rest)*)
144 )))
145 };
146 ({ $($label:ident : $ty:ident),* }) => {{
147 let mut kts = BTreeMap::new();
148 $(
149 kts.insert(
150 Label::from(stringify!($label)),
151 make_type!($ty),
152 );
153 )*
154 rc(ExprKind::RecordType(kts))
155 }};
156 ($ty:ident -> $($rest:tt)*) => {
157 rc(ExprKind::Pi(
158 "_".into(),
159 make_type!($ty),
160 make_type!($($rest)*)
161 ))
162 };
163 (($($arg:tt)*) -> $($rest:tt)*) => {
164 rc(ExprKind::Pi(
165 "_".into(),
166 make_type!($($arg)*),
167 make_type!($($rest)*)
168 ))
169 };
170 (forall ($var:ident : $($ty:tt)*) -> $($rest:tt)*) => {
171 rc(ExprKind::Pi(
172 stringify!($var).into(),
173 make_type!($($ty)*),
174 make_type!($($rest)*)
175 ))
176 };
177}
178
179pub fn type_of_builtin<'cx>(cx: Ctxt<'cx>, b: Builtin) -> Hir<'cx> {
180 use Builtin::*;
181 let expr = match b {
182 Bool | Natural | Integer | Double | Text => make_type!(Type),
183 List | Optional => make_type!(
184 Type -> Type
185 ),
186
187 NaturalFold => make_type!(
188 Natural ->
189 forall (natural: Type) ->
190 forall (succ: natural -> natural) ->
191 forall (zero: natural) ->
192 natural
193 ),
194 NaturalBuild => make_type!(
195 (forall (natural: Type) ->
196 forall (succ: natural -> natural) ->
197 forall (zero: natural) ->
198 natural) ->
199 Natural
200 ),
201 NaturalIsZero | NaturalEven | NaturalOdd => make_type!(
202 Natural -> Bool
203 ),
204 NaturalToInteger => make_type!(Natural -> Integer),
205 NaturalShow => make_type!(Natural -> Text),
206 NaturalSubtract => make_type!(Natural -> Natural -> Natural),
207
208 IntegerToDouble => make_type!(Integer -> Double),
209 IntegerShow => make_type!(Integer -> Text),
210 IntegerNegate => make_type!(Integer -> Integer),
211 IntegerClamp => make_type!(Integer -> Natural),
212
213 DoubleShow => make_type!(Double -> Text),
214 TextShow => make_type!(Text -> Text),
215 TextReplace => make_type!(
216 forall (needle: Text) ->
217 forall (replacement: Text) ->
218 forall (haystack: Text) ->
219 Text
220 ),
221 ListBuild => make_type!(
222 forall (a: Type) ->
223 (forall (list: Type) ->
224 forall (cons: a -> list -> list) ->
225 forall (nil: list) ->
226 list) ->
227 List a
228 ),
229 ListFold => make_type!(
230 forall (a: Type) ->
231 (List a) ->
232 forall (list: Type) ->
233 forall (cons: a -> list -> list) ->
234 forall (nil: list) ->
235 list
236 ),
237 ListLength => make_type!(forall (a: Type) -> (List a) -> Natural),
238 ListHead | ListLast => {
239 make_type!(forall (a: Type) -> (List a) -> Optional a)
240 }
241 ListIndexed => make_type!(
242 forall (a: Type) ->
243 (List a) ->
244 List { index: Natural, value: a }
245 ),
246 ListReverse => make_type!(
247 forall (a: Type) -> (List a) -> List a
248 ),
249
250 OptionalNone => make_type!(
251 forall (A: Type) -> Optional A
252 ),
253 };
254 Parsed::from_expr_without_imports(expr)
255 .resolve(cx)
256 .unwrap()
257 .0
258}
259
260macro_rules! make_closure {
262 (var($var:ident)) => {{
263 rc(ExprKind::Var(V(
264 Label::from(stringify!($var)).into(),
265 0
266 )))
267 }};
268 (λ($var:tt : $($ty:tt)*) -> $($body:tt)*) => {{
269 let var = Label::from(stringify!($var));
270 let ty = make_closure!($($ty)*);
271 let body = make_closure!($($body)*);
272 rc(ExprKind::Lam(var, ty, body))
273 }};
274 (Type) => {
275 rc(ExprKind::Const(Type))
276 };
277 (Natural) => {
278 rc(ExprKind::Builtin(Builtin::Natural))
279 };
280 (List $($ty:tt)*) => {{
281 let ty = make_closure!($($ty)*);
282 rc(ExprKind::Op(OpKind::App(
283 rc(ExprKind::Builtin(Builtin::List)),
284 ty
285 )))
286 }};
287 (Some($($v:tt)*)) => {
288 rc(ExprKind::SomeLit(
289 make_closure!($($v)*)
290 ))
291 };
292 (1 + $($v:tt)*) => {
293 rc(ExprKind::Op(OpKind::BinOp(
294 BinOp::NaturalPlus,
295 make_closure!($($v)*),
296 rc(ExprKind::Num(NumKind::Natural(1)))
297 )))
298 };
299 ([ $($head:tt)* ] # $($tail:tt)*) => {{
300 let head = make_closure!($($head)*);
301 let tail = make_closure!($($tail)*);
302 rc(ExprKind::Op(OpKind::BinOp(
303 BinOp::ListAppend,
304 rc(ExprKind::NEListLit(vec![head])),
305 tail,
306 )))
307 }};
308}
309
310#[allow(clippy::cognitive_complexity)]
311fn apply_builtin<'cx>(
312 b: Builtin,
313 args: Vec<Nir<'cx>>,
314 env: NzEnv<'cx>,
315) -> NirKind<'cx> {
316 let cx = env.cx();
317 use NirKind::*;
318 use NumKind::{Bool, Double, Integer, Natural};
319
320 enum Ret<'cx> {
322 NirKind(NirKind<'cx>),
323 Nir(Nir<'cx>),
324 DoneAsIs,
325 }
326 let make_closure = |e| {
327 Parsed::from_expr_without_imports(e)
328 .resolve(cx)
329 .unwrap()
330 .typecheck(cx)
331 .unwrap()
332 .as_hir()
333 .eval(env.clone())
334 };
335
336 let ret = match (b, args.as_slice()) {
337 (Builtin::Bool, [])
338 | (Builtin::Natural, [])
339 | (Builtin::Integer, [])
340 | (Builtin::Double, [])
341 | (Builtin::Text, []) => Ret::NirKind(BuiltinType(b)),
342 (Builtin::Optional, [t]) => Ret::NirKind(OptionalType(t.clone())),
343 (Builtin::List, [t]) => Ret::NirKind(ListType(t.clone())),
344
345 (Builtin::OptionalNone, [t]) => {
346 Ret::NirKind(EmptyOptionalLit(t.clone()))
347 }
348 (Builtin::NaturalIsZero, [n]) => match &*n.kind() {
349 Num(Natural(n)) => Ret::NirKind(Num(Bool(*n == 0))),
350 _ => Ret::DoneAsIs,
351 },
352 (Builtin::NaturalEven, [n]) => match &*n.kind() {
353 Num(Natural(n)) => Ret::NirKind(Num(Bool(*n % 2 == 0))),
354 _ => Ret::DoneAsIs,
355 },
356 (Builtin::NaturalOdd, [n]) => match &*n.kind() {
357 Num(Natural(n)) => Ret::NirKind(Num(Bool(*n % 2 != 0))),
358 _ => Ret::DoneAsIs,
359 },
360 (Builtin::NaturalToInteger, [n]) => match &*n.kind() {
361 Num(Natural(n)) => Ret::NirKind(Num(Integer(*n as i64))),
362 _ => Ret::DoneAsIs,
363 },
364 (Builtin::NaturalShow, [n]) => match &*n.kind() {
365 Num(Natural(n)) => Ret::Nir(Nir::from_text(n)),
366 _ => Ret::DoneAsIs,
367 },
368 (Builtin::NaturalSubtract, [a, b]) => match (&*a.kind(), &*b.kind()) {
369 (Num(Natural(a)), Num(Natural(b))) => {
370 Ret::NirKind(Num(Natural(if b > a { b - a } else { 0 })))
371 }
372 (Num(Natural(0)), _) => Ret::Nir(b.clone()),
373 (_, Num(Natural(0))) => Ret::NirKind(Num(Natural(0))),
374 _ if a == b => Ret::NirKind(Num(Natural(0))),
375 _ => Ret::DoneAsIs,
376 },
377 (Builtin::IntegerShow, [n]) => match &*n.kind() {
378 Num(Integer(n)) => {
379 let s = if *n < 0 {
380 n.to_string()
381 } else {
382 format!("+{}", n)
383 };
384 Ret::Nir(Nir::from_text(s))
385 }
386 _ => Ret::DoneAsIs,
387 },
388 (Builtin::IntegerToDouble, [n]) => match &*n.kind() {
389 Num(Integer(n)) => {
390 Ret::NirKind(Num(Double(NaiveDouble::from(*n as f64))))
391 }
392 _ => Ret::DoneAsIs,
393 },
394 (Builtin::IntegerNegate, [n]) => match &*n.kind() {
395 Num(Integer(n)) => Ret::NirKind(Num(Integer(-n))),
396 _ => Ret::DoneAsIs,
397 },
398 (Builtin::IntegerClamp, [n]) => match &*n.kind() {
399 Num(Integer(n)) => {
400 Ret::NirKind(Num(Natural((*n).try_into().unwrap_or(0))))
401 }
402 _ => Ret::DoneAsIs,
403 },
404 (Builtin::DoubleShow, [n]) => match &*n.kind() {
405 Num(Double(n)) => Ret::Nir(Nir::from_text(n)),
406 _ => Ret::DoneAsIs,
407 },
408 (Builtin::TextShow, [v]) => match &*v.kind() {
409 TextLit(tlit) => {
410 if let Some(s) = tlit.as_text() {
411 let txt: InterpolatedText<Expr> =
413 std::iter::once(InterpolatedTextContents::Text(s))
414 .collect();
415 Ret::Nir(Nir::from_text(txt))
416 } else {
417 Ret::DoneAsIs
418 }
419 }
420 _ => Ret::DoneAsIs,
421 },
422 (Builtin::TextReplace, [needle, replacement, haystack]) => {
423 fn nir_to_string(n: &Nir) -> Option<String> {
425 match &*n.kind() {
426 TextLit(n_lit) => n_lit.as_text(),
427 _ => None,
428 }
429 }
430
431 match nir_to_string(needle) {
434 Some(n) if n.is_empty() => Ret::Nir(haystack.clone()),
436 Some(n) => {
437 if let Some(h) = nir_to_string(haystack) {
440 if let Some(r) = nir_to_string(replacement) {
442 Ret::Nir(Nir::from_text(h.replace(&n, &r)))
443 } else {
444 use itertools::Itertools;
445
446 let parts = h.split(&n).map(|s| {
447 InterpolatedTextContents::Text(s.to_string())
448 });
449 let replacement = InterpolatedTextContents::Expr(
450 replacement.clone(),
451 );
452
453 Ret::Nir(Nir::from_kind(NirKind::TextLit(
454 nze::nir::TextLit::new(Itertools::intersperse(
455 parts,
456 replacement,
457 )),
458 )))
459 }
460 } else {
461 Ret::DoneAsIs
462 }
463 }
464 _ => Ret::DoneAsIs,
465 }
466 }
467 (Builtin::ListLength, [_, l]) => match &*l.kind() {
468 EmptyListLit(_) => Ret::NirKind(Num(Natural(0))),
469 NEListLit(xs) => Ret::NirKind(Num(Natural(xs.len() as u64))),
470 _ => Ret::DoneAsIs,
471 },
472 (Builtin::ListHead, [_, l]) => match &*l.kind() {
473 EmptyListLit(n) => Ret::NirKind(EmptyOptionalLit(n.clone())),
474 NEListLit(xs) => {
475 Ret::NirKind(NEOptionalLit(xs.iter().next().unwrap().clone()))
476 }
477 _ => Ret::DoneAsIs,
478 },
479 (Builtin::ListLast, [_, l]) => match &*l.kind() {
480 EmptyListLit(n) => Ret::NirKind(EmptyOptionalLit(n.clone())),
481 NEListLit(xs) => Ret::NirKind(NEOptionalLit(
482 xs.iter().rev().next().unwrap().clone(),
483 )),
484 _ => Ret::DoneAsIs,
485 },
486 (Builtin::ListReverse, [_, l]) => match &*l.kind() {
487 EmptyListLit(n) => Ret::NirKind(EmptyListLit(n.clone())),
488 NEListLit(xs) => {
489 Ret::NirKind(NEListLit(xs.iter().rev().cloned().collect()))
490 }
491 _ => Ret::DoneAsIs,
492 },
493 (Builtin::ListIndexed, [t, l]) => {
494 match l.kind() {
495 EmptyListLit(_) | NEListLit(_) => {
496 let mut kts = HashMap::new();
498 kts.insert(
499 "index".into(),
500 Nir::from_builtin(cx, Builtin::Natural),
501 );
502 kts.insert("value".into(), t.clone());
503 let t = Nir::from_kind(RecordType(kts));
504
505 let list = match l.kind() {
507 EmptyListLit(_) => EmptyListLit(t),
508 NEListLit(xs) => NEListLit(
509 xs.iter()
510 .enumerate()
511 .map(|(i, e)| {
512 let mut kvs = HashMap::new();
513 kvs.insert(
514 "index".into(),
515 Nir::from_kind(Num(Natural(i as u64))),
516 );
517 kvs.insert("value".into(), e.clone());
518 Nir::from_kind(RecordLit(kvs))
519 })
520 .collect(),
521 ),
522 _ => unreachable!(),
523 };
524 Ret::NirKind(list)
525 }
526 _ => Ret::DoneAsIs,
527 }
528 }
529 (Builtin::ListBuild, [t, f]) => {
530 let list_t = Nir::from_builtin(cx, Builtin::List).app(t.clone());
531 Ret::Nir(
532 f.app(list_t)
533 .app(
534 make_closure(make_closure!(
535 λ(T : Type) ->
536 λ(a : var(T)) ->
537 λ(as : List var(T)) ->
538 [ var(a) ] # var(as)
539 ))
540 .app(t.clone()),
541 )
542 .app(EmptyListLit(t.clone()).into_nir()),
543 )
544 }
545 (Builtin::ListFold, [_, l, _, cons, nil]) => match &*l.kind() {
546 EmptyListLit(_) => Ret::Nir(nil.clone()),
547 NEListLit(xs) => {
548 let mut v = nil.clone();
549 for x in xs.iter().cloned().rev() {
550 v = cons.app(x).app(v);
551 }
552 Ret::Nir(v)
553 }
554 _ => Ret::DoneAsIs,
555 },
556 (Builtin::NaturalBuild, [f]) => Ret::Nir(
557 f.app(Nir::from_builtin(cx, Builtin::Natural))
558 .app(make_closure(make_closure!(
559 λ(x : Natural) ->
560 1 + var(x)
561 )))
562 .app(Num(Natural(0)).into_nir()),
563 ),
564
565 (Builtin::NaturalFold, [n, t, succ, zero]) => match &*n.kind() {
566 Num(Natural(0)) => Ret::Nir(zero.clone()),
567 Num(Natural(n)) => {
568 let fold = Nir::from_builtin(cx, Builtin::NaturalFold)
569 .app(Num(Natural(n - 1)).into_nir())
570 .app(t.clone())
571 .app(succ.clone())
572 .app(zero.clone());
573 Ret::Nir(succ.app(fold))
574 }
575 _ => Ret::DoneAsIs,
576 },
577 _ => Ret::DoneAsIs,
578 };
579 match ret {
580 Ret::NirKind(v) => v,
581 Ret::Nir(v) => v.kind().clone(),
582 Ret::DoneAsIs => AppliedBuiltin(BuiltinClosure { b, args, env }),
583 }
584}
585
586impl<'cx> std::cmp::PartialEq for BuiltinClosure<'cx> {
587 fn eq(&self, other: &Self) -> bool {
588 self.b == other.b && self.args == other.args
589 }
590}
591impl<'cx> std::cmp::Eq for BuiltinClosure<'cx> {}
592
593impl std::fmt::Display for Builtin {
594 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
595 use Builtin::*;
596 f.write_str(match *self {
597 Bool => "Bool",
598 Natural => "Natural",
599 Integer => "Integer",
600 Double => "Double",
601 Text => "Text",
602 List => "List",
603 Optional => "Optional",
604 OptionalNone => "None",
605 NaturalBuild => "Natural/build",
606 NaturalFold => "Natural/fold",
607 NaturalIsZero => "Natural/isZero",
608 NaturalEven => "Natural/even",
609 NaturalOdd => "Natural/odd",
610 NaturalToInteger => "Natural/toInteger",
611 NaturalShow => "Natural/show",
612 NaturalSubtract => "Natural/subtract",
613 IntegerToDouble => "Integer/toDouble",
614 IntegerNegate => "Integer/negate",
615 IntegerClamp => "Integer/clamp",
616 IntegerShow => "Integer/show",
617 DoubleShow => "Double/show",
618 ListBuild => "List/build",
619 ListFold => "List/fold",
620 ListLength => "List/length",
621 ListHead => "List/head",
622 ListLast => "List/last",
623 ListIndexed => "List/indexed",
624 ListReverse => "List/reverse",
625 TextShow => "Text/show",
626 TextReplace => "Text/replace",
627 })
628 }
629}