diff options
author | Nadrieril | 2019-05-04 12:38:36 +0200 |
---|---|---|
committer | Nadrieril | 2019-05-04 12:38:36 +0200 |
commit | 45be2ff1f5bb3d6e0faa098402adf985b3d5e7ca (patch) | |
tree | a7b15b0bf4b5b211eb42a559bc3cfde9a14b7749 /dhall_core | |
parent | ca4e2b39c838cde6da835470699579e8eddc1535 (diff) |
Rename dhall_core to dhall_syntax
Diffstat (limited to 'dhall_core')
-rw-r--r-- | dhall_core/Cargo.toml | 16 | ||||
-rw-r--r-- | dhall_core/src/context.rs | 80 | ||||
-rw-r--r-- | dhall_core/src/core.rs | 563 | ||||
-rw-r--r-- | dhall_core/src/import.rs | 64 | ||||
-rw-r--r-- | dhall_core/src/label.rs | 34 | ||||
-rw-r--r-- | dhall_core/src/lib.rs | 28 | ||||
-rw-r--r-- | dhall_core/src/parser.rs | 984 | ||||
-rw-r--r-- | dhall_core/src/printer.rs | 498 | ||||
-rw-r--r-- | dhall_core/src/text.rs | 116 | ||||
-rw-r--r-- | dhall_core/src/visitor.rs | 667 |
10 files changed, 0 insertions, 3050 deletions
diff --git a/dhall_core/Cargo.toml b/dhall_core/Cargo.toml deleted file mode 100644 index 476b9fa..0000000 --- a/dhall_core/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "dhall_core" -version = "0.1.0" -authors = ["NanoTech <nanotech@nanotechcorp.net>", "Nadrieril <nadrieril@users.noreply.github.com>"] -license = "BSD-2-Clause" -edition = "2018" - -[lib] -doctest = false - -[dependencies] -itertools = "0.8.0" -percent-encoding = "1.0.1" -pest = "2.1" -dhall_generated_parser = { path = "../dhall_generated_parser" } -improved_slice_patterns = { version = "2.0.0", path = "../improved_slice_patterns" } diff --git a/dhall_core/src/context.rs b/dhall_core/src/context.rs deleted file mode 100644 index 55bfff5..0000000 --- a/dhall_core/src/context.rs +++ /dev/null @@ -1,80 +0,0 @@ -use std::cmp::Eq; -use std::collections::HashMap; -use std::hash::Hash; - -/// A `(Context a)` associates `Text` labels with values of type `a` -/// -/// The `Context` is used for type-checking when `(a = Expr X)` -/// -/// * You create a `Context` using `empty` and `insert` -/// * You transform a `Context` using `fmap` -/// * You consume a `Context` using `lookup` and `toList` -/// -/// The difference between a `Context` and a `Map` is that a `Context` lets you -/// have multiple ordered occurrences of the same key and you can query for the -/// `n`th occurrence of a given key. -/// -#[derive(Debug, Clone)] -pub struct Context<K: Eq + Hash, T>(HashMap<K, Vec<T>>); - -impl<K: Hash + Eq + Clone, T> Context<K, T> { - /// An empty context with no key-value pairs - pub fn new() -> Self { - Context(HashMap::new()) - } - - /// Look up a key by name and index - /// - /// ```c - /// lookup _ _ empty = Nothing - /// lookup k 0 (insert k v c) = Just v - /// lookup k n (insert k v c) = lookup k (n - 1) c -- 1 <= n - /// lookup k n (insert j v c) = lookup k n c -- k /= j - /// ``` - pub fn lookup<'a>(&'a self, k: &K, n: usize) -> Option<&'a T> { - self.0.get(k).and_then(|v| { - if n < v.len() { - v.get(v.len() - 1 - n) - } else { - None - } - }) - } - - pub fn map<U, F: Fn(&K, &T) -> U>(&self, f: F) -> Context<K, U> { - Context( - self.0 - .iter() - .map(|(k, vs)| { - ((*k).clone(), vs.iter().map(|v| f(k, v)).collect()) - }) - .collect(), - ) - } - - pub fn lookup_all<'a>(&'a self, k: &K) -> impl Iterator<Item = &T> { - self.0.get(k).into_iter().flat_map(|v| v.iter()) - } - - pub fn iter<'a>(&'a self) -> impl Iterator<Item = (&K, &T)> { - self.0 - .iter() - .flat_map(|(k, vs)| vs.iter().map(move |v| (k, v))) - } - - pub fn iter_keys<'a>(&'a self) -> impl Iterator<Item = (&K, &Vec<T>)> { - self.0.iter() - } -} - -impl<K: Hash + Eq + Clone, T: Clone> Context<K, T> { - /// Add a key-value pair to the `Context` - pub fn insert(&self, k: K, v: T) -> Self { - let mut ctx = (*self).clone(); - { - let m = ctx.0.entry(k).or_insert_with(Vec::new); - m.push(v); - } - ctx - } -} diff --git a/dhall_core/src/core.rs b/dhall_core/src/core.rs deleted file mode 100644 index 3db07dd..0000000 --- a/dhall_core/src/core.rs +++ /dev/null @@ -1,563 +0,0 @@ -#![allow(non_snake_case)] -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::rc::Rc; - -use crate::context::Context; -use crate::visitor; -use crate::*; - -pub type Integer = isize; -pub type Natural = usize; -pub type Double = NaiveDouble; - -/// An empty type -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum X {} - -pub fn trivial_result<T>(x: Result<T, X>) -> T { - match x { - Ok(x) => x, - Err(e) => match e {}, - } -} - -/// Double with bitwise equality -#[derive(Debug, Copy, Clone)] -pub struct NaiveDouble(f64); - -impl PartialEq for NaiveDouble { - fn eq(&self, other: &Self) -> bool { - self.0.to_bits() == other.0.to_bits() - } -} - -impl Eq for NaiveDouble {} - -impl From<f64> for NaiveDouble { - fn from(x: f64) -> Self { - NaiveDouble(x) - } -} - -impl From<NaiveDouble> for f64 { - fn from(x: NaiveDouble) -> f64 { - x.0 - } -} - -/// Constants for a pure type system -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Const { - Type, - Kind, - Sort, -} - -/// Bound variable -/// -/// The `Label` field is the variable's name (i.e. \"`x`\"). -/// The `Int` field is a DeBruijn index. -/// See dhall-lang/standard/semantics.md for details -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct V<Label>(pub Label, pub usize); - -// This is only for the specific `Label` type, not generic -impl From<Label> for V<Label> { - fn from(x: Label) -> V<Label> { - V(x, 0) - } -} -impl<'a> From<&'a Label> for V<Label> { - fn from(x: &'a Label) -> V<Label> { - V(x.clone(), 0) - } -} - -// Definition order must match precedence order for -// pretty-printing to work correctly -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum BinOp { - /// x ? y - ImportAlt, - /// x || y` - BoolOr, - /// x + y` - NaturalPlus, - /// x ++ y` - TextAppend, - /// x # y - ListAppend, - /// x && y` - BoolAnd, - /// x ∧ y` - Combine, - /// x // y - Prefer, - /// x //\\ y - CombineTypes, - /// x * y` - NaturalTimes, - /// x == y` - BoolEQ, - /// x != y` - BoolNE, -} - -/// Built-ins -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Builtin { - Bool, - Natural, - Integer, - Double, - Text, - List, - Optional, - OptionalNone, - NaturalBuild, - NaturalFold, - NaturalIsZero, - NaturalEven, - NaturalOdd, - NaturalToInteger, - NaturalShow, - IntegerToDouble, - IntegerShow, - DoubleShow, - ListBuild, - ListFold, - ListLength, - ListHead, - ListLast, - ListIndexed, - ListReverse, - OptionalFold, - OptionalBuild, - TextShow, -} - -pub type ParsedExpr = SubExpr<X, Import>; -pub type ResolvedExpr = SubExpr<X, X>; -pub type DhallExpr = ResolvedExpr; - -#[derive(Debug, PartialEq, Eq)] -pub struct SubExpr<Note, Embed>(pub Rc<Expr<Note, Embed>>); - -pub type Expr<Note, Embed> = ExprF<SubExpr<Note, Embed>, Label, Note, Embed>; - -/// Syntax tree for expressions -// Having the recursion out of the enum definition enables writing -// much more generic code and improves pattern-matching behind -// smart pointers. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ExprF<SubExpr, Label, Note, Embed> { - Const(Const), - /// `x` - /// `x@n` - Var(V<Label>), - /// `λ(x : A) -> b` - Lam(Label, SubExpr, SubExpr), - /// `A -> B` - /// `∀(x : A) -> B` - Pi(Label, SubExpr, SubExpr), - /// `f a` - App(SubExpr, SubExpr), - /// `let x = r in e` - /// `let x : t = r in e` - Let(Label, Option<SubExpr>, SubExpr, SubExpr), - /// `x : t` - Annot(SubExpr, SubExpr), - /// Built-in values - Builtin(Builtin), - // Binary operations - BinOp(BinOp, SubExpr, SubExpr), - /// `True` - BoolLit(bool), - /// `if x then y else z` - BoolIf(SubExpr, SubExpr, SubExpr), - /// `1` - NaturalLit(Natural), - /// `+2` - IntegerLit(Integer), - /// `3.24` - DoubleLit(Double), - /// `"Some ${interpolated} text"` - TextLit(InterpolatedText<SubExpr>), - /// `[] : List t` - EmptyListLit(SubExpr), - /// `[x, y, z]` - NEListLit(Vec<SubExpr>), - /// Deprecated Optional literal form - /// `[] : Optional a` - /// `[x] : Optional a` - OldOptionalLit(Option<SubExpr>, SubExpr), - /// `Some e` - SomeLit(SubExpr), - /// `{ k1 : t1, k2 : t1 }` - RecordType(BTreeMap<Label, SubExpr>), - /// `{ k1 = v1, k2 = v2 }` - RecordLit(BTreeMap<Label, SubExpr>), - /// `< k1 : t1, k2 >` - UnionType(BTreeMap<Label, Option<SubExpr>>), - /// `< k1 = t1, k2 : t2, k3 >` - UnionLit(Label, SubExpr, BTreeMap<Label, Option<SubExpr>>), - /// `merge x y : t` - Merge(SubExpr, SubExpr, Option<SubExpr>), - /// `e.x` - Field(SubExpr, Label), - /// `e.{ x, y, z }` - Projection(SubExpr, Vec<Label>), - /// Annotation on the AST. Unused for now but could hold e.g. file location information - Note(Note, SubExpr), - /// Embeds an import or the result of resolving the import - Embed(Embed), -} - -impl<SE, L, N, E> ExprF<SE, L, N, E> { - pub(crate) fn visit<'a, V, Return>(&'a self, v: V) -> Return - where - V: visitor::GenericVisitor<&'a ExprF<SE, L, N, E>, Return>, - { - v.visit(self) - } - - fn traverse_ref_with_special_handling_of_binders<'a, SE2, L2, N2, E2, Err>( - &'a self, - visit_subexpr: impl FnMut(&'a SE) -> Result<SE2, Err>, - visit_under_binder: impl FnOnce(&'a L, &'a SE) -> Result<SE2, Err>, - visit_note: impl FnOnce(&'a N) -> Result<N2, Err>, - visit_embed: impl FnOnce(&'a E) -> Result<E2, Err>, - visit_label: impl FnMut(&'a L) -> Result<L2, Err>, - ) -> Result<ExprF<SE2, L2, N2, E2>, Err> - where - L: Ord, - L2: Ord, - { - self.visit(visitor::TraverseRefWithBindersVisitor { - visit_subexpr, - visit_under_binder, - visit_note, - visit_embed, - visit_label, - }) - } - - fn traverse_ref<'a, SE2, L2, N2, E2, Err>( - &'a self, - visit_subexpr: impl FnMut(&'a SE) -> Result<SE2, Err>, - visit_note: impl FnOnce(&'a N) -> Result<N2, Err>, - visit_embed: impl FnOnce(&'a E) -> Result<E2, Err>, - visit_label: impl FnMut(&'a L) -> Result<L2, Err>, - ) -> Result<ExprF<SE2, L2, N2, E2>, Err> - where - L: Ord, - L2: Ord, - { - self.visit(visitor::TraverseRefVisitor { - visit_subexpr, - visit_note, - visit_embed, - visit_label, - }) - } - - pub fn map_ref_with_special_handling_of_binders<'a, SE2, L2, N2, E2>( - &'a self, - mut map_subexpr: impl FnMut(&'a SE) -> SE2, - mut map_under_binder: impl FnMut(&'a L, &'a SE) -> SE2, - map_note: impl FnOnce(&'a N) -> N2, - map_embed: impl FnOnce(&'a E) -> E2, - mut map_label: impl FnMut(&'a L) -> L2, - ) -> ExprF<SE2, L2, N2, E2> - where - L: Ord, - L2: Ord, - { - trivial_result(self.traverse_ref_with_special_handling_of_binders( - |x| Ok(map_subexpr(x)), - |l, x| Ok(map_under_binder(l, x)), - |x| Ok(map_note(x)), - |x| Ok(map_embed(x)), - |x| Ok(map_label(x)), - )) - } - - pub fn map_ref<'a, SE2, L2, N2, E2>( - &'a self, - mut map_subexpr: impl FnMut(&'a SE) -> SE2, - map_note: impl FnOnce(&'a N) -> N2, - map_embed: impl FnOnce(&'a E) -> E2, - mut map_label: impl FnMut(&'a L) -> L2, - ) -> ExprF<SE2, L2, N2, E2> - where - L: Ord, - L2: Ord, - { - trivial_result(self.traverse_ref( - |x| Ok(map_subexpr(x)), - |x| Ok(map_note(x)), - |x| Ok(map_embed(x)), - |x| Ok(map_label(x)), - )) - } - - pub fn traverse_ref_simple<'a, SE2, Err>( - &'a self, - visit_subexpr: impl FnMut(&'a SE) -> Result<SE2, Err>, - ) -> Result<ExprF<SE2, L, N, E>, Err> - where - L: Ord + Clone, - N: Clone, - E: Clone, - { - self.traverse_ref( - visit_subexpr, - |x| Ok(N::clone(x)), - |x| Ok(E::clone(x)), - |x| Ok(L::clone(x)), - ) - } - - pub fn map_ref_simple<'a, SE2>( - &'a self, - map_subexpr: impl Fn(&'a SE) -> SE2, - ) -> ExprF<SE2, L, N, E> - where - L: Ord + Clone, - N: Clone, - E: Clone, - { - self.map_ref(map_subexpr, N::clone, E::clone, L::clone) - } -} - -impl<N, E> Expr<N, E> { - fn traverse_embed<E2, Err>( - &self, - visit_embed: impl FnMut(&E) -> Result<E2, Err>, - ) -> Result<Expr<N, E2>, Err> - where - N: Clone, - { - self.visit(&mut visitor::TraverseEmbedVisitor(visit_embed)) - } - - fn map_embed<E2>(&self, mut map_embed: impl FnMut(&E) -> E2) -> Expr<N, E2> - where - N: Clone, - { - trivial_result(self.traverse_embed(|x| Ok(map_embed(x)))) - } - - pub fn roll(&self) -> SubExpr<N, E> - where - N: Clone, - E: Clone, - { - rc(ExprF::clone(self)) - } - - pub fn squash_embed<E2>( - &self, - f: impl FnMut(&E) -> SubExpr<N, E2>, - ) -> SubExpr<N, E2> - where - N: Clone, - { - trivial_result(self.visit(&mut visitor::SquashEmbedVisitor(f))) - } -} - -impl<E: Clone> Expr<X, E> { - pub fn note_absurd<N>(&self) -> Expr<N, E> { - self.visit(&mut visitor::NoteAbsurdVisitor) - } -} - -impl<N: Clone> Expr<N, X> { - pub fn embed_absurd<E>(&self) -> Expr<N, E> { - self.visit(&mut visitor::EmbedAbsurdVisitor) - } -} - -impl<N, E> SubExpr<N, E> { - pub fn as_ref(&self) -> &Expr<N, E> { - self.0.as_ref() - } - - pub fn traverse_embed<E2, Err>( - &self, - visit_embed: impl FnMut(&E) -> Result<E2, Err>, - ) -> Result<SubExpr<N, E2>, Err> - where - N: Clone, - { - Ok(rc(self.as_ref().traverse_embed(visit_embed)?)) - } - - pub fn map_embed<E2>( - &self, - map_embed: impl FnMut(&E) -> E2, - ) -> SubExpr<N, E2> - where - N: Clone, - { - rc(self.as_ref().map_embed(map_embed)) - } - - pub fn map_subexprs_with_special_handling_of_binders<'a>( - &'a self, - map_expr: impl FnMut(&'a Self) -> Self, - map_under_binder: impl FnMut(&'a Label, &'a Self) -> Self, - ) -> Self { - match self.as_ref() { - ExprF::Embed(_) => SubExpr::clone(self), - // Recursive call - // TODO: don't discard the note ! - ExprF::Note(_, e) => e - .map_subexprs_with_special_handling_of_binders( - map_expr, - map_under_binder, - ), - // Call ExprF::map_ref - e => rc(e.map_ref_with_special_handling_of_binders( - map_expr, - map_under_binder, - |_| unreachable!(), - |_| unreachable!(), - Label::clone, - )), - } - } - - pub fn unroll(&self) -> Expr<N, E> - where - N: Clone, - E: Clone, - { - ExprF::clone(self.as_ref()) - } - - pub fn unnote(&self) -> SubExpr<X, E> - where - E: Clone, - { - rc(self.as_ref().visit(&mut visitor::UnNoteVisitor)) - } - - /// `shift` is used by both normalization and type-checking to avoid variable - /// capture by shifting variable indices - /// See https://github.com/dhall-lang/dhall-lang/blob/master/standard/semantics.md#shift - /// for details - pub fn shift(&self, delta: isize, var: &V<Label>) -> Self { - match self.as_ref() { - ExprF::Var(v) => rc(ExprF::Var(v.shift(delta, var))), - _ => self.map_subexprs_with_special_handling_of_binders( - |e| e.shift(delta, var), - |x: &Label, e| e.shift(delta, &var.shift(1, &x.into())), - ), - } - } - - pub fn subst_shift(&self, var: &V<Label>, val: &Self) -> Self { - match self.as_ref() { - ExprF::Var(v) if v == var => val.clone(), - ExprF::Var(v) => rc(ExprF::Var(v.shift(-1, var))), - _ => self.map_subexprs_with_special_handling_of_binders( - |e| e.subst_shift(var, val), - |x: &Label, e| { - e.subst_shift( - &var.shift(1, &x.into()), - &val.shift(1, &x.into()), - ) - }, - ), - } - } -} - -impl<N: Clone> SubExpr<N, X> { - pub fn embed_absurd<T>(&self) -> SubExpr<N, T> { - rc(self.as_ref().embed_absurd()) - } -} - -impl<E: Clone> SubExpr<X, E> { - pub fn note_absurd<N>(&self) -> SubExpr<N, E> { - rc(self.as_ref().note_absurd()) - } -} - -impl<N, E> Clone for SubExpr<N, E> { - fn clone(&self) -> Self { - SubExpr(Rc::clone(&self.0)) - } -} - -// Should probably rename this -pub fn rc<N, E>(x: Expr<N, E>) -> SubExpr<N, E> { - SubExpr(Rc::new(x)) -} - -/// Add an isize to an usize -/// Panics on over/underflow -fn add_ui(u: usize, i: isize) -> usize { - if i < 0 { - u.checked_sub(i.checked_neg().unwrap() as usize).unwrap() - } else { - u.checked_add(i as usize).unwrap() - } -} - -impl<Label: PartialEq + Clone> V<Label> { - pub fn shift(&self, delta: isize, var: &V<Label>) -> Self { - let V(x, n) = var; - let V(y, m) = self; - if x == y && n <= m { - V(y.clone(), add_ui(*m, delta)) - } else { - V(y.clone(), *m) - } - } -} - -/// `shift` is used by both normalization and type-checking to avoid variable -/// capture by shifting variable indices -/// See https://github.com/dhall-lang/dhall-lang/blob/master/standard/semantics.md#shift -/// for details -/// -pub fn shift<N, E>( - delta: isize, - var: &V<Label>, - in_expr: &SubExpr<N, E>, -) -> SubExpr<N, E> { - in_expr.shift(delta, var) -} - -pub fn shift0_multiple<N, E>( - deltas: &HashMap<Label, isize>, - in_expr: &SubExpr<N, E>, -) -> SubExpr<N, E> { - shift0_multiple_inner(&Context::new(), deltas, in_expr) -} - -fn shift0_multiple_inner<N, E>( - ctx: &Context<Label, ()>, - deltas: &HashMap<Label, isize>, - in_expr: &SubExpr<N, E>, -) -> SubExpr<N, E> { - use crate::ExprF::*; - match in_expr.as_ref() { - Var(V(y, m)) if ctx.lookup(y, *m).is_none() => { - let delta = deltas.get(y).unwrap_or(&0); - rc(Var(V(y.clone(), add_ui(*m, *delta)))) - } - _ => in_expr.map_subexprs_with_special_handling_of_binders( - |e| shift0_multiple_inner(ctx, deltas, e), - |x: &Label, e| { - shift0_multiple_inner(&ctx.insert(x.clone(), ()), deltas, e) - }, - ), - } -} diff --git a/dhall_core/src/import.rs b/dhall_core/src/import.rs deleted file mode 100644 index 00f293c..0000000 --- a/dhall_core/src/import.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::path::PathBuf; - -/// The beginning of a file path which anchors subsequent path components -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -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, Hash)] -pub enum ImportLocation { - Local(FilePrefix, PathBuf), - Remote(URL), - Env(String), - Missing, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct URL { - pub scheme: Scheme, - pub authority: String, - pub path: PathBuf, - pub query: Option<String>, - pub headers: Option<Box<ImportHashed>>, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum Scheme { - HTTP, - HTTPS, -} - -/// How to interpret the import's contents (i.e. as Dhall code or raw text) -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum ImportMode { - Code, - RawText, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Hash { - pub protocol: String, - pub hash: String, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ImportHashed { - pub location: ImportLocation, - pub hash: Option<Hash>, -} - -/// Reference to an external resource -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Import { - pub mode: ImportMode, - pub location_hashed: ImportHashed, -} diff --git a/dhall_core/src/label.rs b/dhall_core/src/label.rs deleted file mode 100644 index 43c3f53..0000000 --- a/dhall_core/src/label.rs +++ /dev/null @@ -1,34 +0,0 @@ -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 Label { - pub fn from_str(s: &str) -> Label { - Label(s.into()) - } - pub fn as_ref(&self) -> &str { - self.0.as_ref() - } -} diff --git a/dhall_core/src/lib.rs b/dhall_core/src/lib.rs deleted file mode 100644 index 3db8222..0000000 --- a/dhall_core/src/lib.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![feature(trace_macros)] -#![feature(slice_patterns)] -#![allow( - clippy::many_single_char_names, - clippy::should_implement_trait, - clippy::new_without_default, - clippy::type_complexity -)] - -//! This crate contains the core AST-handling primitives for the [dhall-rust][dhall-rust] crate. -//! This is highly unstable and breaks regularly; use at your own risk. -//! -//! [dhall-rust]: https://github.com/Nadrieril/dhall-rust - -mod core; -pub use crate::core::*; -mod import; -pub use crate::import::*; -mod label; -pub use crate::label::*; -mod text; -pub use crate::text::*; -mod printer; -pub use crate::printer::*; -mod parser; -pub use crate::parser::*; -pub mod context; -pub mod visitor; diff --git a/dhall_core/src/parser.rs b/dhall_core/src/parser.rs deleted file mode 100644 index 12383d4..0000000 --- a/dhall_core/src/parser.rs +++ /dev/null @@ -1,984 +0,0 @@ -use itertools::Itertools; -use pest::iterators::Pair; -use pest::Parser; -pub use pest::Span; -use std::borrow::Cow; -use std::collections::BTreeMap; -use std::path::PathBuf; - -use dhall_generated_parser::{DhallParser, Rule}; - -use crate::*; - -// This file consumes the parse tree generated by pest and turns it into -// our own AST. All those custom macros should eventually moved into -// their own crate because they are quite general and useful. For now they -// are here and hopefully you can figure out how they work. - -use crate::ExprF::*; - -type ParsedExpr<'a> = Expr<Span<'a>, Import>; -type ParsedSubExpr<'a> = SubExpr<Span<'a>, Import>; -type ParsedText<'a> = InterpolatedText<SubExpr<Span<'a>, Import>>; -type ParsedTextContents<'a> = - InterpolatedTextContents<SubExpr<Span<'a>, Import>>; - -pub type ParseError = pest::error::Error<Rule>; - -pub type ParseResult<T> = Result<T, ParseError>; - -fn rc(x: ParsedExpr<'_>) -> ParsedSubExpr<'_> { - crate::rc(x) -} - -fn spanned<'a>(_span: Span<'a>, x: ParsedExpr<'a>) -> ParsedExpr<'a> { - x - // This breaks equality testing; I need to fix that first - // Note(span, rc(x)) -} - -#[derive(Debug)] -enum Either<A, B> { - Left(A), - Right(B), -} - -impl crate::Builtin { - pub fn parse(s: &str) -> Option<Self> { - use crate::Builtin::*; - match s { - "Bool" => Some(Bool), - "Natural" => Some(Natural), - "Integer" => Some(Integer), - "Double" => Some(Double), - "Text" => Some(Text), - "List" => Some(List), - "Optional" => Some(Optional), - "None" => Some(OptionalNone), - "Natural/build" => Some(NaturalBuild), - "Natural/fold" => Some(NaturalFold), - "Natural/isZero" => Some(NaturalIsZero), - "Natural/even" => Some(NaturalEven), - "Natural/odd" => Some(NaturalOdd), - "Natural/toInteger" => Some(NaturalToInteger), - "Natural/show" => Some(NaturalShow), - "Integer/toDouble" => Some(IntegerToDouble), - "Integer/show" => Some(IntegerShow), - "Double/show" => Some(DoubleShow), - "List/build" => Some(ListBuild), - "List/fold" => Some(ListFold), - "List/length" => Some(ListLength), - "List/head" => Some(ListHead), - "List/last" => Some(ListLast), - "List/indexed" => Some(ListIndexed), - "List/reverse" => Some(ListReverse), - "Optional/fold" => Some(OptionalFold), - "Optional/build" => Some(OptionalBuild), - "Text/show" => Some(TextShow), - _ => None, - } - } -} - -pub fn custom_parse_error(pair: &Pair<Rule>, msg: String) -> ParseError { - let msg = - format!("{} while matching on:\n{}", msg, debug_pair(pair.clone())); - let e = pest::error::ErrorVariant::CustomError { message: msg }; - pest::error::Error::new_from_span(e, pair.as_span()) -} - -fn debug_pair(pair: Pair<Rule>) -> String { - use std::fmt::Write; - let mut s = String::new(); - fn aux(s: &mut String, indent: usize, prefix: String, pair: Pair<Rule>) { - let indent_str = "| ".repeat(indent); - let rule = pair.as_rule(); - let contents = pair.as_str(); - let mut inner = pair.into_inner(); - let mut first = true; - while let Some(p) = inner.next() { - if first { - first = false; - let last = inner.peek().is_none(); - if last && p.as_str() == contents { - let prefix = format!("{}{:?} > ", prefix, rule); - aux(s, indent, prefix, p); - continue; - } else { - writeln!( - s, - r#"{}{}{:?}: "{}""#, - indent_str, prefix, rule, contents - ) - .unwrap(); - } - } - aux(s, indent + 1, "".into(), p); - } - if first { - writeln!( - s, - r#"{}{}{:?}: "{}""#, - indent_str, prefix, rule, contents - ) - .unwrap(); - } - } - aux(&mut s, 0, "".into(), pair); - s -} - -macro_rules! make_parser { - (@pattern, rule, $name:ident) => (Rule::$name); - (@pattern, token_rule, $name:ident) => (Rule::$name); - (@pattern, rule_group, $name:ident) => (_); - (@filter, rule) => (true); - (@filter, token_rule) => (true); - (@filter, rule_group) => (false); - - (@body, - $pair:expr, - $children:expr, - rule!( $name:ident<$o:ty>; $($args:tt)* ) - ) => ( - make_parser!(@body, - $pair, - $children, - rule!( $name<$o> as $name; $($args)* ) - ) - ); - (@body, - $pair:expr, - $children:expr, - rule!( - $name:ident<$o:ty> - as $group:ident; - captured_str!($x:pat) => $body:expr - ) - ) => ({ - let $x = $pair.as_str(); - let res: $o = $body; - Ok(ParsedValue::$group(res)) - }); - (@body, - $pair:expr, - $children:expr, - rule!( - $name:ident<$o:ty> - as $group:ident; - children!( $( [$($args:tt)*] => $body:expr ),* $(,)* ) - ) - ) => ({ - #[allow(unused_imports)] - use ParsedValue::*; - #[allow(unreachable_code)] - let res: $o = improved_slice_patterns::match_vec!($children; - $( [$($args)*] => $body, )* - [x..] => Err( - format!("Unexpected children: {:?}", x.collect::<Vec<_>>()) - )?, - ).map_err(|_| -> String { unreachable!() })?; - Ok(ParsedValue::$group(res)) - }); - (@body, - $pair:expr, - $children:expr, - rule!( - $name:ident<$o:ty> - as $group:ident; - $span:ident; - $($args:tt)* - ) - ) => ({ - let $span = $pair.as_span(); - make_parser!(@body, - $pair, - $children, - rule!( - $name<$o> - as $group; - $($args)* - ) - ) - }); - (@body, - $pair:expr, - $children:expr, - token_rule!($name:ident<$o:ty>) - ) => ({ - Ok(ParsedValue::$name(())) - }); - (@body, $pair:expr, $children:expr, rule_group!( $name:ident<$o:ty> )) => ( - unreachable!() - ); - - ($( $submac:ident!( $name:ident<$o:ty> $($args:tt)* ); )*) => ( - #[allow(non_camel_case_types, dead_code, clippy::large_enum_variant)] - #[derive(Debug)] - enum ParsedValue<'a> { - $( $name($o), )* - } - - fn parse_any<'a>(pair: Pair<'a, Rule>, children: Vec<ParsedValue<'a>>) - -> Result<ParsedValue<'a>, String> { - match pair.as_rule() { - $( - make_parser!(@pattern, $submac, $name) - if make_parser!(@filter, $submac) - => make_parser!(@body, pair, children, - $submac!( $name<$o> $($args)* )) - , - )* - r => Err(format!("Unexpected {:?}", r)), - } - } - ); -} - -// Non-recursive implementation to avoid stack overflows -fn do_parse<'a>(initial_pair: Pair<'a, Rule>) -> ParseResult<ParsedValue<'a>> { - enum StackFrame<'a> { - Unprocessed(Pair<'a, Rule>), - Processed(Pair<'a, Rule>, usize), - } - use StackFrame::*; - let mut pairs_stack: Vec<StackFrame> = - vec![Unprocessed(initial_pair.clone())]; - let mut values_stack: Vec<ParsedValue> = vec![]; - while let Some(p) = pairs_stack.pop() { - match p { - Unprocessed(mut pair) => loop { - let mut pairs: Vec<_> = pair.clone().into_inner().collect(); - let n_children = pairs.len(); - if n_children == 1 && can_be_shortcutted(pair.as_rule()) { - pair = pairs.pop().unwrap(); - continue; - } else { - pairs_stack.push(Processed(pair, n_children)); - pairs_stack - .extend(pairs.into_iter().map(StackFrame::Unprocessed)); - break; - } - }, - Processed(pair, n) => { - let mut children: Vec<_> = - values_stack.split_off(values_stack.len() - n); - children.reverse(); - let val = match parse_any(pair.clone(), children) { - Ok(v) => v, - Err(msg) => Err(custom_parse_error(&pair, msg))?, - }; - values_stack.push(val); - } - } - } - Ok(values_stack.pop().unwrap()) -} - -// List of rules that can be shortcutted if they have a single child -fn can_be_shortcutted(rule: Rule) -> bool { - use Rule::*; - match rule { - expression - | import_alt_expression - | or_expression - | plus_expression - | text_append_expression - | list_append_expression - | and_expression - | combine_expression - | prefer_expression - | combine_types_expression - | times_expression - | equal_expression - | not_equal_expression - | application_expression - | first_application_expression - | selector_expression - | annotated_expression => true, - _ => false, - } -} - -make_parser! { - token_rule!(EOI<()>); - - rule!(simple_label<Label>; - captured_str!(s) => Label::from(s.trim().to_owned()) - ); - rule!(quoted_label<Label>; - captured_str!(s) => Label::from(s.trim().to_owned()) - ); - rule!(label<Label>; children!( - [simple_label(l)] => l, - [quoted_label(l)] => l, - )); - - rule!(double_quote_literal<ParsedText<'a>>; children!( - [double_quote_chunk(chunks)..] => { - chunks.collect() - } - )); - - rule!(double_quote_chunk<ParsedTextContents<'a>>; children!( - [interpolation(e)] => { - InterpolatedTextContents::Expr(rc(e)) - }, - [double_quote_escaped(s)] => { - InterpolatedTextContents::Text(s) - }, - [double_quote_char(s)] => { - InterpolatedTextContents::Text(s.to_owned()) - }, - )); - rule!(double_quote_escaped<String>; - captured_str!(s) => { - match s { - "\"" => "\"".to_owned(), - "$" => "$".to_owned(), - "\\" => "\\".to_owned(), - "/" => "/".to_owned(), - "b" => "\u{0008}".to_owned(), - "f" => "\u{000C}".to_owned(), - "n" => "\n".to_owned(), - "r" => "\r".to_owned(), - "t" => "\t".to_owned(), - _ => { - // "uXXXX" - use std::convert::TryFrom; - let c = u16::from_str_radix(&s[1..5], 16).unwrap(); - let c = char::try_from(u32::from(c)).unwrap(); - std::iter::once(c).collect() - } - } - } - ); - rule!(double_quote_char<&'a str>; - captured_str!(s) => s - ); - - rule!(single_quote_literal<ParsedText<'a>>; children!( - [single_quote_continue(lines)] => { - let space = InterpolatedTextContents::Text(" ".to_owned()); - let newline = InterpolatedTextContents::Text("\n".to_owned()); - let min_indent = lines - .iter() - .map(|l| { - l.iter().rev().take_while(|c| **c == space).count() - }) - .min() - .unwrap(); - - lines - .into_iter() - .rev() - .map(|mut l| { l.split_off(l.len() - min_indent); l }) - .intersperse(vec![newline]) - .flat_map(|x| x.into_iter().rev()) - .collect::<ParsedText>() - } - )); - rule!(single_quote_char<&'a str>; - captured_str!(s) => s - ); - rule!(escaped_quote_pair<&'a str>; - captured_str!(_) => "''" - ); - rule!(escaped_interpolation<&'a str>; - captured_str!(_) => "${" - ); - rule!(interpolation<ParsedExpr<'a>>; children!( - [expression(e)] => e - )); - - rule!(single_quote_continue<Vec<Vec<ParsedTextContents<'a>>>>; children!( - [interpolation(c), single_quote_continue(lines)] => { - let c = InterpolatedTextContents::Expr(rc(c)); - let mut lines = lines; - lines.last_mut().unwrap().push(c); - lines - }, - [escaped_quote_pair(c), single_quote_continue(lines)] => { - let c = InterpolatedTextContents::Text(c.to_owned()); - let mut lines = lines; - lines.last_mut().unwrap().push(c); - lines - }, - [escaped_interpolation(c), single_quote_continue(lines)] => { - let c = InterpolatedTextContents::Text(c.to_owned()); - let mut lines = lines; - lines.last_mut().unwrap().push(c); - lines - }, - [single_quote_char("\n"), single_quote_continue(lines)] => { - let mut lines = lines; - lines.push(vec![]); - lines - }, - [single_quote_char(c), single_quote_continue(lines)] => { - let c = InterpolatedTextContents::Text(c.to_owned()); - let mut lines = lines; - lines.last_mut().unwrap().push(c); - lines - }, - [] => { - vec![vec![]] - }, - )); - - rule!(builtin<ParsedExpr<'a>>; span; - captured_str!(s) => { - spanned(span, match crate::Builtin::parse(s) { - Some(b) => Builtin(b), - None => match s { - "True" => BoolLit(true), - "False" => BoolLit(false), - "Type" => Const(crate::Const::Type), - "Kind" => Const(crate::Const::Kind), - "Sort" => Const(crate::Const::Sort), - _ => Err( - format!("Unrecognized builtin: '{}'", s) - )?, - } - }) - } - ); - - token_rule!(NaN<()>); - token_rule!(minus_infinity_literal<()>); - token_rule!(plus_infinity_literal<()>); - - rule!(numeric_double_literal<core::Double>; - captured_str!(s) => { - let s = s.trim(); - match s.parse::<f64>() { - Ok(x) if x.is_infinite() => - Err(format!("Overflow while parsing double literal '{}'", s))?, - Ok(x) => NaiveDouble::from(x), - Err(e) => Err(format!("{}", e))?, - } - } - ); - - rule!(double_literal<core::Double>; children!( - [numeric_double_literal(n)] => n, - [minus_infinity_literal(n)] => std::f64::NEG_INFINITY.into(), - [plus_infinity_literal(n)] => std::f64::INFINITY.into(), - [NaN(n)] => std::f64::NAN.into(), - )); - - rule!(natural_literal<core::Natural>; - captured_str!(s) => { - s.trim() - .parse() - .map_err(|e| format!("{}", e))? - } - ); - - rule!(integer_literal<core::Integer>; - captured_str!(s) => { - s.trim() - .parse() - .map_err(|e| format!("{}", e))? - } - ); - - rule!(identifier<ParsedExpr<'a>> as expression; span; children!( - [variable(v)] => { - spanned(span, Var(v)) - }, - [builtin(e)] => e, - )); - - rule!(variable<V<Label>>; children!( - [label(l), natural_literal(idx)] => { - V(l, idx) - }, - [label(l)] => { - V(l, 0) - }, - )); - - rule!(unquoted_path_component<&'a str>; captured_str!(s) => s); - rule!(quoted_path_component<&'a str>; captured_str!(s) => s); - rule!(path_component<String>; children!( - [unquoted_path_component(s)] => { - percent_encoding::percent_decode(s.as_bytes()) - .decode_utf8_lossy() - .into_owned() - }, - [quoted_path_component(s)] => s.to_string(), - )); - rule!(path<PathBuf>; children!( - [path_component(components)..] => { - components.collect() - } - )); - - rule_group!(local<(FilePrefix, PathBuf)>); - - rule!(parent_path<(FilePrefix, PathBuf)> as local; children!( - [path(p)] => (FilePrefix::Parent, p) - )); - rule!(here_path<(FilePrefix, PathBuf)> as local; children!( - [path(p)] => (FilePrefix::Here, p) - )); - rule!(home_path<(FilePrefix, PathBuf)> as local; children!( - [path(p)] => (FilePrefix::Home, p) - )); - rule!(absolute_path<(FilePrefix, PathBuf)> as local; children!( - [path(p)] => (FilePrefix::Absolute, p) - )); - - rule!(scheme<Scheme>; captured_str!(s) => match s { - "http" => Scheme::HTTP, - "https" => Scheme::HTTPS, - _ => unreachable!(), - }); - - rule!(http_raw<URL>; children!( - [scheme(sch), authority(auth), path(p)] => URL { - scheme: sch, - authority: auth, - path: p, - query: None, - headers: None, - }, - [scheme(sch), authority(auth), path(p), query(q)] => URL { - scheme: sch, - authority: auth, - path: p, - query: Some(q), - headers: None, - }, - )); - - rule!(authority<String>; captured_str!(s) => s.to_owned()); - - rule!(query<String>; captured_str!(s) => s.to_owned()); - - rule!(http<URL>; children!( - [http_raw(url)] => url, - [http_raw(url), import_hashed(ih)] => - URL { headers: Some(Box::new(ih)), ..url }, - )); - - rule!(env<String>; children!( - [bash_environment_variable(s)] => s, - [posix_environment_variable(s)] => s, - )); - rule!(bash_environment_variable<String>; captured_str!(s) => s.to_owned()); - rule!(posix_environment_variable<String>; children!( - [posix_environment_variable_character(chars)..] => { - chars.collect() - }, - )); - rule!(posix_environment_variable_character<Cow<'a, str>>; - captured_str!(s) => { - match s { - "\\\"" => Cow::Owned("\"".to_owned()), - "\\\\" => Cow::Owned("\\".to_owned()), - "\\a" => Cow::Owned("\u{0007}".to_owned()), - "\\b" => Cow::Owned("\u{0008}".to_owned()), - "\\f" => Cow::Owned("\u{000C}".to_owned()), - "\\n" => Cow::Owned("\n".to_owned()), - "\\r" => Cow::Owned("\r".to_owned()), - "\\t" => Cow::Owned("\t".to_owned()), - "\\v" => Cow::Owned("\u{000B}".to_owned()), - _ => Cow::Borrowed(s) - } - } - ); - - token_rule!(missing<()>); - - rule!(import_type<ImportLocation>; children!( - [missing(_)] => { - ImportLocation::Missing - }, - [env(e)] => { - ImportLocation::Env(e) - }, - [http(url)] => { - ImportLocation::Remote(url) - }, - [local((prefix, p))] => { - ImportLocation::Local(prefix, p) - }, - )); - - rule!(hash<Hash>; captured_str!(s) => - Hash { - protocol: s.trim()[..6].to_owned(), - hash: s.trim()[7..].to_owned(), - } - ); - - rule!(import_hashed<ImportHashed>; children!( - [import_type(location)] => - ImportHashed { location, hash: None }, - [import_type(location), hash(h)] => - ImportHashed { location, hash: Some(h) }, - )); - - token_rule!(Text<()>); - - rule!(import<ParsedExpr<'a>> as expression; span; children!( - [import_hashed(location_hashed)] => { - spanned(span, Embed(Import { - mode: ImportMode::Code, - location_hashed - })) - }, - [import_hashed(location_hashed), Text(_)] => { - spanned(span, Embed(Import { - mode: ImportMode::RawText, - location_hashed - })) - }, - )); - - token_rule!(lambda<()>); - token_rule!(forall<()>); - token_rule!(arrow<()>); - token_rule!(merge<()>); - token_rule!(if_<()>); - token_rule!(in_<()>); - - rule!(expression<ParsedExpr<'a>> as expression; span; children!( - [lambda(()), label(l), expression(typ), - arrow(()), expression(body)] => { - spanned(span, Lam(l, rc(typ), rc(body))) - }, - [if_(()), expression(cond), expression(left), expression(right)] => { - spanned(span, BoolIf(rc(cond), rc(left), rc(right))) - }, - [let_binding(bindings).., in_(()), expression(final_expr)] => { - bindings.rev().fold( - final_expr, - |acc, x| Let(x.0, x.1, x.2, rc(acc)) - ) - }, - [forall(()), label(l), expression(typ), - arrow(()), expression(body)] => { - spanned(span, Pi(l, rc(typ), rc(body))) - }, - [expression(typ), arrow(()), expression(body)] => { - spanned(span, Pi("_".into(), rc(typ), rc(body))) - }, - [merge(()), expression(x), expression(y), expression(z)] => { - spanned(span, Merge(rc(x), rc(y), Some(rc(z)))) - }, - [expression(e)] => e, - )); - - rule!(let_binding<(Label, Option<ParsedSubExpr<'a>>, ParsedSubExpr<'a>)>; - children!( - [label(name), expression(annot), expression(expr)] => - (name, Some(rc(annot)), rc(expr)), - [label(name), expression(expr)] => - (name, None, rc(expr)), - )); - - token_rule!(List<()>); - token_rule!(Optional<()>); - - rule!(empty_collection<ParsedExpr<'a>> as expression; span; children!( - [List(_), expression(t)] => { - spanned(span, EmptyListLit(rc(t))) - }, - [Optional(_), expression(t)] => { - spanned(span, OldOptionalLit(None, rc(t))) - }, - )); - - rule!(non_empty_optional<ParsedExpr<'a>> as expression; span; children!( - [expression(x), Optional(_), expression(t)] => { - spanned(span, OldOptionalLit(Some(rc(x)), rc(t))) - } - )); - - rule!(import_alt_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::ImportAlt; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(or_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::BoolOr; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(plus_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::NaturalPlus; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(text_append_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::TextAppend; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(list_append_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::ListAppend; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(and_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::BoolAnd; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(combine_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::Combine; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(prefer_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::Prefer; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(combine_types_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::CombineTypes; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(times_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::NaturalTimes; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(equal_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::BoolEQ; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(not_equal_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::BoolNE; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - - rule!(annotated_expression<ParsedExpr<'a>> as expression; span; children!( - [expression(e)] => e, - [expression(e), expression(annot)] => { - spanned(span, Annot(rc(e), rc(annot))) - }, - )); - - token_rule!(Some_<()>); - - rule!(application_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - rest.fold(first, |acc, e| App(rc(acc), rc(e))) - }, - )); - - rule!(first_application_expression<ParsedExpr<'a>> as expression; span; - children!( - [expression(e)] => e, - [Some_(()), expression(e)] => { - spanned(span, SomeLit(rc(e))) - }, - [merge(()), expression(x), expression(y)] => { - spanned(span, Merge(rc(x), rc(y), None)) - }, - )); - - rule!(selector_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), selector(rest)..] => { - rest.fold(first, |acc, e| match e { - Either::Left(l) => Field(rc(acc), l), - Either::Right(ls) => Projection(rc(acc), ls), - }) - } - )); - - rule!(selector<Either<Label, Vec<Label>>>; children!( - [label(l)] => Either::Left(l), - [labels(ls)] => Either::Right(ls), - )); - - rule!(labels<Vec<Label>>; children!( - [label(ls)..] => ls.collect(), - )); - - rule!(primitive_expression<ParsedExpr<'a>> as expression; span; children!( - [double_literal(n)] => spanned(span, DoubleLit(n)), - [natural_literal(n)] => spanned(span, NaturalLit(n)), - [integer_literal(n)] => spanned(span, IntegerLit(n)), - [double_quote_literal(s)] => spanned(span, TextLit(s)), - [single_quote_literal(s)] => spanned(span, TextLit(s)), - [expression(e)] => e, - )); - - rule!(empty_record_literal<ParsedExpr<'a>> as expression; span; - captured_str!(_) => spanned(span, RecordLit(BTreeMap::new())) - ); - - rule!(empty_record_type<ParsedExpr<'a>> as expression; span; - captured_str!(_) => spanned(span, RecordType(BTreeMap::new())) - ); - - rule!(non_empty_record_type_or_literal<ParsedExpr<'a>> as expression; span; - children!( - [label(first_label), non_empty_record_type(rest)] => { - let (first_expr, mut map) = rest; - map.insert(first_label, rc(first_expr)); - spanned(span, RecordType(map)) - }, - [label(first_label), non_empty_record_literal(rest)] => { - let (first_expr, mut map) = rest; - map.insert(first_label, rc(first_expr)); - spanned(span, RecordLit(map)) - }, - )); - - rule!(non_empty_record_type - <(ParsedExpr<'a>, BTreeMap<Label, ParsedSubExpr<'a>>)>; children!( - [expression(expr), record_type_entry(entries)..] => { - (expr, entries.collect()) - } - )); - - rule!(record_type_entry<(Label, ParsedSubExpr<'a>)>; children!( - [label(name), expression(expr)] => (name, rc(expr)) - )); - - rule!(non_empty_record_literal - <(ParsedExpr<'a>, BTreeMap<Label, ParsedSubExpr<'a>>)>; children!( - [expression(expr), record_literal_entry(entries)..] => { - (expr, entries.collect()) - } - )); - - rule!(record_literal_entry<(Label, ParsedSubExpr<'a>)>; children!( - [label(name), expression(expr)] => (name, rc(expr)) - )); - - rule!(union_type_or_literal<ParsedExpr<'a>> as expression; span; children!( - [empty_union_type(_)] => { - spanned(span, UnionType(BTreeMap::new())) - }, - [non_empty_union_type_or_literal((Some((l, e)), entries))] => { - spanned(span, UnionLit(l, e, entries)) - }, - [non_empty_union_type_or_literal((None, entries))] => { - spanned(span, UnionType(entries)) - }, - )); - - token_rule!(empty_union_type<()>); - - rule!(non_empty_union_type_or_literal - <(Option<(Label, ParsedSubExpr<'a>)>, - BTreeMap<Label, Option<ParsedSubExpr<'a>>>)>; - children!( - [label(l), union_literal_variant_value((e, entries))] => { - (Some((l, e)), entries) - }, - [label(l), union_type_or_literal_variant_type((e, rest))] => { - let (x, mut entries) = rest; - entries.insert(l, e); - (x, entries) - }, - )); - - rule!(union_literal_variant_value - <(ParsedSubExpr<'a>, BTreeMap<Label, Option<ParsedSubExpr<'a>>>)>; - children!( - [expression(e), union_type_entry(entries)..] => { - (rc(e), entries.collect()) - }, - )); - - rule!(union_type_entry<(Label, Option<ParsedSubExpr<'a>>)>; children!( - [label(name), expression(expr)] => (name, Some(rc(expr))), - [label(name)] => (name, None), - )); - - // TODO: unary union variants - rule!(union_type_or_literal_variant_type - <(Option<ParsedSubExpr<'a>>, - (Option<(Label, ParsedSubExpr<'a>)>, - BTreeMap<Label, Option<ParsedSubExpr<'a>>>))>; - children!( - [expression(e), non_empty_union_type_or_literal(rest)] => { - (Some(rc(e)), rest) - }, - [expression(e)] => { - (Some(rc(e)), (None, BTreeMap::new())) - }, - [non_empty_union_type_or_literal(rest)] => { - (None, rest) - }, - [] => { - (None, (None, BTreeMap::new())) - }, - )); - - rule!(non_empty_list_literal<ParsedExpr<'a>> as expression; span; - children!( - [expression(items)..] => spanned( - span, - NEListLit(items.map(rc).collect()) - ) - )); - - rule!(final_expression<ParsedExpr<'a>> as expression; children!( - [expression(e), EOI(_eoi)] => e - )); -} - -pub fn parse_expr<'a>(s: &'a str) -> ParseResult<ParsedSubExpr<'a>> { - let mut pairs = DhallParser::parse(Rule::final_expression, s)?; - let expr = do_parse(pairs.next().unwrap())?; - assert_eq!(pairs.next(), None); - match expr { - ParsedValue::expression(e) => Ok(rc(e)), - _ => unreachable!(), - } - // Ok(rc(BoolLit(false))) -} - -#[test] -fn test_parse() { - // let expr = r#"{ x = "foo", y = 4 }.x"#; - // let expr = r#"(1 + 2) * 3"#; - let expr = r#"(1) + 3 * 5"#; - println!("{:?}", parse_expr(expr)); - match parse_expr(expr) { - Err(e) => { - println!("{:?}", e); - println!("{}", e); - } - ok => println!("{:?}", ok), - }; - // assert!(false); -} diff --git a/dhall_core/src/printer.rs b/dhall_core/src/printer.rs deleted file mode 100644 index 704000a..0000000 --- a/dhall_core/src/printer.rs +++ /dev/null @@ -1,498 +0,0 @@ -use crate::*; -use itertools::Itertools; -use std::fmt::{self, Display}; - -/// Generic instance that delegates to subexpressions -impl<SE: Display + Clone, N, E: Display> Display for ExprF<SE, Label, N, E> { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - use crate::ExprF::*; - match self { - Lam(a, b, c) => { - write!(f, "λ({} : {}) → {}", a, b, c)?; - } - BoolIf(a, b, c) => { - write!(f, "if {} then {} else {}", a, b, c)?; - } - Pi(a, b, c) if &String::from(a) == "_" => { - write!(f, "{} → {}", b, c)?; - } - Pi(a, b, c) => { - write!(f, "∀({} : {}) → {}", a, b, c)?; - } - Let(a, b, c, d) => { - write!(f, "let {}", a)?; - if let Some(b) = b { - write!(f, " : {}", b)?; - } - write!(f, " = {} in {}", c, d)?; - } - EmptyListLit(t) => { - write!(f, "[] : List {}", t)?; - } - NEListLit(es) => { - fmt_list("[", ", ", "]", es, f, Display::fmt)?; - } - OldOptionalLit(None, t) => { - write!(f, "[] : Optional {}", t)?; - } - OldOptionalLit(Some(x), t) => { - write!(f, "[{}] : Optional {}", x, t)?; - } - SomeLit(e) => { - write!(f, "Some {}", e)?; - } - Merge(a, b, c) => { - write!(f, "merge {} {}", a, b)?; - if let Some(c) = c { - write!(f, " : {}", c)?; - } - } - Annot(a, b) => { - write!(f, "{} : {}", a, b)?; - } - ExprF::BinOp(op, a, b) => { - write!(f, "{} {} {}", a, op, b)?; - } - ExprF::App(a, b) => { - write!(f, "{} {}", a, b)?; - } - Field(a, b) => { - write!(f, "{}.{}", a, b)?; - } - Projection(e, ls) => { - write!(f, "{}.", e)?; - fmt_list("{ ", ", ", " }", ls, f, Display::fmt)?; - } - 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(a) => a.fmt(f)?, - RecordType(a) if a.is_empty() => f.write_str("{}")?, - RecordType(a) => fmt_list("{ ", ", ", " }", a, f, |(k, t), f| { - write!(f, "{} : {}", k, t) - })?, - RecordLit(a) if a.is_empty() => f.write_str("{=}")?, - RecordLit(a) => fmt_list("{ ", ", ", " }", a, f, |(k, v), f| { - write!(f, "{} = {}", k, v) - })?, - UnionType(a) => fmt_list("< ", " | ", " >", a, f, |(k, v), f| { - write!(f, "{}", k)?; - if let Some(v) = v { - write!(f, ": {}", v)?; - } - Ok(()) - })?, - UnionLit(a, b, c) => { - write!(f, "< {} = {}", a, b)?; - for (k, v) in c { - write!(f, " | {}", k)?; - if let Some(v) = v { - write!(f, ": {}", v)?; - } - } - f.write_str(" >")? - } - Embed(a) => a.fmt(f)?, - Note(_, b) => b.fmt(f)?, - } - Ok(()) - } -} - -// There is a one-to-one correspondence between the formatter and the grammar. Each phase is -// named after a corresponding grammar group, and the structure of the formatter reflects -// the relationship between the corresponding grammar rules. This leads to the nice property -// of automatically getting all the parentheses and precedences right. -#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] -enum PrintPhase { - Base, - Operator, - BinOp(core::BinOp), - App, - Import, - Primitive, -} - -// Wraps an Expr with a phase, so that phase selsction can be done -// separate from the actual printing -#[derive(Clone)] -struct PhasedExpr<'a, S, A>(&'a SubExpr<S, A>, PrintPhase); - -impl<'a, S: Clone, A: Display + Clone> Display for PhasedExpr<'a, S, A> { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - self.0.as_ref().fmt_phase(f, self.1) - } -} - -impl<'a, S: Clone, A: Display + Clone> PhasedExpr<'a, S, A> { - fn phase(self, phase: PrintPhase) -> PhasedExpr<'a, S, A> { - PhasedExpr(self.0, phase) - } -} - -impl<S: Clone, A: Display + Clone> Expr<S, A> { - fn fmt_phase( - &self, - f: &mut fmt::Formatter, - mut phase: PrintPhase, - ) -> Result<(), fmt::Error> { - use crate::ExprF::*; - use PrintPhase::*; - - let needs_paren = match self { - Lam(_, _, _) - | BoolIf(_, _, _) - | Pi(_, _, _) - | Let(_, _, _, _) - | EmptyListLit(_) - | NEListLit(_) - | OldOptionalLit(_, _) - | SomeLit(_) - | Merge(_, _, _) - | Annot(_, _) - if phase > Base => - { - true - } - // Precedence is magically handled by the ordering of BinOps. - ExprF::BinOp(op, _, _) if phase > PrintPhase::BinOp(*op) => true, - ExprF::App(_, _) if phase > PrintPhase::App => true, - Field(_, _) | Projection(_, _) if phase > Import => true, - _ => false, - }; - - if needs_paren { - phase = Base; - } - - // Annotate subexpressions with the appropriate phase, defaulting to Base - let phased_self = match self.map_ref_simple(|e| PhasedExpr(e, Base)) { - Pi(a, b, c) => { - if &String::from(&a) == "_" { - Pi(a, b.phase(Operator), c) - } else { - Pi(a, b, c) - } - } - Merge(a, b, c) => Merge( - a.phase(Import), - b.phase(Import), - c.map(|x| x.phase(PrintPhase::App)), - ), - Annot(a, b) => Annot(a.phase(Operator), b), - ExprF::BinOp(op, a, b) => ExprF::BinOp( - op, - a.phase(PrintPhase::BinOp(op)), - b.phase(PrintPhase::BinOp(op)), - ), - EmptyListLit(t) => EmptyListLit(t.phase(Import)), - OldOptionalLit(x, t) => OldOptionalLit(x, t.phase(Import)), - SomeLit(e) => SomeLit(e.phase(Import)), - ExprF::App(f, a) => ExprF::App(f.phase(Import), a.phase(Import)), - Field(a, b) => Field(a.phase(Primitive), b), - Projection(e, ls) => Projection(e.phase(Primitive), ls), - Note(n, b) => Note(n, b.phase(phase)), - e => e, - }; - - if needs_paren { - f.write_str("(")?; - } - - // Uses the ExprF<PhasedExpr<_>, _, _, _> instance - phased_self.fmt(f)?; - - if needs_paren { - f.write_str(")")?; - } - Ok(()) - } -} - -impl<S: Clone, A: Display + Clone> Display for SubExpr<S, A> { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - self.as_ref().fmt_phase(f, PrintPhase::Base) - } -} - -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<SubExpr: Display + Clone> Display for InterpolatedText<SubExpr> { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - f.write_str("\"")?; - for x in self.iter() { - match x { - InterpolatedTextContents::Text(a) => { - for c in a.chars() { - match c { - '\\' => f.write_str("\\\\"), - '"' => f.write_str("\\\""), - '$' => f.write_str("\\$"), - '\u{0008}' => f.write_str("\\b"), - '\u{000C}' => f.write_str("\\f"), - '\n' => f.write_str("\\n"), - '\r' => f.write_str("\\r"), - '\t' => f.write_str("\\t"), - c => write!(f, "{}", c), - }?; - } - } - InterpolatedTextContents::Expr(e) => { - f.write_str("${ ")?; - e.fmt(f)?; - f.write_str(" }")?; - } - } - } - f.write_str("\"")?; - Ok(()) - } -} - -impl Display for Const { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - <Self as fmt::Debug>::fmt(self, f) - } -} - -impl Display for BinOp { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - use crate::BinOp::*; - f.write_str(match self { - BoolOr => "||", - TextAppend => "++", - NaturalPlus => "+", - BoolAnd => "&&", - Combine => "/\\", - NaturalTimes => "*", - BoolEQ => "==", - BoolNE => "!=", - CombineTypes => "//\\\\", - ImportAlt => "?", - Prefer => "//", - ListAppend => "#", - }) - } -} - -impl Display for NaiveDouble { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - let v = f64::from(*self); - if v == std::f64::INFINITY { - f.write_str("Infinity") - } else if v == std::f64::NEG_INFINITY { - f.write_str("-Infinity") - } else if v.is_nan() { - f.write_str("NaN") - } else { - let s = format!("{}", v); - if s.contains('e') || s.contains('.') { - f.write_str(&s) - } else { - write!(f, "{}.0", s) - } - } - } -} - -impl Display for Label { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - // TODO: distinguish between reserved and nonreserved locations for quoting builtins - let s = String::from(self); - let is_reserved = match s.as_str() { - "let" | "in" | "if" | "then" | "else" | "Type" | "Kind" - | "Sort" | "True" | "False" => true, - _ => crate::Builtin::parse(&s).is_some(), - }; - if !is_reserved && s.chars().all(|c| c.is_ascii_alphanumeric()) { - write!(f, "{}", s) - } else { - write!(f, "`{}`", s) - } - } -} - -impl Display for Hash { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}:{}", self.protocol, self.hash) - } -} -impl Display for ImportHashed { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - use std::path::PathBuf; - use FilePrefix::*; - use ImportLocation::*; - let quoted_path_component = |s: &str| -> String { - if s.chars().all(|c| c.is_ascii_alphanumeric()) { - s.to_owned() - } else { - format!("\"{}\"", s) - } - }; - let fmt_path = |f: &mut fmt::Formatter, p: &PathBuf| { - let res: String = p - .iter() - .map(|c| quoted_path_component(c.to_string_lossy().as_ref())) - .join("/"); - f.write_str(&res) - }; - - match &self.location { - Local(prefix, path) => { - let prefix = match prefix { - Here => ".", - Parent => "..", - Home => "~", - Absolute => "", - }; - write!(f, "{}/", prefix)?; - fmt_path(f, path)?; - } - Remote(url) => { - write!(f, "{}://{}/", url.scheme, url.authority,)?; - fmt_path(f, &url.path)?; - if let Some(q) = &url.query { - write!(f, "?{}", q)? - } - if let Some(h) = &url.headers { - write!(f, " using ({})", h)? - } - } - Env(s) => { - write!(f, "env:")?; - if s.chars().all(|c| c.is_ascii_alphanumeric()) { - write!(f, "{}", s)?; - } else { - write!(f, "\"")?; - for c in s.chars() { - match c { - '"' => f.write_str("\\\"")?, - '\\' => f.write_str("\\\\")?, - '\u{0007}' => f.write_str("\\a")?, - '\u{0008}' => f.write_str("\\b")?, - '\u{000C}' => f.write_str("\\f")?, - '\n' => f.write_str("\\n")?, - '\r' => f.write_str("\\r")?, - '\t' => f.write_str("\\t")?, - '\u{000B}' => f.write_str("\\v")?, - _ => write!(f, "{}", c)?, - } - } - write!(f, "\"")?; - } - } - Missing => { - write!(f, "missing")?; - } - } - if let Some(hash) = &self.hash { - write!(f, " ")?; - hash.fmt(f)?; - } - Ok(()) - } -} - -impl Display for Import { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - self.location_hashed.fmt(f)?; - use ImportMode::*; - match self.mode { - Code => {} - RawText => write!(f, " as Text")?, - } - Ok(()) - } -} - -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", - 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 Scheme { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - use crate::Scheme::*; - f.write_str(match *self { - HTTP => "http", - HTTPS => "https", - }) - } -} - -impl<Label: Display> Display for V<Label> { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - let V(x, 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 deleted file mode 100644 index 83643d9..0000000 --- a/dhall_core/src/text.rs +++ /dev/null @@ -1,116 +0,0 @@ -use std::iter::FromIterator; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct InterpolatedText<SubExpr> { - head: String, - tail: Vec<(SubExpr, String)>, -} - -impl<SubExpr> From<(String, Vec<(SubExpr, String)>)> - for InterpolatedText<SubExpr> -{ - fn from(x: (String, Vec<(SubExpr, String)>)) -> Self { - InterpolatedText { - head: x.0, - tail: x.1, - } - } -} - -impl<SubExpr> From<String> for InterpolatedText<SubExpr> { - fn from(s: String) -> Self { - InterpolatedText { - head: s, - tail: vec![], - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum InterpolatedTextContents<SubExpr> { - Text(String), - Expr(SubExpr), -} - -impl<SubExpr> InterpolatedTextContents<SubExpr> { - pub fn is_empty(&self) -> bool { - use InterpolatedTextContents::{Expr, Text}; - match self { - Expr(_) => false, - Text(s) => s.is_empty(), - } - } -} - -impl<SubExpr> InterpolatedText<SubExpr> { - pub fn traverse_ref<'a, SubExpr2, E, F>( - &'a self, - mut f: F, - ) -> Result<InterpolatedText<SubExpr2>, E> - where - F: FnMut(&'a SubExpr) -> Result<SubExpr2, E>, - { - Ok(InterpolatedText { - head: self.head.clone(), - tail: self - .tail - .iter() - .map(|(e, s)| Ok((f(e)?, s.clone()))) - .collect::<Result<_, _>>()?, - }) - } - - pub fn iter<'a>( - &'a self, - ) -> impl Iterator<Item = InterpolatedTextContents<SubExpr>> + 'a - where - SubExpr: Clone, - { - use std::iter::once; - use InterpolatedTextContents::{Expr, Text}; - once(Text(self.head.clone())) - .chain(self.tail.iter().flat_map(|(e, s)| { - once(Expr(SubExpr::clone(e))).chain(once(Text(s.clone()))) - })) - .filter(|c| !c.is_empty()) - } - - pub fn into_iter( - self, - ) -> impl Iterator<Item = InterpolatedTextContents<SubExpr>> { - use std::iter::once; - use InterpolatedTextContents::{Expr, Text}; - once(Text(self.head)) - .chain( - self.tail - .into_iter() - .flat_map(|(e, s)| once(Expr(e)).chain(once(Text(s)))), - ) - .filter(|c| !c.is_empty()) - } -} - -impl<SubExpr> FromIterator<InterpolatedTextContents<SubExpr>> - for InterpolatedText<SubExpr> -{ - fn from_iter<T>(iter: T) -> Self - where - T: IntoIterator<Item = InterpolatedTextContents<SubExpr>>, - { - let mut res = InterpolatedText { - head: String::new(), - tail: Vec::new(), - }; - 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) => { - res.tail.push((e, String::new())); - crnt_str = &mut res.tail.last_mut().unwrap().1; - } - } - } - res - } -} diff --git a/dhall_core/src/visitor.rs b/dhall_core/src/visitor.rs deleted file mode 100644 index caaefce..0000000 --- a/dhall_core/src/visitor.rs +++ /dev/null @@ -1,667 +0,0 @@ -use std::collections::BTreeMap; - -use crate::*; - -/// A way too generic Visitor trait. -pub trait GenericVisitor<Input, Return>: Sized { - fn visit(self, input: Input) -> Return; -} - -/// A visitor trait that can be used to traverse `ExprF`s. We need this pattern -/// so that Rust lets us have as much mutability as we can. -/// For example, `traverse_embed` cannot be made using only `traverse_ref`, because -/// `traverse_ref` takes a `FnMut` so we would need to pass multiple mutable -/// reverences to this argument to `traverse_ref`. But Rust's ownership system -/// is all about preventing exactly this ! So we have to be more clever. -/// The visitor pattern allows us to have only one mutable thing the whole -/// time: the visitor itself. The visitor can then carry around multiple closures -/// or just one, and Rust is ok with either. See for example TraverseRefVisitor -/// and TraverseEmbedVisitor. -/// This is very generic. For a more legible trait, see ExprFInFallibleVisitor -pub trait ExprFVeryGenericVisitor<'a, Ret, SE1, L1, N1, E1>: Sized { - type Error; - type SE2; - type L2; - type N2; - type E2; - - fn visit_subexpr( - &mut self, - subexpr: &'a SE1, - ) -> Result<Self::SE2, Self::Error>; - fn visit_label(&mut self, label: &'a L1) -> Result<Self::L2, Self::Error>; - - fn visit_binder( - self, - label: &'a L1, - subexpr: &'a SE1, - ) -> Result<(Self::L2, Self::SE2), Self::Error>; - - fn visit_embed_squash(self, embed: &'a E1) -> Result<Ret, Self::Error>; - - fn visit_note_squash( - self, - note: &'a N1, - subexpr: &'a SE1, - ) -> Result<Ret, Self::Error>; - - // Called with the result of the map, in the non-embed/note case. - // Useful to change the result type, and/or avoid some loss of info - fn visit_resulting_exprf( - result: ExprF<Self::SE2, Self::L2, Self::N2, Self::E2>, - ) -> Result<Ret, Self::Error>; -} - -impl<'a, T, Ret, SE1, L1, N1, E1> - GenericVisitor<&'a ExprF<SE1, L1, N1, E1>, Result<Ret, T::Error>> for T -where - L1: Ord, - T::L2: Ord, - T: ExprFVeryGenericVisitor<'a, Ret, SE1, L1, N1, E1>, -{ - fn visit(self, input: &'a ExprF<SE1, L1, N1, E1>) -> Result<Ret, T::Error> { - fn vec<'a, T, U, Err, F: FnMut(&'a T) -> Result<U, Err>>( - x: &'a [T], - f: F, - ) -> Result<Vec<U>, Err> { - x.iter().map(f).collect() - } - fn opt<'a, T, U, Err, F: FnOnce(&'a T) -> Result<U, Err>>( - x: &'a Option<T>, - f: F, - ) -> Result<Option<U>, Err> { - Ok(match x { - Some(x) => Some(f(x)?), - None => None, - }) - } - fn btmap<'a, V, Ret, SE, L, N, E>( - x: &'a BTreeMap<L, SE>, - mut v: V, - ) -> Result<BTreeMap<V::L2, V::SE2>, V::Error> - where - L: Ord, - V::L2: Ord, - V: ExprFVeryGenericVisitor<'a, Ret, SE, L, N, E>, - { - x.iter() - .map(|(k, x)| Ok((v.visit_label(k)?, v.visit_subexpr(x)?))) - .collect() - } - fn btoptmap<'a, V, Ret, SE, L, N, E>( - x: &'a BTreeMap<L, Option<SE>>, - mut v: V, - ) -> Result<BTreeMap<V::L2, Option<V::SE2>>, V::Error> - where - L: Ord, - V::L2: Ord, - V: ExprFVeryGenericVisitor<'a, Ret, SE, L, N, E>, - { - x.iter() - .map(|(k, x)| { - Ok(( - v.visit_label(k)?, - match x { - Some(x) => Some(v.visit_subexpr(x)?), - None => None, - }, - )) - }) - .collect() - } - - let mut v = self; - use crate::ExprF::*; - T::visit_resulting_exprf(match input { - Var(V(l, n)) => Var(V(v.visit_label(l)?, *n)), - Lam(l, t, e) => { - let t = v.visit_subexpr(t)?; - let (l, e) = v.visit_binder(l, e)?; - Lam(l, t, e) - } - Pi(l, t, e) => { - let t = v.visit_subexpr(t)?; - let (l, e) = v.visit_binder(l, e)?; - Pi(l, t, e) - } - Let(l, t, a, e) => { - let t = opt(t, &mut |e| v.visit_subexpr(e))?; - let a = v.visit_subexpr(a)?; - let (l, e) = v.visit_binder(l, e)?; - Let(l, t, a, e) - } - App(f, a) => App(v.visit_subexpr(f)?, v.visit_subexpr(a)?), - Annot(x, t) => Annot(v.visit_subexpr(x)?, v.visit_subexpr(t)?), - Const(k) => Const(*k), - Builtin(v) => Builtin(*v), - BoolLit(b) => BoolLit(*b), - NaturalLit(n) => NaturalLit(*n), - IntegerLit(n) => IntegerLit(*n), - DoubleLit(n) => DoubleLit(*n), - TextLit(t) => TextLit(t.traverse_ref(|e| v.visit_subexpr(e))?), - BinOp(o, x, y) => { - BinOp(*o, v.visit_subexpr(x)?, v.visit_subexpr(y)?) - } - BoolIf(b, t, f) => BoolIf( - v.visit_subexpr(b)?, - v.visit_subexpr(t)?, - v.visit_subexpr(f)?, - ), - EmptyListLit(t) => EmptyListLit(v.visit_subexpr(t)?), - NEListLit(es) => NEListLit(vec(es, |e| v.visit_subexpr(e))?), - OldOptionalLit(x, t) => OldOptionalLit( - opt(x, |e| v.visit_subexpr(e))?, - v.visit_subexpr(t)?, - ), - SomeLit(e) => SomeLit(v.visit_subexpr(e)?), - RecordType(kts) => RecordType(btmap(kts, v)?), - RecordLit(kvs) => RecordLit(btmap(kvs, v)?), - UnionType(kts) => UnionType(btoptmap(kts, v)?), - UnionLit(k, x, kts) => UnionLit( - v.visit_label(k)?, - v.visit_subexpr(x)?, - btoptmap(kts, v)?, - ), - Merge(x, y, t) => Merge( - v.visit_subexpr(x)?, - v.visit_subexpr(y)?, - opt(t, |e| v.visit_subexpr(e))?, - ), - Field(e, l) => Field(v.visit_subexpr(e)?, v.visit_label(l)?), - Projection(e, ls) => { - Projection(v.visit_subexpr(e)?, vec(ls, |l| v.visit_label(l))?) - } - Note(n, e) => return v.visit_note_squash(n, e), - Embed(a) => return v.visit_embed_squash(a), - }) - } -} - -/// Like ExprFVeryGenericVisitor, but sets the return -/// type to ExprF<_> -pub trait ExprFFallibleVisitor<'a, SE1, SE2, L1, L2, N1, N2, E1, E2>: - Sized -{ - type Error; - - fn visit_subexpr(&mut self, subexpr: &'a SE1) -> Result<SE2, Self::Error>; - fn visit_label(&mut self, label: &'a L1) -> Result<L2, Self::Error>; - fn visit_note(self, note: &'a N1) -> Result<N2, Self::Error>; - fn visit_embed(self, embed: &'a E1) -> Result<E2, Self::Error>; - - fn visit_subexpr_under_binder( - mut self, - _label: &'a L1, - subexpr: &'a SE1, - ) -> Result<SE2, Self::Error> { - self.visit_subexpr(subexpr) - } - - fn visit_binder( - mut self, - label: &'a L1, - subexpr: &'a SE1, - ) -> Result<(L2, SE2), Self::Error> { - Ok(( - self.visit_label(label)?, - self.visit_subexpr_under_binder(label, subexpr)?, - )) - } - - fn visit_embed_squash( - self, - embed: &'a E1, - ) -> Result<ExprF<SE2, L2, N2, E2>, Self::Error> { - Ok(ExprF::Embed(self.visit_embed(embed)?)) - } - - fn visit_note_squash( - mut self, - note: &'a N1, - subexpr: &'a SE1, - ) -> Result<ExprF<SE2, L2, N2, E2>, Self::Error> { - let subexpr = self.visit_subexpr(subexpr)?; - let note = self.visit_note(note)?; - Ok(ExprF::Note(note, subexpr)) - } -} - -impl<'a, T, SE1, SE2, L1, L2, N1, N2, E1, E2> - ExprFVeryGenericVisitor<'a, ExprF<SE2, L2, N2, E2>, SE1, L1, N1, E1> for T -where - T: ExprFFallibleVisitor<'a, SE1, SE2, L1, L2, N1, N2, E1, E2>, -{ - type Error = T::Error; - type SE2 = SE2; - type L2 = L2; - type N2 = N2; - type E2 = E2; - - fn visit_subexpr( - &mut self, - subexpr: &'a SE1, - ) -> Result<Self::SE2, Self::Error> { - self.visit_subexpr(subexpr) - } - - fn visit_label(&mut self, label: &'a L1) -> Result<Self::L2, Self::Error> { - self.visit_label(label) - } - - fn visit_binder( - self, - label: &'a L1, - subexpr: &'a SE1, - ) -> Result<(Self::L2, Self::SE2), Self::Error> { - self.visit_binder(label, subexpr) - } - - fn visit_embed_squash( - self, - embed: &'a E1, - ) -> Result<ExprF<SE2, L2, N2, E2>, Self::Error> { - self.visit_embed_squash(embed) - } - - fn visit_note_squash( - self, - note: &'a N1, - subexpr: &'a SE1, - ) -> Result<ExprF<SE2, L2, N2, E2>, Self::Error> { - self.visit_note_squash(note, subexpr) - } - - // Called with the result of the map, in the non-embed/note case. - // Useful to change the result type, and/or avoid some loss of info - fn visit_resulting_exprf( - result: ExprF<Self::SE2, Self::L2, Self::N2, Self::E2>, - ) -> Result<ExprF<SE2, L2, N2, E2>, Self::Error> { - Ok(result) - } -} - -/// Like ExprFFallibleVisitor, but without the error handling. -pub trait ExprFInFallibleVisitor<'a, SE1, SE2, L1, L2, N1, N2, E1, E2>: - Sized -{ - fn visit_subexpr(&mut self, subexpr: &'a SE1) -> SE2; - fn visit_label(&mut self, label: &'a L1) -> L2; - fn visit_note(self, note: &'a N1) -> N2; - fn visit_embed(self, embed: &'a E1) -> E2; - - fn visit_subexpr_under_binder( - mut self, - _label: &'a L1, - subexpr: &'a SE1, - ) -> SE2 { - self.visit_subexpr(subexpr) - } - - fn visit_binder(mut self, label: &'a L1, subexpr: &'a SE1) -> (L2, SE2) { - ( - self.visit_label(label), - self.visit_subexpr_under_binder(label, subexpr), - ) - } - - fn visit_embed_squash(self, embed: &'a E1) -> ExprF<SE2, L2, N2, E2> { - ExprF::Embed(self.visit_embed(embed)) - } - - fn visit_note_squash( - mut self, - note: &'a N1, - subexpr: &'a SE1, - ) -> ExprF<SE2, L2, N2, E2> { - let subexpr = self.visit_subexpr(subexpr); - let note = self.visit_note(note); - ExprF::Note(note, subexpr) - } -} - -struct InfallibleWrapper<T>(T); - -impl<'a, T, SE1, SE2, L1, L2, N1, N2, E1, E2> - ExprFFallibleVisitor<'a, SE1, SE2, L1, L2, N1, N2, E1, E2> - for InfallibleWrapper<T> -where - T: ExprFInFallibleVisitor<'a, SE1, SE2, L1, L2, N1, N2, E1, E2>, -{ - type Error = X; - - fn visit_subexpr(&mut self, subexpr: &'a SE1) -> Result<SE2, Self::Error> { - Ok(self.0.visit_subexpr(subexpr)) - } - fn visit_label(&mut self, label: &'a L1) -> Result<L2, Self::Error> { - Ok(self.0.visit_label(label)) - } - fn visit_note(self, note: &'a N1) -> Result<N2, Self::Error> { - Ok(self.0.visit_note(note)) - } - fn visit_embed(self, embed: &'a E1) -> Result<E2, Self::Error> { - Ok(self.0.visit_embed(embed)) - } - - fn visit_binder( - self, - label: &'a L1, - subexpr: &'a SE1, - ) -> Result<(L2, SE2), Self::Error> { - Ok(self.0.visit_binder(label, subexpr)) - } - - fn visit_embed_squash( - self, - embed: &'a E1, - ) -> Result<ExprF<SE2, L2, N2, E2>, Self::Error> { - Ok(self.0.visit_embed_squash(embed)) - } - - fn visit_note_squash( - self, - note: &'a N1, - subexpr: &'a SE1, - ) -> Result<ExprF<SE2, L2, N2, E2>, Self::Error> { - Ok(self.0.visit_note_squash(note, subexpr)) - } -} - -impl<'a, T, SE1, SE2, L1, L2, N1, N2, E1, E2> - GenericVisitor<&'a ExprF<SE1, L1, N1, E1>, ExprF<SE2, L2, N2, E2>> for T -where - L1: Ord, - L2: Ord, - T: ExprFInFallibleVisitor<'a, SE1, SE2, L1, L2, N1, N2, E1, E2>, -{ - fn visit( - self, - input: &'a ExprF<SE1, L1, N1, E1>, - ) -> ExprF<SE2, L2, N2, E2> { - trivial_result(InfallibleWrapper(self).visit(input)) - } -} - -pub struct TraverseRefWithBindersVisitor<F1, F2, F3, F4, F5> { - pub visit_subexpr: F1, - pub visit_under_binder: F2, - pub visit_note: F3, - pub visit_embed: F4, - pub visit_label: F5, -} - -impl<'a, SE, L, N, E, SE2, L2, N2, E2, Err, F1, F2, F3, F4, F5> - ExprFFallibleVisitor<'a, SE, SE2, L, L2, N, N2, E, E2> - for TraverseRefWithBindersVisitor<F1, F2, F3, F4, F5> -where - SE: 'a, - L: 'a, - N: 'a, - E: 'a, - L: Ord, - L2: Ord, - F1: FnMut(&'a SE) -> Result<SE2, Err>, - F2: FnOnce(&'a L, &'a SE) -> Result<SE2, Err>, - F3: FnOnce(&'a N) -> Result<N2, Err>, - F4: FnOnce(&'a E) -> Result<E2, Err>, - F5: FnMut(&'a L) -> Result<L2, Err>, -{ - type Error = Err; - - fn visit_subexpr(&mut self, subexpr: &'a SE) -> Result<SE2, Self::Error> { - (self.visit_subexpr)(subexpr) - } - fn visit_subexpr_under_binder( - self, - label: &'a L, - subexpr: &'a SE, - ) -> Result<SE2, Self::Error> { - (self.visit_under_binder)(label, subexpr) - } - fn visit_note(self, note: &'a N) -> Result<N2, Self::Error> { - (self.visit_note)(note) - } - fn visit_embed(self, embed: &'a E) -> Result<E2, Self::Error> { - (self.visit_embed)(embed) - } - fn visit_label(&mut self, label: &'a L) -> Result<L2, Self::Error> { - (self.visit_label)(label) - } -} - -pub struct TraverseRefVisitor<F1, F2, F3, F4> { - pub visit_subexpr: F1, - pub visit_note: F2, - pub visit_embed: F3, - pub visit_label: F4, -} - -impl<'a, SE, L, N, E, SE2, L2, N2, E2, Err, F1, F2, F3, F4> - ExprFFallibleVisitor<'a, SE, SE2, L, L2, N, N2, E, E2> - for TraverseRefVisitor<F1, F2, F3, F4> -where - SE: 'a, - L: 'a, - N: 'a, - E: 'a, - L: Ord, - L2: Ord, - F1: FnMut(&'a SE) -> Result<SE2, Err>, - F2: FnOnce(&'a N) -> Result<N2, Err>, - F3: FnOnce(&'a E) -> Result<E2, Err>, - F4: FnMut(&'a L) -> Result<L2, Err>, -{ - type Error = Err; - - fn visit_subexpr(&mut self, subexpr: &'a SE) -> Result<SE2, Self::Error> { - (self.visit_subexpr)(subexpr) - } - fn visit_note(self, note: &'a N) -> Result<N2, Self::Error> { - (self.visit_note)(note) - } - fn visit_embed(self, embed: &'a E) -> Result<E2, Self::Error> { - (self.visit_embed)(embed) - } - fn visit_label(&mut self, label: &'a L) -> Result<L2, Self::Error> { - (self.visit_label)(label) - } -} - -pub struct TraverseEmbedVisitor<F1>(pub F1); - -impl<'a, 'b, N, E, E2, Err, F1> - ExprFFallibleVisitor< - 'a, - SubExpr<N, E>, - SubExpr<N, E2>, - Label, - Label, - N, - N, - E, - E2, - > for &'b mut TraverseEmbedVisitor<F1> -where - N: Clone + 'a, - F1: FnMut(&E) -> Result<E2, Err>, -{ - type Error = Err; - - fn visit_subexpr( - &mut self, - subexpr: &'a SubExpr<N, E>, - ) -> Result<SubExpr<N, E2>, Self::Error> { - Ok(rc(subexpr.as_ref().visit(&mut **self)?)) - } - fn visit_note(self, note: &'a N) -> Result<N, Self::Error> { - Ok(N::clone(note)) - } - fn visit_embed(self, embed: &'a E) -> Result<E2, Self::Error> { - (self.0)(embed) - } - fn visit_label(&mut self, label: &'a Label) -> Result<Label, Self::Error> { - Ok(Label::clone(label)) - } -} - -pub struct SquashEmbedVisitor<F1>(pub F1); - -impl<'a, 'b, N, E1, E2, F1> - ExprFVeryGenericVisitor<'a, SubExpr<N, E2>, SubExpr<N, E1>, Label, N, E1> - for &'b mut SquashEmbedVisitor<F1> -where - N: Clone + 'a, - F1: FnMut(&E1) -> SubExpr<N, E2>, -{ - type Error = X; - type SE2 = SubExpr<N, E2>; - type L2 = Label; - type N2 = N; - type E2 = E2; - - fn visit_subexpr( - &mut self, - subexpr: &'a SubExpr<N, E1>, - ) -> Result<Self::SE2, Self::Error> { - Ok(subexpr.as_ref().visit(&mut **self)?) - } - - fn visit_label( - &mut self, - label: &'a Label, - ) -> Result<Self::L2, Self::Error> { - Ok(Label::clone(label)) - } - - fn visit_binder( - mut self, - label: &'a Label, - subexpr: &'a SubExpr<N, E1>, - ) -> Result<(Self::L2, Self::SE2), Self::Error> { - Ok((self.visit_label(label)?, self.visit_subexpr(subexpr)?)) - } - - fn visit_embed_squash( - self, - embed: &'a E1, - ) -> Result<SubExpr<N, E2>, Self::Error> { - Ok((self.0)(embed)) - } - - fn visit_note_squash( - mut self, - note: &'a N, - subexpr: &'a SubExpr<N, E1>, - ) -> Result<SubExpr<N, E2>, Self::Error> { - let subexpr = self.visit_subexpr(subexpr)?; - let note = N::clone(note); - Ok(rc(ExprF::Note(note, subexpr))) - } - - // Called with the result of the map, in the non-embed/note case. - // Useful to change the result type, and/or avoid some loss of info - fn visit_resulting_exprf( - result: ExprF<Self::SE2, Self::L2, Self::N2, Self::E2>, - ) -> Result<SubExpr<N, E2>, Self::Error> { - Ok(rc(result)) - } -} - -pub struct UnNoteVisitor; - -impl<'a, 'b, N, E> - ExprFInFallibleVisitor< - 'a, - SubExpr<N, E>, - SubExpr<X, E>, - Label, - Label, - N, - X, - E, - E, - > for &'b mut UnNoteVisitor -where - E: Clone + 'a, -{ - fn visit_subexpr(&mut self, subexpr: &'a SubExpr<N, E>) -> SubExpr<X, E> { - rc(subexpr.as_ref().visit(&mut **self)) - } - fn visit_note(self, _: &'a N) -> X { - unreachable!() - } - fn visit_note_squash( - self, - _: &'a N, - subexpr: &'a SubExpr<N, E>, - ) -> Expr<X, E> { - subexpr.as_ref().visit(self) - } - fn visit_embed(self, embed: &'a E) -> E { - E::clone(embed) - } - fn visit_label(&mut self, label: &'a Label) -> Label { - Label::clone(label) - } -} - -pub struct NoteAbsurdVisitor; - -impl<'a, 'b, N, E> - ExprFInFallibleVisitor< - 'a, - SubExpr<X, E>, - SubExpr<N, E>, - Label, - Label, - X, - N, - E, - E, - > for &'b mut NoteAbsurdVisitor -where - E: Clone + 'a, -{ - fn visit_subexpr(&mut self, subexpr: &'a SubExpr<X, E>) -> SubExpr<N, E> { - rc(subexpr.as_ref().visit(&mut **self)) - } - fn visit_note(self, note: &'a X) -> N { - match *note {} - } - fn visit_embed(self, embed: &'a E) -> E { - E::clone(embed) - } - fn visit_label(&mut self, label: &'a Label) -> Label { - Label::clone(label) - } -} - -pub struct EmbedAbsurdVisitor; - -impl<'a, 'b, N, E> - ExprFInFallibleVisitor< - 'a, - SubExpr<N, X>, - SubExpr<N, E>, - Label, - Label, - N, - N, - X, - E, - > for &'b mut EmbedAbsurdVisitor -where - N: Clone + 'a, -{ - fn visit_subexpr(&mut self, subexpr: &'a SubExpr<N, X>) -> SubExpr<N, E> { - rc(subexpr.as_ref().visit(&mut **self)) - } - fn visit_note(self, note: &'a N) -> N { - N::clone(note) - } - fn visit_embed(self, embed: &'a X) -> E { - match *embed {} - } - fn visit_label(&mut self, label: &'a Label) -> Label { - Label::clone(label) - } -} |