summaryrefslogtreecommitdiff
path: root/dhall/src
diff options
context:
space:
mode:
authorBasile Henry2020-10-17 00:40:25 +0200
committerBasile Henry2020-10-25 15:50:02 +0000
commit1872d6ba12de0e73ef321e026f594d1780e3e084 (patch)
tree521dd086ce5383a898db7ea585b0425df0b3629d /dhall/src
parentb657ae8db335d2c029b4c1fa10eb765b579d5d80 (diff)
Implement Text/replace
Diffstat (limited to 'dhall/src')
-rw-r--r--dhall/src/builtins.rs64
-rw-r--r--dhall/src/syntax/text/dhall.abnf2
2 files changed, 64 insertions, 2 deletions
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<Nir>, 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<String> {
+ 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",
})
}
}
diff --git a/dhall/src/syntax/text/dhall.abnf b/dhall/src/syntax/text/dhall.abnf
index 2d487f5..e913341 100644
--- a/dhall/src/syntax/text/dhall.abnf
+++ b/dhall/src/syntax/text/dhall.abnf
@@ -412,6 +412,7 @@ builtin =
/ List-indexed
/ List-reverse
/ Text-show
+ / Text-replace
/ Bool
/ True
/ False
@@ -464,6 +465,7 @@ List-last = %x4c.69.73.74.2f.6c.61.73.74
List-indexed = %x4c.69.73.74.2f.69.6e.64.65.78.65.64
List-reverse = %x4c.69.73.74.2f.72.65.76.65.72.73.65
Text-show = %x54.65.78.74.2f.73.68.6f.77
+Text-replace = %x54.65.78.74.2f.72.65.70.6c.61.63.65
; Operators
combine = %x2227 / "/\"