summaryrefslogtreecommitdiff
path: root/dhall_syntax/src/core
diff options
context:
space:
mode:
authorFintan Halpenny2019-09-02 23:09:26 +0100
committerFintan Halpenny2019-09-02 23:09:26 +0100
commit8553b398a5f97eed240f5360282e911392cab6ff (patch)
tree076d554b7e066cf854aa50f350096ce55e3bd691 /dhall_syntax/src/core
parente73f822b6972e8fa2e72b56ff5378b91bea1a5e6 (diff)
parent737abd9be6d35bbce784d9cf249edf7ad14677d6 (diff)
Merge remote-tracking branch 'origin/master' into fintan/canonicalize
Diffstat (limited to 'dhall_syntax/src/core')
-rw-r--r--dhall_syntax/src/core/context.rs2
-rw-r--r--dhall_syntax/src/core/expr.rs291
-rw-r--r--dhall_syntax/src/core/import.rs91
-rw-r--r--dhall_syntax/src/core/text.rs2
-rw-r--r--dhall_syntax/src/core/visitor.rs293
5 files changed, 250 insertions, 429 deletions
diff --git a/dhall_syntax/src/core/context.rs b/dhall_syntax/src/core/context.rs
index eeec121..6844baa 100644
--- a/dhall_syntax/src/core/context.rs
+++ b/dhall_syntax/src/core/context.rs
@@ -4,7 +4,7 @@ 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)`
+/// The `Context` is used for type-checking when `(a = Expr)`
///
/// * You create a `Context` using `empty` and `insert`
/// * You transform a `Context` using `fmap`
diff --git a/dhall_syntax/src/core/expr.rs b/dhall_syntax/src/core/expr.rs
index 6522cb1..51b6c47 100644
--- a/dhall_syntax/src/core/expr.rs
+++ b/dhall_syntax/src/core/expr.rs
@@ -8,14 +8,34 @@ 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 {
+pub fn trivial_result<T>(x: Result<T, !>) -> T {
match x {
Ok(x) => x,
- Err(e) => match e {},
+ Err(e) => e,
+ }
+}
+
+/// A location in the source text
+#[derive(Debug, Clone)]
+pub struct Span {
+ input: Rc<str>,
+ /// # Safety
+ ///
+ /// Must be a valid character boundary index into `input`.
+ start: usize,
+ /// # Safety
+ ///
+ /// Must be a valid character boundary index into `input`.
+ end: usize,
+}
+
+impl Span {
+ pub(crate) fn make(input: Rc<str>, sp: pest::Span) -> Self {
+ Span {
+ input,
+ start: sp.start(),
+ end: sp.end(),
+ }
}
}
@@ -31,6 +51,15 @@ impl PartialEq for NaiveDouble {
impl Eq for NaiveDouble {}
+impl std::hash::Hash for NaiveDouble {
+ fn hash<H>(&self, state: &mut H)
+ where
+ H: std::hash::Hasher,
+ {
+ self.0.to_bits().hash(state)
+ }
+}
+
impl From<f64> for NaiveDouble {
fn from(x: f64) -> Self {
NaiveDouble(x)
@@ -44,7 +73,7 @@ impl From<NaiveDouble> for f64 {
}
/// Constants for a pure type system
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Const {
Type,
Kind,
@@ -56,7 +85,7 @@ pub enum Const {
/// 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)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct V<Label>(pub Label, pub usize);
// This is only for the specific `Label` type, not generic
@@ -73,7 +102,7 @@ impl<'a> From<&'a Label> for V<Label> {
// Definition order must match precedence order for
// pretty-printing to work correctly
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum BinOp {
/// `x ? y`
ImportAlt,
@@ -104,7 +133,7 @@ pub enum BinOp {
}
/// Built-ins
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Builtin {
Bool,
Natural,
@@ -137,29 +166,34 @@ pub enum Builtin {
TextShow,
}
-pub type ParsedExpr = SubExpr<X, Import>;
-pub type ResolvedExpr = SubExpr<X, X>;
-pub type DhallExpr = ResolvedExpr;
+// Each node carries an annotation.
+#[derive(Debug, Clone)]
+pub struct Expr<Embed>(Box<(RawExpr<Embed>, Option<Span>)>);
-// Each node carries an annotation. In practice it's either X (no annotation) or a Span.
-#[derive(Debug)]
-pub struct SubExpr<Note, Embed>(Rc<(Expr<Note, Embed>, Option<Note>)>);
+pub type RawExpr<Embed> = ExprF<Expr<Embed>, Embed>;
-impl<Note, Embed: PartialEq> std::cmp::PartialEq for SubExpr<Note, Embed> {
+impl<Embed: PartialEq> std::cmp::PartialEq for Expr<Embed> {
fn eq(&self, other: &Self) -> bool {
self.0.as_ref().0 == other.0.as_ref().0
}
}
-impl<Note, Embed: Eq> std::cmp::Eq for SubExpr<Note, Embed> {}
+impl<Embed: Eq> std::cmp::Eq for Expr<Embed> {}
-pub type Expr<Note, Embed> = ExprF<SubExpr<Note, Embed>, Embed>;
+impl<Embed: std::hash::Hash> std::hash::Hash for Expr<Embed> {
+ fn hash<H>(&self, state: &mut H)
+ where
+ H: std::hash::Hasher,
+ {
+ (self.0).0.hash(state)
+ }
+}
/// 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)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ExprF<SubExpr, Embed> {
Const(Const),
/// `x`
@@ -209,11 +243,15 @@ pub enum ExprF<SubExpr, Embed> {
UnionType(DupTreeMap<Label, Option<SubExpr>>),
/// `merge x y : t`
Merge(SubExpr, SubExpr, Option<SubExpr>),
+ /// `toMap x : t`
+ ToMap(SubExpr, Option<SubExpr>),
/// `e.x`
Field(SubExpr, Label),
/// `e.{ x, y, z }`
Projection(SubExpr, DupTreeSet<Label>),
- /// Embeds an import or the result of resolving the import
+ /// `./some/path`
+ Import(Import<SubExpr>),
+ /// Embeds the result of resolving an import
Embed(Embed),
}
@@ -225,229 +263,134 @@ impl<SE, E> ExprF<SE, E> {
v.visit(self)
}
- pub fn traverse_ref_with_special_handling_of_binders<'a, SE2, E2, Err>(
+ pub fn traverse_ref_with_special_handling_of_binders<'a, SE2, Err>(
&'a self,
visit_subexpr: impl FnMut(&'a SE) -> Result<SE2, Err>,
visit_under_binder: impl FnOnce(&'a Label, &'a SE) -> Result<SE2, Err>,
- visit_embed: impl FnOnce(&'a E) -> Result<E2, Err>,
- ) -> Result<ExprF<SE2, E2>, Err> {
+ ) -> Result<ExprF<SE2, E>, Err>
+ where
+ E: Clone,
+ {
self.visit(visitor::TraverseRefWithBindersVisitor {
visit_subexpr,
visit_under_binder,
- visit_embed,
})
}
- fn traverse_ref<'a, SE2, E2, Err>(
+ fn traverse_ref<'a, SE2, Err>(
&'a self,
visit_subexpr: impl FnMut(&'a SE) -> Result<SE2, Err>,
- visit_embed: impl FnOnce(&'a E) -> Result<E2, Err>,
- ) -> Result<ExprF<SE2, E2>, Err> {
- self.visit(visitor::TraverseRefVisitor {
- visit_subexpr,
- visit_embed,
- })
+ ) -> Result<ExprF<SE2, E>, Err>
+ where
+ E: Clone,
+ {
+ self.visit(visitor::TraverseRefVisitor { visit_subexpr })
}
- pub fn map_ref_with_special_handling_of_binders<'a, SE2, E2>(
+ pub fn map_ref_with_special_handling_of_binders<'a, SE2>(
&'a self,
mut map_subexpr: impl FnMut(&'a SE) -> SE2,
mut map_under_binder: impl FnMut(&'a Label, &'a SE) -> SE2,
- map_embed: impl FnOnce(&'a E) -> E2,
- ) -> ExprF<SE2, E2> {
+ ) -> ExprF<SE2, E>
+ where
+ E: Clone,
+ {
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_embed(x)),
))
}
- pub fn map_ref<'a, SE2, E2>(
+ pub fn map_ref<'a, SE2>(
&'a self,
mut map_subexpr: impl FnMut(&'a SE) -> SE2,
- map_embed: impl FnOnce(&'a E) -> E2,
- ) -> ExprF<SE2, E2> {
- trivial_result(
- self.traverse_ref(|x| Ok(map_subexpr(x)), |x| Ok(map_embed(x))),
- )
- }
-
- pub fn traverse_ref_simple<'a, SE2, Err>(
- &'a self,
- visit_subexpr: impl FnMut(&'a SE) -> Result<SE2, Err>,
- ) -> Result<ExprF<SE2, E>, Err>
- where
- E: Clone,
- {
- self.traverse_ref(visit_subexpr, |x| Ok(E::clone(x)))
- }
-
- pub fn map_ref_simple<'a, SE2>(
- &'a self,
- map_subexpr: impl Fn(&'a SE) -> SE2,
) -> ExprF<SE2, E>
where
E: Clone,
{
- self.map_ref(map_subexpr, E::clone)
+ trivial_result(self.traverse_ref(|x| Ok(map_subexpr(x))))
}
}
-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))))
- }
-
+impl<E> RawExpr<E> {
pub fn traverse_resolve<E2, Err>(
&self,
- visit_embed: impl FnMut(&E) -> Result<E2, Err>,
- ) -> Result<Expr<N, E2>, Err>
- where
- N: Clone,
- {
+ visit_import: impl FnMut(&Import<Expr<E2>>) -> Result<E2, Err>,
+ ) -> Result<RawExpr<E2>, Err> {
self.traverse_resolve_with_visitor(&mut visitor::ResolveVisitor(
- visit_embed,
+ visit_import,
))
}
pub(crate) fn traverse_resolve_with_visitor<E2, Err, F1>(
&self,
visitor: &mut visitor::ResolveVisitor<F1>,
- ) -> Result<Expr<N, E2>, Err>
+ ) -> Result<RawExpr<E2>, Err>
where
- N: Clone,
- F1: FnMut(&E) -> Result<E2, Err>,
+ F1: FnMut(&Import<Expr<E2>>) -> Result<E2, Err>,
{
match self {
ExprF::BinOp(BinOp::ImportAlt, l, r) => l
.as_ref()
.traverse_resolve_with_visitor(visitor)
- .or(r.as_ref().traverse_resolve_with_visitor(visitor)),
- _ => self.visit(visitor),
+ .or_else(|_| r.as_ref().traverse_resolve_with_visitor(visitor)),
+ _ => {
+ let e = self.visit(&mut *visitor)?;
+ Ok(match &e {
+ ExprF::Import(import) => ExprF::Embed((visitor.0)(import)?),
+ _ => e,
+ })
+ }
}
}
}
-impl Expr<X, X> {
- pub fn absurd<N, E>(&self) -> Expr<N, E> {
- self.visit(&mut visitor::AbsurdVisitor)
- }
-}
-
-impl<E: Clone> Expr<X, E> {
- pub fn note_absurd<N>(&self) -> Expr<N, E> {
- self.visit(&mut visitor::NoteAbsurdVisitor)
- }
-}
-
-impl<N, E> SubExpr<N, E> {
- pub fn as_ref(&self) -> &Expr<N, E> {
+impl<E> Expr<E> {
+ pub fn as_ref(&self) -> &RawExpr<E> {
&self.0.as_ref().0
}
- pub fn new(x: Expr<N, E>, n: N) -> Self {
- SubExpr(Rc::new((x, Some(n))))
+ pub fn new(x: RawExpr<E>, n: Span) -> Self {
+ Expr(Box::new((x, Some(n))))
}
- pub fn from_expr_no_note(x: Expr<N, E>) -> Self {
- SubExpr(Rc::new((x, None)))
+ pub fn from_expr_no_span(x: RawExpr<E>) -> Self {
+ Expr(Box::new((x, None)))
}
pub fn from_builtin(b: Builtin) -> Self {
- SubExpr::from_expr_no_note(ExprF::Builtin(b))
+ Expr::from_expr_no_span(ExprF::Builtin(b))
}
- pub fn rewrap<E2>(&self, x: Expr<N, E2>) -> SubExpr<N, E2>
- where
- N: Clone,
- {
- SubExpr(Rc::new((x, (self.0).1.clone())))
- }
-
- pub fn traverse_embed<E2, Err>(
- &self,
- visit_embed: impl FnMut(&E) -> Result<E2, Err>,
- ) -> Result<SubExpr<N, E2>, Err>
- where
- N: Clone,
- {
- Ok(self.rewrap(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,
- {
- self.rewrap(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
- where
- N: Clone,
- {
- match self.as_ref() {
- ExprF::Embed(_) => SubExpr::clone(self),
- // This calls ExprF::map_ref
- e => self.rewrap(e.map_ref_with_special_handling_of_binders(
- map_expr,
- map_under_binder,
- |_| unreachable!(),
- )),
- }
+ pub fn rewrap<E2>(&self, x: RawExpr<E2>) -> Expr<E2> {
+ Expr(Box::new((x, (self.0).1.clone())))
}
+}
+impl<E> Expr<E> {
pub fn traverse_resolve<E2, Err>(
&self,
- visit_embed: impl FnMut(&E) -> Result<E2, Err>,
- ) -> Result<SubExpr<N, E2>, Err>
- where
- N: Clone,
- {
- Ok(self.rewrap(self.as_ref().traverse_resolve(visit_embed)?))
- }
-}
-
-impl SubExpr<X, X> {
- pub fn absurd<N: Clone, T>(&self) -> SubExpr<N, T> {
- SubExpr::from_expr_no_note(self.as_ref().absurd())
+ visit_import: impl FnMut(&Import<Expr<E2>>) -> Result<E2, Err>,
+ ) -> Result<Expr<E2>, Err> {
+ Ok(self.rewrap(self.as_ref().traverse_resolve(visit_import)?))
}
}
-impl<E: Clone> SubExpr<X, E> {
- pub fn note_absurd<N>(&self) -> SubExpr<N, E> {
- SubExpr::from_expr_no_note(self.as_ref().note_absurd())
- }
+// Should probably rename this
+pub fn rc<E>(x: RawExpr<E>) -> Expr<E> {
+ Expr::from_expr_no_span(x)
}
-impl<N, E> Clone for SubExpr<N, E> {
- fn clone(&self) -> Self {
- SubExpr(Rc::clone(&self.0))
- }
+pub(crate) fn spanned(
+ span: Span,
+ x: crate::parser::ParsedRawExpr,
+) -> crate::parser::ParsedExpr {
+ Expr::new(x, span)
}
-
-// Should probably rename this
-pub fn rc<E>(x: Expr<X, E>) -> SubExpr<X, E> {
- SubExpr::from_expr_no_note(x)
+pub(crate) fn unspanned(
+ x: crate::parser::ParsedRawExpr,
+) -> crate::parser::ParsedExpr {
+ Expr::from_expr_no_span(x)
}
/// Add an isize to an usize
diff --git a/dhall_syntax/src/core/import.rs b/dhall_syntax/src/core/import.rs
index 4aad70d..ef696b9 100644
--- a/dhall_syntax/src/core/import.rs
+++ b/dhall_syntax/src/core/import.rs
@@ -16,32 +16,22 @@ pub struct File {
pub file_path: Vec<String>,
}
-impl IntoIterator for File {
- type Item = String;
- type IntoIter = ::std::vec::IntoIter<Self::Item>;
-
- fn into_iter(self) -> Self::IntoIter {
- self.file_path.into_iter()
- }
-}
-
/// The location of import (i.e. local vs. remote vs. environment)
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum ImportLocation {
+pub enum ImportLocation<SubExpr> {
Local(FilePrefix, File),
- Remote(URL),
+ Remote(URL<SubExpr>),
Env(String),
Missing,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct URL {
+pub struct URL<SubExpr> {
pub scheme: Scheme,
pub authority: String,
pub path: File,
pub query: Option<String>,
- // TODO: implement inline headers
- pub headers: Option<Box<ImportHashed>>,
+ pub headers: Option<SubExpr>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -63,17 +53,56 @@ pub enum Hash {
SHA256(Vec<u8>),
}
+/// Reference to an external resource
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct ImportHashed {
- pub location: ImportLocation,
+pub struct Import<SubExpr> {
+ pub mode: ImportMode,
+ pub location: ImportLocation<SubExpr>,
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,
+impl<SE> URL<SE> {
+ pub fn visit_subexpr<'a, Err, SE2>(
+ &'a self,
+ f: impl FnOnce(&'a SE) -> Result<SE2, Err>,
+ ) -> Result<URL<SE2>, Err> {
+ let headers = self.headers.as_ref().map(f).transpose()?;
+ Ok(URL {
+ scheme: self.scheme,
+ authority: self.authority.clone(),
+ path: self.path.clone(),
+ query: self.query.clone(),
+ headers,
+ })
+ }
+}
+
+impl<SE> ImportLocation<SE> {
+ pub fn visit_subexpr<'a, Err, SE2>(
+ &'a self,
+ f: impl FnOnce(&'a SE) -> Result<SE2, Err>,
+ ) -> Result<ImportLocation<SE2>, Err> {
+ use ImportLocation::*;
+ Ok(match self {
+ Local(prefix, path) => Local(*prefix, path.clone()),
+ Remote(url) => Remote(url.visit_subexpr(f)?),
+ Env(env) => Env(env.clone()),
+ Missing => Missing,
+ })
+ }
+}
+
+impl<SE> Import<SE> {
+ pub fn visit_subexpr<'a, Err, SE2>(
+ &'a self,
+ f: impl FnOnce(&'a SE) -> Result<SE2, Err>,
+ ) -> Result<Import<SE2>, Err> {
+ Ok(Import {
+ mode: self.mode,
+ location: self.location.visit_subexpr(f)?,
+ hash: self.hash.clone(),
+ })
+ }
}
pub trait Canonicalize {
@@ -83,7 +112,7 @@ pub trait Canonicalize {
impl Canonicalize for File {
fn canonicalize(&self) -> File {
let mut file_path = Vec::new();
- let mut file_path_components = self.clone().into_iter();
+ let mut file_path_components = self.file_path.clone().into_iter();
loop {
let component = file_path_components.next();
@@ -128,8 +157,8 @@ impl Canonicalize for File {
}
}
-impl Canonicalize for ImportLocation {
- fn canonicalize(&self) -> ImportLocation {
+impl<SubExpr: Copy> Canonicalize for ImportLocation<SubExpr> {
+ fn canonicalize(&self) -> ImportLocation<SubExpr> {
match self {
ImportLocation::Local(prefix, file) => ImportLocation::Local(*prefix, file.canonicalize()),
ImportLocation::Remote(url) => ImportLocation::Remote(URL {
@@ -137,22 +166,10 @@ impl Canonicalize for ImportLocation {
authority: url.authority.clone(),
path: url.path.canonicalize(),
query: url.query.clone(),
- headers: url.headers.clone().map(|boxed_hash| Box::new(boxed_hash.canonicalize())),
+ headers: url.headers.clone(),
}),
ImportLocation::Env(name) => ImportLocation::Env(name.to_string()),
ImportLocation::Missing => ImportLocation::Missing,
}
}
}
-
-impl Canonicalize for ImportHashed {
- fn canonicalize(&self) -> ImportHashed {
- ImportHashed { hash: self.hash.clone(), location: self.location.canonicalize() }
- }
-}
-
-impl Canonicalize for Import {
- fn canonicalize(&self) -> Import {
- Import { mode: self.mode, location_hashed: self.location_hashed.canonicalize() }
- }
-}
diff --git a/dhall_syntax/src/core/text.rs b/dhall_syntax/src/core/text.rs
index 0ce1e6f..10fd68a 100644
--- a/dhall_syntax/src/core/text.rs
+++ b/dhall_syntax/src/core/text.rs
@@ -1,6 +1,6 @@
use std::iter::FromIterator;
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct InterpolatedText<SubExpr> {
head: String,
tail: Vec<(SubExpr, String)>,
diff --git a/dhall_syntax/src/core/visitor.rs b/dhall_syntax/src/core/visitor.rs
index 18f76d9..435771e 100644
--- a/dhall_syntax/src/core/visitor.rs
+++ b/dhall_syntax/src/core/visitor.rs
@@ -2,52 +2,56 @@ use crate::*;
use std::iter::FromIterator;
/// A way too generic Visitor trait.
-pub trait GenericVisitor<Input, Return>: Sized {
- fn visit(self, input: Input) -> Return;
+pub trait GenericVisitor<Input, Output>: Sized {
+ fn visit(self, input: Input) -> Output;
}
-/// 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, E1>: Sized {
+/// 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_ref_with_special_handling_of_binders` 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.
+pub trait ExprFFallibleVisitor<'a, SE1, SE2, E1, E2>: Sized {
type Error;
- type SE2;
- type E2;
- fn visit_subexpr(
- &mut self,
- subexpr: &'a SE1,
- ) -> Result<Self::SE2, Self::Error>;
+ fn visit_subexpr(&mut self, subexpr: &'a SE1) -> Result<SE2, Self::Error>;
+ fn visit_embed(self, embed: &'a E1) -> Result<E2, Self::Error>;
fn visit_subexpr_under_binder(
- self,
- label: &'a Label,
+ mut self,
+ _label: &'a Label,
subexpr: &'a SE1,
- ) -> Result<Self::SE2, Self::Error>;
+ ) -> Result<SE2, Self::Error> {
+ self.visit_subexpr(subexpr)
+ }
+}
- fn visit_embed_squash(self, embed: &'a E1) -> Result<Ret, Self::Error>;
+/// Like ExprFFallibleVisitor, but without the error handling.
+pub trait ExprFInFallibleVisitor<'a, SE1, SE2, E1, E2>: Sized {
+ fn visit_subexpr(&mut self, subexpr: &'a SE1) -> SE2;
+ fn visit_embed(self, embed: &'a E1) -> E2;
- // Called with the result of the map, in the non-embed case.
- // Useful to change the result type, and/or avoid some loss of info
- fn visit_resulting_exprf(
- result: ExprF<Self::SE2, Self::E2>,
- ) -> Result<Ret, Self::Error>;
+ fn visit_subexpr_under_binder(
+ mut self,
+ _label: &'a Label,
+ subexpr: &'a SE1,
+ ) -> SE2 {
+ self.visit_subexpr(subexpr)
+ }
}
-impl<'a, T, Ret, SE1, E1>
- GenericVisitor<&'a ExprF<SE1, E1>, Result<Ret, T::Error>> for T
+impl<'a, T, SE1, SE2, E1, E2>
+ GenericVisitor<&'a ExprF<SE1, E1>, Result<ExprF<SE2, E2>, T::Error>> for T
where
- T: ExprFVeryGenericVisitor<'a, Ret, SE1, E1>,
+ T: ExprFFallibleVisitor<'a, SE1, SE2, E1, E2>,
{
- fn visit(self, input: &'a ExprF<SE1, E1>) -> Result<Ret, T::Error> {
+ fn visit(
+ self,
+ input: &'a ExprF<SE1, E1>,
+ ) -> Result<ExprF<SE2, E2>, T::Error> {
fn vec<'a, T, U, Err, F: FnMut(&'a T) -> Result<U, Err>>(
x: &'a [T],
f: F,
@@ -63,27 +67,27 @@ where
None => None,
})
}
- fn dupmap<'a, V, Ret, SE, E, T>(
- x: impl IntoIterator<Item = (&'a Label, &'a SE)>,
+ fn dupmap<'a, V, SE1, SE2, E1, E2, T>(
+ x: impl IntoIterator<Item = (&'a Label, &'a SE1)>,
mut v: V,
) -> Result<T, V::Error>
where
- SE: 'a,
- T: FromIterator<(Label, V::SE2)>,
- V: ExprFVeryGenericVisitor<'a, Ret, SE, E>,
+ SE1: 'a,
+ T: FromIterator<(Label, SE2)>,
+ V: ExprFFallibleVisitor<'a, SE1, SE2, E1, E2>,
{
x.into_iter()
.map(|(k, x)| Ok((k.clone(), v.visit_subexpr(x)?)))
.collect()
}
- fn optdupmap<'a, V, Ret, SE, E, T>(
- x: impl IntoIterator<Item = (&'a Label, &'a Option<SE>)>,
+ fn optdupmap<'a, V, SE1, SE2, E1, E2, T>(
+ x: impl IntoIterator<Item = (&'a Label, &'a Option<SE1>)>,
mut v: V,
) -> Result<T, V::Error>
where
- SE: 'a,
- T: FromIterator<(Label, Option<V::SE2>)>,
- V: ExprFVeryGenericVisitor<'a, Ret, SE, E>,
+ SE1: 'a,
+ T: FromIterator<(Label, Option<SE2>)>,
+ V: ExprFFallibleVisitor<'a, SE1, SE2, E1, E2>,
{
x.into_iter()
.map(|(k, x)| {
@@ -100,7 +104,7 @@ where
let mut v = self;
use crate::ExprF::*;
- T::visit_resulting_exprf(match input {
+ Ok(match input {
Var(v) => Var(v.clone()),
Lam(l, t, e) => {
let t = v.visit_subexpr(t)?;
@@ -146,93 +150,25 @@ where
v.visit_subexpr(y)?,
opt(t, |e| v.visit_subexpr(e))?,
),
+ ToMap(x, t) => {
+ ToMap(v.visit_subexpr(x)?, opt(t, |e| v.visit_subexpr(e))?)
+ }
Field(e, l) => Field(v.visit_subexpr(e)?, l.clone()),
Projection(e, ls) => Projection(v.visit_subexpr(e)?, ls.clone()),
Assert(e) => Assert(v.visit_subexpr(e)?),
- Embed(a) => return v.visit_embed_squash(a),
+ Import(i) => Import(i.visit_subexpr(|e| v.visit_subexpr(e))?),
+ Embed(a) => Embed(v.visit_embed(a)?),
})
}
}
-/// Like ExprFVeryGenericVisitor, but sets the return
-/// type to ExprF<_>
-pub trait ExprFFallibleVisitor<'a, SE1, SE2, E1, E2>: Sized {
- type Error;
-
- fn visit_subexpr(&mut self, subexpr: &'a SE1) -> Result<SE2, Self::Error>;
- fn visit_embed(self, embed: &'a E1) -> Result<E2, Self::Error>;
-
- fn visit_subexpr_under_binder(
- mut self,
- _label: &'a Label,
- subexpr: &'a SE1,
- ) -> Result<SE2, Self::Error> {
- self.visit_subexpr(subexpr)
- }
-
- fn visit_embed_squash(
- self,
- embed: &'a E1,
- ) -> Result<ExprF<SE2, E2>, Self::Error> {
- Ok(ExprF::Embed(self.visit_embed(embed)?))
- }
-}
-
-impl<'a, T, SE1, SE2, E1, E2>
- ExprFVeryGenericVisitor<'a, ExprF<SE2, E2>, SE1, E1> for T
+impl<'a, T, SE1, SE2, E1, E2> GenericVisitor<&'a ExprF<SE1, E1>, ExprF<SE2, E2>>
+ for T
where
- T: ExprFFallibleVisitor<'a, SE1, SE2, E1, E2>,
+ T: ExprFInFallibleVisitor<'a, SE1, SE2, E1, E2>,
{
- type Error = T::Error;
- type SE2 = SE2;
- type E2 = E2;
-
- fn visit_subexpr(
- &mut self,
- subexpr: &'a SE1,
- ) -> Result<Self::SE2, Self::Error> {
- self.visit_subexpr(subexpr)
- }
-
- fn visit_subexpr_under_binder(
- self,
- label: &'a Label,
- subexpr: &'a SE1,
- ) -> Result<Self::SE2, Self::Error> {
- self.visit_subexpr_under_binder(label, subexpr)
- }
-
- fn visit_embed_squash(
- self,
- embed: &'a E1,
- ) -> Result<ExprF<SE2, E2>, Self::Error> {
- self.visit_embed_squash(embed)
- }
-
- // Called with the result of the map, in the non-embed case.
- // Useful to change the result type, and/or avoid some loss of info
- fn visit_resulting_exprf(
- result: ExprF<Self::SE2, Self::E2>,
- ) -> Result<ExprF<SE2, E2>, Self::Error> {
- Ok(result)
- }
-}
-
-/// Like ExprFFallibleVisitor, but without the error handling.
-pub trait ExprFInFallibleVisitor<'a, SE1, SE2, E1, E2>: Sized {
- fn visit_subexpr(&mut self, subexpr: &'a SE1) -> SE2;
- fn visit_embed(self, embed: &'a E1) -> E2;
-
- fn visit_subexpr_under_binder(
- mut self,
- _label: &'a Label,
- subexpr: &'a SE1,
- ) -> SE2 {
- self.visit_subexpr(subexpr)
- }
-
- fn visit_embed_squash(self, embed: &'a E1) -> ExprF<SE2, E2> {
- ExprF::Embed(self.visit_embed(embed))
+ fn visit(self, input: &'a ExprF<SE1, E1>) -> ExprF<SE2, E2> {
+ trivial_result(InfallibleWrapper(self).visit(input))
}
}
@@ -243,7 +179,7 @@ impl<'a, T, SE1, SE2, E1, E2> ExprFFallibleVisitor<'a, SE1, SE2, E1, E2>
where
T: ExprFInFallibleVisitor<'a, SE1, SE2, E1, E2>,
{
- type Error = X;
+ type Error = !;
fn visit_subexpr(&mut self, subexpr: &'a SE1) -> Result<SE2, Self::Error> {
Ok(self.0.visit_subexpr(subexpr))
@@ -259,40 +195,20 @@ where
) -> Result<SE2, Self::Error> {
Ok(self.0.visit_subexpr_under_binder(label, subexpr))
}
-
- fn visit_embed_squash(
- self,
- embed: &'a E1,
- ) -> Result<ExprF<SE2, E2>, Self::Error> {
- Ok(self.0.visit_embed_squash(embed))
- }
}
-impl<'a, T, SE1, SE2, E1, E2> GenericVisitor<&'a ExprF<SE1, E1>, ExprF<SE2, E2>>
- for T
-where
- T: ExprFInFallibleVisitor<'a, SE1, SE2, E1, E2>,
-{
- fn visit(self, input: &'a ExprF<SE1, E1>) -> ExprF<SE2, E2> {
- trivial_result(InfallibleWrapper(self).visit(input))
- }
-}
-
-pub struct TraverseRefWithBindersVisitor<F1, F2, F4> {
+pub struct TraverseRefWithBindersVisitor<F1, F2> {
pub visit_subexpr: F1,
pub visit_under_binder: F2,
- pub visit_embed: F4,
}
-impl<'a, SE, E, SE2, E2, Err, F1, F2, F4>
- ExprFFallibleVisitor<'a, SE, SE2, E, E2>
- for TraverseRefWithBindersVisitor<F1, F2, F4>
+impl<'a, SE, E, SE2, Err, F1, F2> ExprFFallibleVisitor<'a, SE, SE2, E, E>
+ for TraverseRefWithBindersVisitor<F1, F2>
where
SE: 'a,
- E: 'a,
+ E: 'a + Clone,
F1: FnMut(&'a SE) -> Result<SE2, Err>,
F2: FnOnce(&'a Label, &'a SE) -> Result<SE2, Err>,
- F4: FnOnce(&'a E) -> Result<E2, Err>,
{
type Error = Err;
@@ -306,107 +222,52 @@ where
) -> Result<SE2, Self::Error> {
(self.visit_under_binder)(label, subexpr)
}
- fn visit_embed(self, embed: &'a E) -> Result<E2, Self::Error> {
- (self.visit_embed)(embed)
+ fn visit_embed(self, embed: &'a E) -> Result<E, Self::Error> {
+ Ok(embed.clone())
}
}
-pub struct TraverseRefVisitor<F1, F3> {
+pub struct TraverseRefVisitor<F1> {
pub visit_subexpr: F1,
- pub visit_embed: F3,
}
-impl<'a, SE, E, SE2, E2, Err, F1, F3> ExprFFallibleVisitor<'a, SE, SE2, E, E2>
- for TraverseRefVisitor<F1, F3>
+impl<'a, SE, E, SE2, Err, F1> ExprFFallibleVisitor<'a, SE, SE2, E, E>
+ for TraverseRefVisitor<F1>
where
SE: 'a,
- E: 'a,
+ E: 'a + Clone,
F1: FnMut(&'a SE) -> Result<SE2, Err>,
- F3: FnOnce(&'a E) -> Result<E2, Err>,
{
type Error = Err;
fn visit_subexpr(&mut self, subexpr: &'a SE) -> Result<SE2, Self::Error> {
(self.visit_subexpr)(subexpr)
}
- fn visit_embed(self, embed: &'a E) -> Result<E2, Self::Error> {
- (self.visit_embed)(embed)
- }
-}
-
-pub struct TraverseEmbedVisitor<F1>(pub F1);
-
-impl<'a, 'b, N, E, E2, Err, F1>
- ExprFFallibleVisitor<'a, SubExpr<N, E>, SubExpr<N, E2>, 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(subexpr.rewrap(subexpr.as_ref().visit(&mut **self)?))
- }
- fn visit_embed(self, embed: &'a E) -> Result<E2, Self::Error> {
- (self.0)(embed)
+ fn visit_embed(self, embed: &'a E) -> Result<E, Self::Error> {
+ Ok(embed.clone())
}
}
pub struct ResolveVisitor<F1>(pub F1);
-impl<'a, 'b, N, E, E2, Err, F1>
- ExprFFallibleVisitor<'a, SubExpr<N, E>, SubExpr<N, E2>, E, E2>
+impl<'a, 'b, E, E2, Err, F1> ExprFFallibleVisitor<'a, Expr<E>, Expr<E2>, E, E2>
for &'b mut ResolveVisitor<F1>
where
- N: Clone + 'a,
- F1: FnMut(&E) -> Result<E2, Err>,
+ F1: FnMut(&Import<Expr<E2>>) -> Result<E2, Err>,
{
type Error = Err;
fn visit_subexpr(
&mut self,
- subexpr: &'a SubExpr<N, E>,
- ) -> Result<SubExpr<N, E2>, Self::Error> {
+ subexpr: &'a Expr<E>,
+ ) -> Result<Expr<E2>, Self::Error> {
Ok(subexpr.rewrap(
subexpr
.as_ref()
.traverse_resolve_with_visitor(&mut **self)?,
))
}
- fn visit_embed(self, embed: &'a E) -> Result<E2, Self::Error> {
- (self.0)(embed)
- }
-}
-pub struct NoteAbsurdVisitor;
-
-impl<'a, 'b, N, E>
- ExprFInFallibleVisitor<'a, SubExpr<X, E>, SubExpr<N, E>, E, E>
- for &'b mut NoteAbsurdVisitor
-where
- E: Clone + 'a,
-{
- fn visit_subexpr(&mut self, subexpr: &'a SubExpr<X, E>) -> SubExpr<N, E> {
- SubExpr::from_expr_no_note(subexpr.as_ref().visit(&mut **self))
- }
- fn visit_embed(self, embed: &'a E) -> E {
- E::clone(embed)
- }
-}
-
-pub struct AbsurdVisitor;
-
-impl<'a, 'b, N, E>
- ExprFInFallibleVisitor<'a, SubExpr<X, X>, SubExpr<N, E>, X, E>
- for &'b mut AbsurdVisitor
-{
- fn visit_subexpr(&mut self, subexpr: &'a SubExpr<X, X>) -> SubExpr<N, E> {
- SubExpr::from_expr_no_note(subexpr.as_ref().visit(&mut **self))
- }
- fn visit_embed(self, embed: &'a X) -> E {
- match *embed {}
+ fn visit_embed(self, _embed: &'a E) -> Result<E2, Self::Error> {
+ unimplemented!()
}
}