From 1872d6ba12de0e73ef321e026f594d1780e3e084 Mon Sep 17 00:00:00 2001 From: Basile Henry Date: Sat, 17 Oct 2020 00:40:25 +0200 Subject: Implement Text/replace --- dhall/src/builtins.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) (limited to 'dhall/src/builtins.rs') diff --git a/dhall/src/builtins.rs b/dhall/src/builtins.rs index 16d656f..ed58e14 100644 --- a/dhall/src/builtins.rs +++ b/dhall/src/builtins.rs @@ -3,7 +3,8 @@ use std::convert::TryInto; use crate::operations::{BinOp, OpKind}; use crate::semantics::{ - skip_resolve_expr, typecheck, Hir, HirKind, Nir, NirKind, NzEnv, VarEnv, + nze, skip_resolve_expr, typecheck, Hir, HirKind, Nir, NirKind, NzEnv, + VarEnv, }; use crate::syntax::Const::Type; use crate::syntax::{ @@ -43,6 +44,7 @@ pub enum Builtin { ListIndexed, ListReverse, TextShow, + TextReplace, } impl Builtin { @@ -78,6 +80,7 @@ impl Builtin { "List/indexed" => Some(ListIndexed), "List/reverse" => Some(ListReverse), "Text/show" => Some(TextShow), + "Text/replace" => Some(TextReplace), _ => None, } } @@ -211,7 +214,12 @@ pub fn type_of_builtin(b: Builtin) -> Hir { DoubleShow => make_type!(Double -> Text), TextShow => make_type!(Text -> Text), - + TextReplace => make_type!( + forall (needle: Text) -> + forall (replacement: Text) -> + forall (haystack: Text) -> + Text + ), ListBuild => make_type!( forall (a: Type) -> (forall (list: Type) -> @@ -401,6 +409,57 @@ fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { } _ => Ret::DoneAsIs, }, + (Builtin::TextReplace, [needle, replacement, haystack]) => { + match (&*needle.kind(), &*haystack.kind()) { + (TextLit(n_lit), TextLit(h_lit)) => { + // The needle and the haystack need to be fully + // evaluated as Text otherwise no progress can be made + match (n_lit.as_text(), h_lit.as_text()) { + (Some(n), Some(h)) => { + // Helper to match a Nir as a text literal + fn nir_to_string(n: &Nir) -> Option { + match &*n.kind() { + TextLit(r_lit) => r_lit.as_text(), + _ => None, + } + } + + // When the needle is empty the haystack is returned untouched + if n.is_empty() { + Ret::Nir(haystack.clone()) + // Fast case when replacement is fully evaluated + } else if let Some(r) = nir_to_string(replacement) { + Ret::Nir(Nir::from_text(h.replace(&n, &r))) + } else { + use std::iter::{once, repeat}; + + let mut parts = h.split(&n).map(|s| { + InterpolatedTextContents::Text( + s.to_string(), + ) + }); + let replacement = + InterpolatedTextContents::Expr( + replacement.clone(), + ); + let first = parts.next(); + let rest = repeat(replacement) + .zip(parts) + .flat_map(|(r, p)| once(r).chain(once(p))); + + Ret::Nir(Nir::from_kind(NirKind::TextLit( + nze::nir::TextLit::new( + first.into_iter().chain(rest), + ), + ))) + } + } + _ => Ret::DoneAsIs, + } + } + _ => Ret::DoneAsIs, + } + } (Builtin::ListLength, [_, l]) => match &*l.kind() { EmptyListLit(_) => Ret::NirKind(Num(Natural(0))), NEListLit(xs) => Ret::NirKind(Num(Natural(xs.len()))), @@ -560,6 +619,7 @@ impl std::fmt::Display for Builtin { ListIndexed => "List/indexed", ListReverse => "List/reverse", TextShow => "Text/show", + TextReplace => "Text/replace", }) } } -- cgit v1.2.3