summaryrefslogtreecommitdiff
path: root/dhall
diff options
context:
space:
mode:
authorBasile Henry2020-10-25 18:30:57 +0100
committerGitHub2020-10-25 18:30:57 +0100
commit8f15b9a53f85ec1f806a36c5d1a0fe455e077e70 (patch)
tree32d311e0aff29e77b167c95a0051d916b2788c4c /dhall
parent49e202de4478c2933f9179ea23efc7d991c35381 (diff)
parent0f2522aadcc81481b14c8611db2777670a85ab32 (diff)
Merge pull request #181 from basile-henry/text-replace
Implement Text/replace
Diffstat (limited to 'dhall')
-rw-r--r--dhall/src/builtins.rs51
-rw-r--r--dhall/src/syntax/text/dhall.abnf2
-rw-r--r--dhall/tests/parser/success/builtinsB.txt2
-rw-r--r--dhall/tests/spec.rs11
4 files changed, 62 insertions, 4 deletions
diff --git a/dhall/src/builtins.rs b/dhall/src/builtins.rs
index 16d656f..e80bf6b 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,44 @@ fn apply_builtin(b: Builtin, args: Vec<Nir>, env: NzEnv) -> NirKind {
}
_ => Ret::DoneAsIs,
},
+ (Builtin::TextReplace, [needle, replacement, haystack]) => {
+ // Helper to match a Nir as a text literal
+ fn nir_to_string(n: &Nir) -> Option<String> {
+ match &*n.kind() {
+ TextLit(n_lit) => n_lit.as_text(),
+ _ => None,
+ }
+ }
+
+ // The needle and the haystack need to be fully
+ // evaluated as Text otherwise no progress can be made
+ match (nir_to_string(needle), nir_to_string(haystack)) {
+ (Some(n), Some(h)) => {
+ // 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 itertools::Itertools;
+
+ let parts = h.split(&n).map(|s| {
+ InterpolatedTextContents::Text(s.to_string())
+ });
+ let replacement =
+ InterpolatedTextContents::Expr(replacement.clone());
+
+ Ret::Nir(Nir::from_kind(NirKind::TextLit(
+ nze::nir::TextLit::new(
+ parts.intersperse(replacement),
+ ),
+ )))
+ }
+ }
+ _ => Ret::DoneAsIs,
+ }
+ }
(Builtin::ListLength, [_, l]) => match &*l.kind() {
EmptyListLit(_) => Ret::NirKind(Num(Natural(0))),
NEListLit(xs) => Ret::NirKind(Num(Natural(xs.len()))),
@@ -560,6 +606,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 / "/\"
diff --git a/dhall/tests/parser/success/builtinsB.txt b/dhall/tests/parser/success/builtinsB.txt
index 1005949..3d3e292 100644
--- a/dhall/tests/parser/success/builtinsB.txt
+++ b/dhall/tests/parser/success/builtinsB.txt
@@ -1 +1 @@
-[Natural/fold, Natural/build, Natural/isZero, Natural/even, Natural/odd, Natural/toInteger, Natural/show, Integer/toDouble, Integer/show, Integer/negate, Integer/clamp, Natural/subtract, Double/show, List/build, List/fold, List/length, List/head, List/last, List/indexed, List/reverse, Text/show, Bool, True, False, Optional, None, Natural, Integer, Double, Text, List, Type, Kind, Sort]
+[Natural/fold, Natural/build, Natural/isZero, Natural/even, Natural/odd, Natural/toInteger, Natural/show, Integer/toDouble, Integer/show, Integer/negate, Integer/clamp, Natural/subtract, Double/show, List/build, List/fold, List/length, List/head, List/last, List/indexed, List/reverse, Text/show, Text/replace, Bool, True, False, Optional, None, Natural, Integer, Double, Text, List, Type, Kind, Sort]
diff --git a/dhall/tests/spec.rs b/dhall/tests/spec.rs
index 9572d52..8d67892 100644
--- a/dhall/tests/spec.rs
+++ b/dhall/tests/spec.rs
@@ -519,6 +519,10 @@ fn define_features() -> Vec<TestFeature> {
|| path == "simple/integerToDouble"
// TODO: fix Double/show
|| path == "prelude/JSON/number/1"
+ // With builtin not implemented yet
+ || path == "unit/WithCreateIntermediateRecords"
+ || path == "unit/WithDesugar"
+ || path == "unit/WithPartiallyAbstract"
}),
..default_feature.clone()
},
@@ -538,7 +542,12 @@ fn define_features() -> Vec<TestFeature> {
variant: SpecTestKind::TypeInferenceSuccess,
// TODO: this fails because of caching shenanigans
// too_slow_path: Rc::new(|path: &str| path == "prelude"),
- exclude_path: Rc::new(|path: &str| path == "prelude"),
+ exclude_path: Rc::new(|path: &str| {
+ false
+ || path == "prelude"
+ // With builtin not implemented yet
+ || path == "unit/WithCreateIntermediateRecords"
+ }),
..default_feature.clone()
},
TestFeature {