summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dhall_core/src/import.rs38
-rw-r--r--dhall_core/src/label.rs38
-rw-r--r--dhall_core/src/printer.rs329
-rw-r--r--dhall_core/src/text.rs92
4 files changed, 497 insertions, 0 deletions
diff --git a/dhall_core/src/import.rs b/dhall_core/src/import.rs
new file mode 100644
index 0000000..3e2fbe8
--- /dev/null
+++ b/dhall_core/src/import.rs
@@ -0,0 +1,38 @@
+use std::path::PathBuf;
+
+/// The beginning of a file path which anchors subsequent path components
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum FilePrefix {
+ /// Absolute path
+ Absolute,
+ /// Path relative to .
+ Here,
+ /// Path relative to ..
+ Parent,
+ /// Path relative to ~
+ Home,
+}
+
+/// The location of import (i.e. local vs. remote vs. environment)
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ImportLocation {
+ Local(FilePrefix, PathBuf),
+ // TODO: other import types
+}
+
+/// How to interpret the import's contents (i.e. as Dhall code or raw text)
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ImportMode {
+ Code,
+ // TODO
+ // RawText,
+}
+
+/// Reference to an external resource
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Import {
+ pub mode: ImportMode,
+ pub location: ImportLocation,
+ // TODO
+ pub hash: Option<()>,
+}
diff --git a/dhall_core/src/label.rs b/dhall_core/src/label.rs
new file mode 100644
index 0000000..3633b93
--- /dev/null
+++ b/dhall_core/src/label.rs
@@ -0,0 +1,38 @@
+use std::fmt::{self, Display};
+use std::rc::Rc;
+
+// The type for labels throughout the AST
+// It owns the data because otherwise lifetimes would make recursive imports impossible
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct Label(Rc<str>);
+
+impl From<String> for Label {
+ fn from(s: String) -> Self {
+ let s: &str = &s;
+ Label(s.into())
+ }
+}
+
+impl<'a> From<&'a str> for Label {
+ fn from(s: &'a str) -> Self {
+ Label(Rc::from(s))
+ }
+}
+
+impl From<Label> for String {
+ fn from(x: Label) -> String {
+ x.0.as_ref().to_owned()
+ }
+}
+
+impl Display for Label {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ self.0.as_ref().fmt(f)
+ }
+}
+
+impl Label {
+ pub fn from_str(s: &str) -> Label {
+ Label(s.into())
+ }
+}
diff --git a/dhall_core/src/printer.rs b/dhall_core/src/printer.rs
new file mode 100644
index 0000000..b72f32b
--- /dev/null
+++ b/dhall_core/src/printer.rs
@@ -0,0 +1,329 @@
+use crate::*;
+use std::fmt::{self, Display};
+
+// There used to be a one-to-one correspondence between the formatters in this section
+// and the grammar. Each formatter is named after the
+// corresponding grammar rule and the relationship between formatters exactly matches
+// the relationship between grammar rules. This leads to the nice emergent property
+// of automatically getting all the parentheses and precedences right.
+//
+// WARNING: This approach has one major disadvantage: you can get an infinite loop if
+// you add a new constructor to the syntax tree without adding a matching
+// case the corresponding builder.
+
+impl<S, A: Display> Display for Expr<S, A> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ // buildExprA
+ use crate::Expr::*;
+ match self {
+ &Annot(ref a, ref b) => {
+ a.fmt_b(f)?;
+ write!(f, " : ")?;
+ b.fmt(f)
+ }
+ &Note(_, ref b) => b.fmt(f),
+ a => a.fmt_b(f),
+ }
+ }
+}
+
+impl<S, A: Display> Expr<S, A> {
+ fn fmt_b(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ use crate::Expr::*;
+ match self {
+ &Lam(ref a, ref b, ref c) => {
+ write!(f, "λ({} : ", a)?;
+ b.fmt(f)?;
+ write!(f, ") → ")?;
+ c.fmt_b(f)
+ }
+ &BoolIf(ref a, ref b, ref c) => {
+ write!(f, "if ")?;
+ a.fmt(f)?;
+ write!(f, " then ")?;
+ b.fmt_b(f)?;
+ write!(f, " else ")?;
+ c.fmt_c(f)
+ }
+ // TODO: wait for decision on label types
+ // &Pi("_", ref b, ref c) => {
+ // b.fmt_c(f)?;
+ // write!(f, " → ")?;
+ // c.fmt_b(f)
+ // }
+ &Pi(ref a, ref b, ref c) => {
+ write!(f, "∀({} : ", a)?;
+ b.fmt(f)?;
+ write!(f, ") → ")?;
+ c.fmt_b(f)
+ }
+ &Let(ref a, None, ref c, ref d) => {
+ write!(f, "let {} = ", a)?;
+ c.fmt(f)?;
+ write!(f, " in ")?;
+ d.fmt_b(f)
+ }
+ &Let(ref a, Some(ref b), ref c, ref d) => {
+ write!(f, "let {} : ", a)?;
+ b.fmt(f)?;
+ write!(f, " = ")?;
+ c.fmt(f)?;
+ write!(f, " in ")?;
+ d.fmt_b(f)
+ }
+ &EmptyListLit(ref t) => {
+ write!(f, "[] : List ")?;
+ t.fmt_e(f)
+ }
+ &NEListLit(ref es) => {
+ fmt_list("[", ", ", "]", es, f, |e, f| e.fmt(f))
+ }
+ &EmptyOptionalLit(ref t) => {
+ write!(f, "None ")?;
+ t.fmt_e(f)?;
+ Ok(())
+ }
+ &NEOptionalLit(ref e) => {
+ write!(f, "Some ")?;
+ e.fmt_e(f)?;
+ Ok(())
+ }
+ &Merge(ref a, ref b, ref c) => {
+ write!(f, "merge ")?;
+ a.fmt_e(f)?;
+ write!(f, " ")?;
+ b.fmt_e(f)?;
+ match c {
+ Some(c) => {
+ write!(f, " : ")?;
+ c.fmt_d(f)?
+ }
+ None => {}
+ }
+ Ok(())
+ }
+ &Note(_, ref b) => b.fmt_b(f),
+ a => a.fmt_c(f),
+ }
+ }
+
+ fn fmt_c(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ use crate::BinOp::*;
+ use crate::Expr::*;
+ match self {
+ // FIXME precedence
+ &BinOp(op, ref a, ref b) => {
+ write!(f, "(")?;
+ a.fmt_d(f)?;
+ write!(
+ f,
+ " {} ",
+ match op {
+ BoolOr => "||",
+ TextAppend => "++",
+ NaturalPlus => "+",
+ BoolAnd => "&&",
+ Combine => "/\\",
+ NaturalTimes => "*",
+ BoolEQ => "==",
+ BoolNE => "!=",
+ CombineTypes => "//\\\\",
+ ImportAlt => "?",
+ Prefer => "//",
+ ListAppend => "#",
+ }
+ )?;
+ b.fmt_c(f)?;
+ write!(f, ")")
+ }
+ &Note(_, ref b) => b.fmt_c(f),
+ a => a.fmt_d(f),
+ }
+ }
+
+ fn fmt_d(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ use crate::Expr::*;
+ match self {
+ &App(ref a, ref args) => {
+ a.fmt_d(f)?;
+ for x in args {
+ f.write_str(" ")?;
+ x.fmt_e(f)?;
+ }
+ Ok(())
+ }
+ &Note(_, ref b) => b.fmt_d(f),
+ a => a.fmt_e(f),
+ }
+ }
+
+ fn fmt_e(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ use crate::Expr::*;
+ match self {
+ &Field(ref a, ref b) => {
+ a.fmt_e(f)?;
+ write!(f, ".{}", b)
+ }
+ &Note(_, ref b) => b.fmt_e(f),
+ a => a.fmt_f(f),
+ }
+ }
+
+ fn fmt_f(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ use crate::Expr::*;
+ match &self {
+ &Var(a) => a.fmt(f),
+ &Const(k) => k.fmt(f),
+ &Builtin(v) => v.fmt(f),
+ &BoolLit(true) => f.write_str("True"),
+ &BoolLit(false) => f.write_str("False"),
+ &NaturalLit(a) => a.fmt(f),
+ &IntegerLit(a) if *a >= 0 => {
+ f.write_str("+")?;
+ a.fmt(f)
+ }
+ &IntegerLit(a) => a.fmt(f),
+ &DoubleLit(a) => a.fmt(f),
+ &TextLit(ref a) => {
+ f.write_str("\"")?;
+ for x in a.iter() {
+ match x {
+ InterpolatedTextContents::Text(a) => {
+ // TODO Format all escapes properly
+ f.write_str(
+ &a.replace("\n", "\\n")
+ .replace("\t", "\\t")
+ .replace("\r", "\\r")
+ .replace("\"", "\\\""),
+ )?;
+ }
+ InterpolatedTextContents::Expr(e) => {
+ f.write_str("${ ")?;
+ e.fmt(f)?;
+ f.write_str(" }")?;
+ }
+ }
+ }
+ f.write_str("\"")?;
+ Ok(())
+ }
+ &RecordType(ref a) if a.is_empty() => f.write_str("{}"),
+ &RecordType(ref a) => {
+ fmt_list("{ ", ", ", " }", a, f, |(k, t), f| {
+ write!(f, "{} : {}", k, t)
+ })
+ }
+ &RecordLit(ref a) if a.is_empty() => f.write_str("{=}"),
+ &RecordLit(ref a) => {
+ fmt_list("{ ", ", ", " }", a, f, |(k, v), f| {
+ write!(f, "{} = {}", k, v)
+ })
+ }
+ &UnionType(ref a) => {
+ fmt_list("< ", " | ", " >", a, f, |(k, v), f| {
+ write!(f, "{} : {}", k, v)
+ })
+ }
+ &UnionLit(ref a, ref b, ref c) => {
+ f.write_str("< ")?;
+ write!(f, "{} = {}", a, b)?;
+ for (k, v) in c {
+ f.write_str(" | ")?;
+ write!(f, "{} : {}", k, v)?;
+ }
+ f.write_str(" >")
+ }
+ &Embed(ref a) => a.fmt(f),
+ &Note(_, ref b) => b.fmt_f(f),
+ a => write!(f, "({})", a),
+ }
+ }
+}
+
+fn fmt_list<T, I, F>(
+ open: &str,
+ sep: &str,
+ close: &str,
+ it: I,
+ f: &mut fmt::Formatter,
+ func: F,
+) -> Result<(), fmt::Error>
+where
+ I: IntoIterator<Item = T>,
+ F: Fn(T, &mut fmt::Formatter) -> Result<(), fmt::Error>,
+{
+ f.write_str(open)?;
+ for (i, x) in it.into_iter().enumerate() {
+ if i > 0 {
+ f.write_str(sep)?;
+ }
+ func(x, f)?;
+ }
+ f.write_str(close)
+}
+
+impl Display for Const {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ <Self as fmt::Debug>::fmt(self, f)
+ }
+}
+
+impl Display for Import {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ <Self as fmt::Debug>::fmt(self, f)
+ }
+}
+
+impl Display for Builtin {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ use crate::Builtin::*;
+ f.write_str(match *self {
+ Bool => "Bool",
+ Natural => "Natural",
+ Integer => "Integer",
+ Double => "Double",
+ Text => "Text",
+ List => "List",
+ Optional => "Optional",
+ OptionalSome => "Some",
+ OptionalNone => "None",
+ NaturalBuild => "Natural/build",
+ NaturalFold => "Natural/fold",
+ NaturalIsZero => "Natural/isZero",
+ NaturalEven => "Natural/even",
+ NaturalOdd => "Natural/odd",
+ NaturalToInteger => "Natural/toInteger",
+ NaturalShow => "Natural/show",
+ IntegerToDouble => "Integer/toDouble",
+ IntegerShow => "Integer/show",
+ DoubleShow => "Double/show",
+ ListBuild => "List/build",
+ ListFold => "List/fold",
+ ListLength => "List/length",
+ ListHead => "List/head",
+ ListLast => "List/last",
+ ListIndexed => "List/indexed",
+ ListReverse => "List/reverse",
+ OptionalFold => "Optional/fold",
+ OptionalBuild => "Optional/build",
+ TextShow => "Text/show",
+ })
+ }
+}
+
+impl Display for V {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ let V(ref x, ref n) = *self;
+ x.fmt(f)?;
+ if *n != 0 {
+ write!(f, "@{}", n)?;
+ }
+ Ok(())
+ }
+}
+
+impl Display for X {
+ fn fmt(&self, _: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ match *self {}
+ }
+}
diff --git a/dhall_core/src/text.rs b/dhall_core/src/text.rs
new file mode 100644
index 0000000..cd5fef3
--- /dev/null
+++ b/dhall_core/src/text.rs
@@ -0,0 +1,92 @@
+use crate::*;
+use std::iter::FromIterator;
+use std::ops::Add;
+use std::rc::Rc;
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct InterpolatedText<Note, Embed> {
+ head: String,
+ tail: Vec<(Rc<Expr<Note, Embed>>, String)>,
+}
+
+impl<N, E> From<(String, Vec<(Rc<Expr<N, E>>, String)>)>
+ for InterpolatedText<N, E>
+{
+ fn from(x: (String, Vec<(Rc<Expr<N, E>>, String)>)) -> Self {
+ InterpolatedText {
+ head: x.0,
+ tail: x.1,
+ }
+ }
+}
+
+impl<N, E> From<String> for InterpolatedText<N, E> {
+ fn from(s: String) -> Self {
+ InterpolatedText {
+ head: s,
+ tail: vec![],
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum InterpolatedTextContents<'a, Note, Embed> {
+ Text(&'a str),
+ Expr(SubExpr<Note, Embed>),
+}
+
+impl<N, E> InterpolatedText<N, E> {
+ pub fn map<N2, E2, F>(&self, mut f: F) -> InterpolatedText<N2, E2>
+ where
+ F: FnMut(&Rc<Expr<N, E>>) -> Rc<Expr<N2, E2>>,
+ {
+ InterpolatedText {
+ head: self.head.clone(),
+ tail: self.tail.iter().map(|(e, s)| (f(e), s.clone())).collect(),
+ }
+ }
+
+ pub fn iter(&self) -> impl Iterator<Item = InterpolatedTextContents<N, E>> {
+ use std::iter::once;
+ once(InterpolatedTextContents::Text(self.head.as_ref())).chain(
+ self.tail.iter().flat_map(|(e, s)| {
+ once(InterpolatedTextContents::Expr(Rc::clone(e)))
+ .chain(once(InterpolatedTextContents::Text(s)))
+ }),
+ )
+ }
+}
+
+impl<'a, N: 'a, E: 'a> FromIterator<InterpolatedTextContents<'a, N, E>>
+ for InterpolatedText<N, E>
+{
+ fn from_iter<T>(iter: T) -> Self
+ where
+ T: IntoIterator<Item = InterpolatedTextContents<'a, N, E>>,
+ {
+ 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 {
+ InterpolatedTextContents::Text(s) => crnt_str.push_str(s),
+ InterpolatedTextContents::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<N, E> Add for &InterpolatedText<N, E> {
+ type Output = InterpolatedText<N, E>;
+ fn add(self, rhs: &InterpolatedText<N, E>) -> Self::Output {
+ self.iter().chain(rhs.iter()).collect()
+ }
+}