From bc1c40d670de0e37edf525fccd13a837b5983e7e Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 14 Mar 2019 21:53:07 +0100 Subject: Handle and parse interpolated strings Closes #25 --- dhall_core/src/core.rs | 140 ++++++++++++++++++++++++++++++++++++++++++----- dhall_core/src/parser.rs | 43 +++++++++------ 2 files changed, 154 insertions(+), 29 deletions(-) (limited to 'dhall_core') diff --git a/dhall_core/src/core.rs b/dhall_core/src/core.rs index b3ba142..8ce9715 100644 --- a/dhall_core/src/core.rs +++ b/dhall_core/src/core.rs @@ -1,6 +1,8 @@ #![allow(non_snake_case)] use std::collections::BTreeMap; use std::fmt::{self, Display}; +use std::iter::FromIterator; +use std::ops::Add; use std::path::PathBuf; use std::rc::Rc; @@ -171,6 +173,112 @@ pub enum BinOp { ListAppend, } +#[derive(Debug, Clone, PartialEq)] +pub struct InterpolatedText { + head: String, + tail: Vec<(Expr, String)>, +} + +impl From<(String, Vec<(Expr, String)>)> + for InterpolatedText +{ + fn from(x: (String, Vec<(Expr, String)>)) -> Self { + InterpolatedText { + head: x.0, + tail: x.1, + } + } +} + +impl From for InterpolatedText { + fn from(s: String) -> Self { + InterpolatedText { + head: s, + tail: vec![], + } + } +} + +// TODO: merge both when we move to Rc<> +// This one is needed when parsing, because we need to own the Expr +pub enum OwnedInterpolatedTextContents<'a, Note, Embed> { + Text(&'a str), + Expr(Expr), +} + +// This one is needed everywhere else, because we don't want Clone traits bounds +// everywhere +pub enum BorrowedInterpolatedTextContents<'a, Note, Embed> { + Text(&'a str), + Expr(&'a Expr), +} + +impl<'a, N: Clone + 'a, E: Clone + 'a> BorrowedInterpolatedTextContents<'a, N, E> { + pub fn to_owned(self) -> OwnedInterpolatedTextContents<'a, N, E> { + match self { + BorrowedInterpolatedTextContents::Text(s) => OwnedInterpolatedTextContents::Text(s), + BorrowedInterpolatedTextContents::Expr(e) => OwnedInterpolatedTextContents::Expr(e.clone()), + } + } +} + +impl InterpolatedText { + pub fn map(&self, mut f: F) -> InterpolatedText + where + F: FnMut(&Expr) -> Expr, + { + InterpolatedText { + head: self.head.clone(), + tail: self.tail.iter().map(|(e, s)| (f(e), s.clone())).collect(), + } + } + + pub fn iter(&self) -> impl Iterator> { + use std::iter::once; + once(BorrowedInterpolatedTextContents::Text(self.head.as_ref())).chain( + self.tail.iter().flat_map(|(e, s)| { + once(BorrowedInterpolatedTextContents::Expr(e)) + .chain(once(BorrowedInterpolatedTextContents::Text(s))) + }), + ) + } +} + +impl<'a, N: Clone + 'a, E: Clone + 'a> + FromIterator> + for InterpolatedText +{ + fn from_iter(iter: T) -> Self + where + T: IntoIterator>, + { + let mut res = InterpolatedText { + head: "".to_owned(), + tail: vec![], + }; + // let mut empty_string = "".to_owned(); + let mut crnt_str = &mut res.head; + for x in iter.into_iter() { + match x { + OwnedInterpolatedTextContents::Text(s) => crnt_str.push_str(s), + OwnedInterpolatedTextContents::Expr(e) => { + // crnt_str = &mut empty_string; + res.tail.push((e.clone(), "".to_owned())); + crnt_str = &mut res.tail.last_mut().unwrap().1; + } + } + } + res + } +} + +impl Add for InterpolatedText { + type Output = InterpolatedText; + fn add(self, rhs: InterpolatedText) -> Self::Output { + self.iter().chain(rhs.iter()).map(BorrowedInterpolatedTextContents::to_owned).collect() + } +} + /// Syntax tree for expressions #[derive(Debug, Clone, PartialEq)] pub enum Expr { @@ -215,7 +323,7 @@ pub enum Expr { /// `DoubleLit n ~ n` DoubleLit(Double), /// `TextLit t ~ t` - TextLit(Builder), + TextLit(InterpolatedText), /// `ListLit t [x, y, z] ~ [x, y, z] : List t` ListLit(Option>>, Vec>), /// `OptionalLit t [e] ~ [e] : Optional t` @@ -358,13 +466,6 @@ impl Expr { _ => None, } } - - pub fn text_lit(&self) -> Option { - match *self { - Expr::TextLit(ref t) => Some(t.clone()), // FIXME? - _ => None, - } - } } impl Expr> { @@ -570,7 +671,21 @@ impl Expr { a.fmt(f) } &DoubleLit(a) => a.fmt(f), - &TextLit(ref a) => ::fmt(a, f), // FIXME Format with Haskell escapes + &TextLit(ref a) => { + for x in a.iter() { + match x { + BorrowedInterpolatedTextContents::Text(a) => { + ::fmt(a, f)? + } // TODO Format escapes properly + BorrowedInterpolatedTextContents::Expr(e) => { + f.write_str("${")?; + e.fmt(f)?; + f.write_str("}")?; + } + } + } + Ok(()) + } &Record(ref a) if a.is_empty() => f.write_str("{}"), &Record(ref a) => fmt_list("{ ", " }", a, f, |(k, t), f| { write!(f, "{} : {}", k, t) @@ -724,7 +839,6 @@ where Expr::App(bx(f.into()), bx(x.into())) } -pub type Builder = String; pub type Double = f64; pub type Int = isize; pub type Integer = isize; @@ -788,7 +902,7 @@ where NaturalLit(n) => NaturalLit(n), IntegerLit(n) => IntegerLit(n), DoubleLit(n) => DoubleLit(n), - TextLit(ref t) => TextLit(t.clone()), + TextLit(ref t) => TextLit(t.map(|e| map(e))), BinOp(o, ref x, ref y) => BinOp(o, bxmap(x), bxmap(y)), ListLit(ref t, ref es) => { let es = es.iter().map(&map).collect(); @@ -972,7 +1086,7 @@ pub fn shift(d: isize, v: &V, e: &Expr) -> Expr { NaturalLit(a) => NaturalLit(*a), IntegerLit(a) => IntegerLit(*a), DoubleLit(a) => DoubleLit(*a), - TextLit(a) => TextLit(a.clone()), + TextLit(a) => TextLit(a.map(|e| shift(d, v, e))), ListLit(t, es) => ListLit( t.as_ref().map(|t| bx(shift(d, v, t))), es.iter().map(|e| shift(d, v, e)).collect(), @@ -1075,7 +1189,7 @@ where NaturalLit(a) => NaturalLit(*a), IntegerLit(a) => IntegerLit(*a), DoubleLit(a) => DoubleLit(*a), - TextLit(a) => TextLit(a.clone()), + TextLit(a) => TextLit(a.map(|b| subst(v, e, b))), ListLit(a, b) => { let a2 = a.as_ref().map(|a| bx(subst(v, e, a))); let b2 = b.iter().map(|be| subst(v, e, be)).collect(); diff --git a/dhall_core/src/parser.rs b/dhall_core/src/parser.rs index 57dd151..ddf3f8f 100644 --- a/dhall_core/src/parser.rs +++ b/dhall_core/src/parser.rs @@ -14,6 +14,8 @@ use crate::core::*; // are here and hopefully you can figure out how they work. pub type ParsedExpr = Expr; +pub type ParsedText = InterpolatedText; +pub type ParsedTextContents<'a> = OwnedInterpolatedTextContents<'a, X, Import>; pub type BoxExpr = Box; pub type ParseError = pest::error::Error; @@ -426,17 +428,25 @@ named!(raw_str<&'a str>; captured_str!(s) => s); named!(label