diff options
-rw-r--r-- | dhall/src/error.rs | 38 | ||||
-rw-r--r-- | dhall/src/expr.rs | 228 | ||||
-rw-r--r-- | dhall/src/lib.rs | 11 | ||||
-rw-r--r-- | dhall/src/parser.rs | 6 | ||||
-rw-r--r-- | dhall/src/phase/binary.rs (renamed from dhall/src/binary.rs) | 0 | ||||
-rw-r--r-- | dhall/src/phase/mod.rs | 389 | ||||
-rw-r--r-- | dhall/src/phase/normalize.rs (renamed from dhall/src/normalize.rs) | 37 | ||||
-rw-r--r-- | dhall/src/phase/parse.rs | 38 | ||||
-rw-r--r-- | dhall/src/phase/resolve.rs (renamed from dhall/src/imports.rs) | 53 | ||||
-rw-r--r-- | dhall/src/phase/typecheck.rs (renamed from dhall/src/typecheck.rs) | 157 | ||||
-rw-r--r-- | dhall/src/serde.rs | 2 | ||||
-rw-r--r-- | dhall/src/tests.rs | 2 | ||||
-rw-r--r-- | dhall/src/traits/deserialize.rs | 2 | ||||
-rw-r--r-- | dhall/src/traits/dynamic_type.rs | 7 | ||||
-rw-r--r-- | dhall/src/traits/static_type.rs | 6 |
15 files changed, 499 insertions, 477 deletions
diff --git a/dhall/src/error.rs b/dhall/src/error.rs index 6ed0bfb..8497a91 100644 --- a/dhall/src/error.rs +++ b/dhall/src/error.rs @@ -1,13 +1,21 @@ +use std::io::Error as IOError; + +use dhall_syntax::ParseError; + +use crate::phase::binary::DecodeError; +use crate::phase::resolve::ImportError; +use crate::phase::typecheck::TypeError; + pub type Result<T> = std::result::Result<T, Error>; #[derive(Debug)] #[non_exhaustive] pub enum Error { - IO(std::io::Error), - Parse(dhall_syntax::ParseError), - Decode(crate::binary::DecodeError), - Resolve(crate::imports::ImportError), - Typecheck(crate::typecheck::TypeError), + IO(IOError), + Parse(ParseError), + Decode(DecodeError), + Resolve(ImportError), + Typecheck(TypeError), Deserialize(String), } @@ -25,28 +33,28 @@ impl std::fmt::Display for Error { } impl std::error::Error for Error {} -impl From<std::io::Error> for Error { - fn from(err: std::io::Error) -> Error { +impl From<IOError> for Error { + fn from(err: IOError) -> Error { Error::IO(err) } } -impl From<dhall_syntax::ParseError> for Error { - fn from(err: dhall_syntax::ParseError) -> Error { +impl From<ParseError> for Error { + fn from(err: ParseError) -> Error { Error::Parse(err) } } -impl From<crate::binary::DecodeError> for Error { - fn from(err: crate::binary::DecodeError) -> Error { +impl From<DecodeError> for Error { + fn from(err: DecodeError) -> Error { Error::Decode(err) } } -impl From<crate::imports::ImportError> for Error { - fn from(err: crate::imports::ImportError) -> Error { +impl From<ImportError> for Error { + fn from(err: ImportError) -> Error { Error::Resolve(err) } } -impl From<crate::typecheck::TypeError> for Error { - fn from(err: crate::typecheck::TypeError) -> Error { +impl From<TypeError> for Error { + fn from(err: TypeError) -> Error { Error::Typecheck(err) } } diff --git a/dhall/src/expr.rs b/dhall/src/expr.rs deleted file mode 100644 index 896753c..0000000 --- a/dhall/src/expr.rs +++ /dev/null @@ -1,228 +0,0 @@ -use crate::imports::ImportRoot; -use crate::normalize::{Thunk, Value}; -use dhall_syntax::*; - -macro_rules! derive_other_traits { - ($ty:ident) => { - impl std::cmp::PartialEq for $ty { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } - } - - impl std::cmp::Eq for $ty {} - - impl std::fmt::Display for $ty { - fn fmt( - &self, - f: &mut std::fmt::Formatter, - ) -> Result<(), std::fmt::Error> { - self.0.fmt(f) - } - } - }; -} - -#[derive(Debug, Clone)] -pub(crate) struct Parsed( - pub(crate) SubExpr<Span, Import>, - pub(crate) ImportRoot, -); -derive_other_traits!(Parsed); - -#[derive(Debug, Clone)] -pub(crate) struct Resolved(pub(crate) SubExpr<Span, Normalized>); -derive_other_traits!(Resolved); - -pub(crate) use self::typed::Typed; - -#[derive(Debug, Clone)] -pub(crate) struct Normalized(pub(crate) Typed); - -impl std::cmp::PartialEq for Normalized { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl std::cmp::Eq for Normalized {} - -impl std::fmt::Display for Normalized { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - self.to_expr().fmt(f) - } -} - -mod typed { - use super::Type; - use crate::normalize::{AlphaVar, Thunk, Value}; - use crate::typecheck::{ - TypeError, TypeInternal, TypeMessage, TypecheckContext, - }; - use dhall_syntax::{Const, SubExpr, X}; - use std::borrow::Cow; - - #[derive(Debug, Clone)] - pub(crate) enum Typed { - // The `Sort` higher-kinded type doesn't have a type - Sort, - // Any other value, along with its type - Value(Thunk, Option<Type>), - } - - impl Typed { - pub(crate) fn from_thunk_and_type(th: Thunk, t: Type) -> Self { - Typed::Value(th, Some(t)) - } - - pub(crate) fn from_thunk_untyped(th: Thunk) -> Self { - Typed::Value(th, None) - } - - // TODO: Avoid cloning if possible - pub(crate) fn to_value(&self) -> Value { - match self { - Typed::Value(th, _) => th.to_value(), - Typed::Sort => Value::Const(Const::Sort), - } - } - - pub(crate) fn to_expr(&self) -> SubExpr<X, X> { - self.to_value().normalize_to_expr() - } - - pub(crate) fn to_expr_alpha(&self) -> SubExpr<X, X> { - self.to_value().normalize_to_expr_maybe_alpha(true) - } - - pub(crate) fn to_thunk(&self) -> Thunk { - match self { - Typed::Value(th, _) => th.clone(), - Typed::Sort => Thunk::from_value(Value::Const(Const::Sort)), - } - } - - pub(crate) fn to_type(&self) -> Type { - match self { - Typed::Sort => Type(TypeInternal::Const(Const::Sort)), - Typed::Value(th, _) => match &*th.as_value() { - Value::Const(c) => Type(TypeInternal::Const(*c)), - _ => Type(TypeInternal::Typed(Box::new(self.clone()))), - }, - } - } - - pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> { - match self { - Typed::Value(_, Some(t)) => Ok(Cow::Borrowed(t)), - Typed::Value(_, None) => Err(TypeError::new( - &TypecheckContext::new(), - TypeMessage::Untyped, - )), - Typed::Sort => Err(TypeError::new( - &TypecheckContext::new(), - TypeMessage::Sort, - )), - } - } - - pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self { - match self { - Typed::Value(th, t) => Typed::Value( - th.shift(delta, var), - t.as_ref().map(|x| x.shift(delta, var)), - ), - Typed::Sort => Typed::Sort, - } - } - - pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { - match self { - Typed::Value(th, t) => Typed::Value( - th.subst_shift(var, val), - t.as_ref().map(|x| x.subst_shift(var, val)), - ), - Typed::Sort => Typed::Sort, - } - } - pub(crate) fn const_sort() -> Self { - Typed::Sort - } - } - - impl std::cmp::PartialEq for Typed { - fn eq(&self, other: &Self) -> bool { - self.to_value() == other.to_value() - } - } - - impl std::cmp::Eq for Typed {} -} - -/// A Dhall expression representing a simple type. -/// -/// This captures what is usually simply called a "type", like -/// `Bool`, `{ x: Integer }` or `Natural -> Text`. -/// -/// For a more general notion of "type", see [Type]. -#[derive(Debug, Clone)] -pub struct SimpleType(pub(crate) SubExpr<X, X>); -derive_other_traits!(SimpleType); - -pub(crate) use crate::typecheck::TypeInternal; - -/// A Dhall expression representing a (possibly higher-kinded) type. -/// -/// This includes [SimpleType]s but also higher-kinded expressions like -/// `Type`, `Kind` and `{ x: Type }`. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Type(pub(crate) TypeInternal); - -impl std::fmt::Display for Type { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - self.to_normalized().fmt(f) - } -} - -// Exposed for the macros -#[doc(hidden)] -impl From<SimpleType> for SubExpr<X, X> { - fn from(x: SimpleType) -> SubExpr<X, X> { - x.0 - } -} - -// Exposed for the macros -#[doc(hidden)] -impl From<SubExpr<X, X>> for SimpleType { - fn from(x: SubExpr<X, X>) -> SimpleType { - SimpleType(x) - } -} - -// Exposed for the macros -#[doc(hidden)] -impl From<Normalized> for Typed { - fn from(x: Normalized) -> Typed { - x.0 - } -} - -impl Normalized { - pub(crate) fn from_thunk_and_type(th: Thunk, t: Type) -> Self { - Normalized(Typed::from_thunk_and_type(th, t)) - } - pub(crate) fn to_expr(&self) -> SubExpr<X, X> { - self.0.to_expr() - } - #[allow(dead_code)] - pub(crate) fn to_expr_alpha(&self) -> SubExpr<X, X> { - self.0.to_expr_alpha() - } - pub(crate) fn to_value(&self) -> Value { - self.0.to_value() - } - pub(crate) fn to_thunk(&self) -> Thunk { - self.0.to_thunk() - } -} diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index a531f64..ec110f7 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -123,18 +123,11 @@ #[macro_use] mod tests; -#[cfg(test)] -mod parser; - -mod binary; /// When manipulating Dhall expressions goes wrong. pub mod error; -pub mod expr; -mod imports; -mod normalize; +mod phase; mod serde; mod traits; -mod typecheck; /// Deserialization of Dhall expressions into Rust pub mod de { @@ -153,7 +146,7 @@ pub mod de { /// expression has that type. pub fn from_str<'a, T: Deserialize<'a>>( s: &'a str, - ty: Option<&crate::expr::Type>, + ty: Option<&crate::phase::Type>, ) -> crate::error::Result<T> { T::from_str(s, ty) } diff --git a/dhall/src/parser.rs b/dhall/src/parser.rs deleted file mode 100644 index 3648c4f..0000000 --- a/dhall/src/parser.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[cfg(test)] -mod spec_tests { - #![rustfmt::skip] - // See ../build.rs - include!(concat!(env!("OUT_DIR"), "/parser_tests.rs")); -} diff --git a/dhall/src/binary.rs b/dhall/src/phase/binary.rs index 9c31d4c..9c31d4c 100644 --- a/dhall/src/binary.rs +++ b/dhall/src/phase/binary.rs diff --git a/dhall/src/phase/mod.rs b/dhall/src/phase/mod.rs new file mode 100644 index 0000000..0b6eca6 --- /dev/null +++ b/dhall/src/phase/mod.rs @@ -0,0 +1,389 @@ +use std::borrow::Cow; +use std::fmt::Display; +use std::path::Path; + +use dhall_syntax::{Const, Import, Span, SubExpr, X}; + +use crate::error::Error; + +use normalize::{AlphaVar, Thunk, Value}; +use resolve::{ImportError, ImportRoot}; +use typecheck::{ + const_to_typed, type_of_const, TypeError, TypeMessage, TypecheckContext, +}; + +pub(crate) mod binary; +pub(crate) mod normalize; +pub(crate) mod parse; +pub(crate) mod resolve; +pub(crate) mod typecheck; + +pub(crate) type ParsedSubExpr = SubExpr<Span, Import>; +pub(crate) type ResolvedSubExpr = SubExpr<Span, Normalized>; +pub(crate) type NormalizedSubExpr = SubExpr<X, X>; + +#[derive(Debug, Clone)] +pub(crate) struct Parsed(pub(crate) ParsedSubExpr, pub(crate) ImportRoot); + +/// An expression where all imports have been resolved +#[derive(Debug, Clone)] +pub(crate) struct Resolved(pub(crate) ResolvedSubExpr); + +/// A typed expression +#[derive(Debug, Clone)] +pub(crate) enum Typed { + // The `Sort` higher-kinded type; it doesn't have a type + Sort, + // Any other value, along with (optionally) its type + Value(Thunk, Option<Type>), +} + +/// A normalized expression. +/// +/// Invariant: the contained Typed expression must be in normal form, +#[derive(Debug, Clone)] +pub(crate) struct Normalized(pub(crate) Typed); + +/// A Dhall expression representing a simple type. +/// +/// This captures what is usually simply called a "type", like +/// `Bool`, `{ x: Integer }` or `Natural -> Text`. +/// +/// For a more general notion of "type", see [Type]. +#[derive(Debug, Clone)] +pub struct SimpleType(pub(crate) NormalizedSubExpr); + +/// A Dhall expression representing a (possibly higher-kinded) type. +/// +/// This includes [SimpleType]s but also higher-kinded expressions like +/// `Type`, `Kind` and `{ x: Type }`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Type(pub(crate) TypeInternal); + +#[derive(Debug, Clone)] +pub(crate) enum TypeInternal { + Const(Const), + /// This must not contain a Const value. + Typed(Box<Typed>), +} + +impl Parsed { + pub fn parse_file(f: &Path) -> Result<Parsed, Error> { + parse::parse_file(f) + } + + pub fn parse_str(s: &str) -> Result<Parsed, Error> { + parse::parse_str(s) + } + + #[allow(dead_code)] + pub fn parse_binary_file(f: &Path) -> Result<Parsed, Error> { + parse::parse_binary_file(f) + } + + pub fn resolve(self) -> Result<Resolved, ImportError> { + resolve::resolve(self) + } + + #[allow(dead_code)] + pub fn skip_resolve(self) -> Result<Resolved, ImportError> { + resolve::skip_resolve_expr(self) + } +} + +impl Resolved { + pub fn typecheck(self) -> Result<Typed, TypeError> { + typecheck::typecheck(self) + } + pub fn typecheck_with(self, ty: &Type) -> Result<Typed, TypeError> { + typecheck::typecheck_with(self, ty) + } + /// Pretends this expression has been typechecked. Use with care. + #[allow(dead_code)] + pub fn skip_typecheck(self) -> Typed { + typecheck::skip_typecheck(self) + } +} + +impl Typed { + /// Reduce an expression to its normal form, performing beta reduction + /// + /// `normalize` does not type-check the expression. You may want to type-check + /// expressions before normalizing them since normalization can convert an + /// ill-typed expression into a well-typed expression. + /// + /// However, `normalize` will not fail if the expression is ill-typed and will + /// leave ill-typed sub-expressions unevaluated. + pub fn normalize(self) -> Normalized { + match &self { + Typed::Sort => {} + Typed::Value(thunk, _) => { + thunk.normalize_nf(); + } + } + Normalized(self) + } + + pub(crate) fn from_thunk_and_type(th: Thunk, t: Type) -> Self { + Typed::Value(th, Some(t)) + } + pub(crate) fn from_thunk_untyped(th: Thunk) -> Self { + Typed::Value(th, None) + } + + // TODO: Avoid cloning if possible + pub(crate) fn to_value(&self) -> Value { + match self { + Typed::Value(th, _) => th.to_value(), + Typed::Sort => Value::Const(Const::Sort), + } + } + pub(crate) fn to_expr(&self) -> NormalizedSubExpr { + self.to_value().normalize_to_expr() + } + pub(crate) fn to_expr_alpha(&self) -> NormalizedSubExpr { + self.to_value().normalize_to_expr_maybe_alpha(true) + } + pub(crate) fn to_thunk(&self) -> Thunk { + match self { + Typed::Value(th, _) => th.clone(), + Typed::Sort => Thunk::from_value(Value::Const(Const::Sort)), + } + } + pub(crate) fn to_type(&self) -> Type { + match self { + Typed::Sort => Type(TypeInternal::Const(Const::Sort)), + Typed::Value(th, _) => match &*th.as_value() { + Value::Const(c) => Type(TypeInternal::Const(*c)), + _ => Type(TypeInternal::Typed(Box::new(self.clone()))), + }, + } + } + + pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> { + match self { + Typed::Value(_, Some(t)) => Ok(Cow::Borrowed(t)), + Typed::Value(_, None) => Err(TypeError::new( + &TypecheckContext::new(), + TypeMessage::Untyped, + )), + Typed::Sort => { + Err(TypeError::new(&TypecheckContext::new(), TypeMessage::Sort)) + } + } + } + + pub(crate) fn const_sort() -> Self { + Typed::Sort + } + + pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + match self { + Typed::Value(th, t) => Typed::Value( + th.shift(delta, var), + t.as_ref().map(|x| x.shift(delta, var)), + ), + Typed::Sort => Typed::Sort, + } + } + + pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { + match self { + Typed::Value(th, t) => Typed::Value( + th.subst_shift(var, val), + t.as_ref().map(|x| x.subst_shift(var, val)), + ), + Typed::Sort => Typed::Sort, + } + } +} + +impl Type { + pub(crate) fn to_normalized(&self) -> Normalized { + self.0.to_normalized() + } + pub(crate) fn to_expr(&self) -> NormalizedSubExpr { + self.0.to_expr() + } + pub(crate) fn to_value(&self) -> Value { + self.0.to_value() + } + pub(crate) fn as_const(&self) -> Option<Const> { + self.0.as_const() + } + pub(crate) fn internal_whnf(&self) -> Option<Value> { + self.0.whnf() + } + pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> { + self.0.get_type() + } + + pub(crate) fn const_sort() -> Self { + Type(TypeInternal::Const(Const::Sort)) + } + pub(crate) fn const_kind() -> Self { + Type(TypeInternal::Const(Const::Kind)) + } + pub(crate) fn const_type() -> Self { + Type(TypeInternal::Const(Const::Type)) + } + + pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + Type(self.0.shift(delta, var)) + } + pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { + Type(self.0.subst_shift(var, val)) + } +} + +impl TypeInternal { + pub(crate) fn to_typed(&self) -> Typed { + match self { + TypeInternal::Typed(e) => e.as_ref().clone(), + TypeInternal::Const(c) => const_to_typed(*c), + } + } + pub(crate) fn to_normalized(&self) -> Normalized { + self.to_typed().normalize() + } + pub(crate) fn to_expr(&self) -> NormalizedSubExpr { + self.to_normalized().to_expr() + } + pub(crate) fn to_value(&self) -> Value { + self.to_typed().to_value() + } + pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> { + Ok(match self { + TypeInternal::Typed(e) => e.get_type()?, + TypeInternal::Const(c) => Cow::Owned(type_of_const(*c)?), + }) + } + pub(crate) fn as_const(&self) -> Option<Const> { + match self { + TypeInternal::Const(c) => Some(*c), + _ => None, + } + } + pub(crate) fn whnf(&self) -> Option<Value> { + match self { + TypeInternal::Typed(e) => Some(e.to_value()), + _ => None, + } + } + pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + use TypeInternal::*; + match self { + Typed(e) => Typed(Box::new(e.shift(delta, var))), + Const(c) => Const(*c), + } + } + pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { + use TypeInternal::*; + match self { + Typed(e) => Typed(Box::new(e.subst_shift(var, val))), + Const(c) => Const(*c), + } + } +} + +impl Normalized { + pub(crate) fn from_thunk_and_type(th: Thunk, t: Type) -> Self { + Normalized(Typed::from_thunk_and_type(th, t)) + } + + pub(crate) fn to_expr(&self) -> NormalizedSubExpr { + self.0.to_expr() + } + #[allow(dead_code)] + pub(crate) fn to_expr_alpha(&self) -> NormalizedSubExpr { + self.0.to_expr_alpha() + } + pub(crate) fn to_value(&self) -> Value { + self.0.to_value() + } + pub(crate) fn to_thunk(&self) -> Thunk { + self.0.to_thunk() + } + pub(crate) fn to_type(self) -> Type { + self.0.to_type() + } + pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + Normalized(self.0.shift(delta, var)) + } +} + +macro_rules! derive_traits_for_wrapper_struct { + ($ty:ident) => { + impl std::cmp::PartialEq for $ty { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + + impl std::cmp::Eq for $ty {} + + impl std::fmt::Display for $ty { + fn fmt( + &self, + f: &mut std::fmt::Formatter, + ) -> Result<(), std::fmt::Error> { + self.0.fmt(f) + } + } + }; +} + +derive_traits_for_wrapper_struct!(Parsed); +derive_traits_for_wrapper_struct!(Resolved); +derive_traits_for_wrapper_struct!(Normalized); +derive_traits_for_wrapper_struct!(SimpleType); + +impl Eq for Typed {} +impl PartialEq for Typed { + fn eq(&self, other: &Self) -> bool { + self.to_value() == other.to_value() + } +} + +impl Eq for TypeInternal {} +impl PartialEq for TypeInternal { + fn eq(&self, other: &Self) -> bool { + self.to_normalized() == other.to_normalized() + } +} + +impl Display for Typed { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + self.to_expr().fmt(f) + } +} + +impl Display for Type { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + self.to_normalized().fmt(f) + } +} + +// Exposed for the macros +#[doc(hidden)] +impl From<SimpleType> for NormalizedSubExpr { + fn from(x: SimpleType) -> NormalizedSubExpr { + x.0 + } +} + +// Exposed for the macros +#[doc(hidden)] +impl From<NormalizedSubExpr> for SimpleType { + fn from(x: NormalizedSubExpr) -> SimpleType { + SimpleType(x) + } +} + +// Exposed for the macros +#[doc(hidden)] +impl From<Normalized> for Typed { + fn from(x: Normalized) -> Typed { + x.0 + } +} diff --git a/dhall/src/normalize.rs b/dhall/src/phase/normalize.rs index 1d306bc..2340bbd 100644 --- a/dhall/src/normalize.rs +++ b/dhall/src/phase/normalize.rs @@ -6,34 +6,13 @@ use dhall_proc_macros as dhall; use dhall_syntax::context::Context; use dhall_syntax::{ rc, BinOp, Builtin, Const, ExprF, Integer, InterpolatedTextContents, Label, - Natural, Span, SubExpr, V, X, + Natural, V, X, }; -use crate::expr::{Normalized, Type, Typed}; - -type InputSubExpr = SubExpr<Span, Normalized>; -type OutputSubExpr = SubExpr<X, X>; - -impl Typed { - /// Reduce an expression to its normal form, performing beta reduction - /// - /// `normalize` does not type-check the expression. You may want to type-check - /// expressions before normalizing them since normalization can convert an - /// ill-typed expression into a well-typed expression. - /// - /// However, `normalize` will not fail if the expression is ill-typed and will - /// leave ill-typed sub-expressions unevaluated. - /// - pub fn normalize(self) -> Normalized { - match &self { - Typed::Sort => {} - Typed::Value(thunk, _) => { - thunk.normalize_nf(); - } - } - Normalized(self) - } -} +use crate::phase::{NormalizedSubExpr, ResolvedSubExpr, Type, Typed}; + +type InputSubExpr = ResolvedSubExpr; +type OutputSubExpr = NormalizedSubExpr; /// Stores a pair of variables: a normal one and if relevant one /// that corresponds to the alpha-normalized version of the first one. @@ -186,9 +165,9 @@ impl NormalizationContext { } } pub(crate) fn from_typecheck_ctx( - tc_ctx: &crate::typecheck::TypecheckContext, + tc_ctx: &crate::phase::typecheck::TypecheckContext, ) -> Self { - use crate::typecheck::EnvItem::*; + use crate::phase::typecheck::EnvItem::*; let mut ctx = Context::new(); for (k, vs) in tc_ctx.0.iter_keys() { for v in vs.iter() { @@ -718,7 +697,7 @@ mod thunk { apply_any, normalize_whnf, AlphaVar, InputSubExpr, NormalizationContext, OutputSubExpr, Value, }; - use crate::expr::Typed; + use crate::phase::Typed; use std::cell::{Ref, RefCell}; use std::rc::Rc; diff --git a/dhall/src/phase/parse.rs b/dhall/src/phase/parse.rs new file mode 100644 index 0000000..809faf4 --- /dev/null +++ b/dhall/src/phase/parse.rs @@ -0,0 +1,38 @@ +use std::fs::File; +use std::io::Read; +use std::path::Path; + +use dhall_syntax::parse_expr; + +use crate::error::Error; +use crate::phase::resolve::ImportRoot; +use crate::phase::Parsed; + +pub(crate) fn parse_file(f: &Path) -> Result<Parsed, Error> { + let mut buffer = String::new(); + File::open(f)?.read_to_string(&mut buffer)?; + let expr = parse_expr(&*buffer)?; + let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned()); + Ok(Parsed(expr.unnote().note_absurd(), root)) +} + +pub(crate) fn parse_str(s: &str) -> Result<Parsed, Error> { + let expr = parse_expr(s)?; + let root = ImportRoot::LocalDir(std::env::current_dir()?); + Ok(Parsed(expr, root)) +} + +pub(crate) fn parse_binary_file(f: &Path) -> Result<Parsed, Error> { + let mut buffer = Vec::new(); + File::open(f)?.read_to_end(&mut buffer)?; + let expr = crate::phase::binary::decode(&buffer)?; + let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned()); + Ok(Parsed(expr.note_absurd(), root)) +} + +#[cfg(test)] +mod spec_tests { + #![rustfmt::skip] + // See build.rs + include!(concat!(env!("OUT_DIR"), "/parser_tests.rs")); +} diff --git a/dhall/src/imports.rs b/dhall/src/phase/resolve.rs index 87642a2..afb49cb 100644 --- a/dhall/src/imports.rs +++ b/dhall/src/phase/resolve.rs @@ -1,11 +1,10 @@ -use crate::error::Error; -use crate::expr::*; -use dhall_syntax::*; use std::collections::HashMap; -use std::fs::File; -use std::io::Read; -use std::path::Path; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; + +use dhall_syntax::Import; + +use crate::error::Error; +use crate::phase::{Normalized, Parsed, Resolved}; #[derive(Debug)] pub enum ImportError { @@ -97,7 +96,11 @@ fn do_resolve_expr( Ok(Resolved(expr)) } -fn skip_resolve_expr( +pub(crate) fn resolve(e: Parsed) -> Result<Resolved, ImportError> { + do_resolve_expr(e, &mut HashMap::new(), &Vec::new()) +} + +pub(crate) fn skip_resolve_expr( Parsed(expr, _root): Parsed, ) -> Result<Resolved, ImportError> { let resolve = |import: &Import| -> Result<Normalized, ImportError> { @@ -107,40 +110,6 @@ fn skip_resolve_expr( Ok(Resolved(expr)) } -impl Parsed { - pub fn parse_file(f: &Path) -> Result<Parsed, Error> { - let mut buffer = String::new(); - File::open(f)?.read_to_string(&mut buffer)?; - let expr = parse_expr(&*buffer)?; - let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned()); - Ok(Parsed(expr.unnote().note_absurd(), root)) - } - - pub fn parse_str(s: &str) -> Result<Parsed, Error> { - let expr = parse_expr(s)?; - let root = ImportRoot::LocalDir(std::env::current_dir()?); - Ok(Parsed(expr, root)) - } - - #[allow(dead_code)] - pub fn parse_binary_file(f: &Path) -> Result<Parsed, Error> { - let mut buffer = Vec::new(); - File::open(f)?.read_to_end(&mut buffer)?; - let expr = crate::binary::decode(&buffer)?; - let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned()); - Ok(Parsed(expr.note_absurd(), root)) - } - - pub fn resolve(self) -> Result<Resolved, ImportError> { - crate::imports::do_resolve_expr(self, &mut HashMap::new(), &Vec::new()) - } - - #[allow(dead_code)] - pub fn skip_resolve(self) -> Result<Resolved, ImportError> { - crate::imports::skip_resolve_expr(self) - } -} - #[cfg(test)] mod spec_tests { #![rustfmt::skip] diff --git a/dhall/src/typecheck.rs b/dhall/src/phase/typecheck.rs index 4dde883..6b12077 100644 --- a/dhall/src/typecheck.rs +++ b/dhall/src/phase/typecheck.rs @@ -4,10 +4,10 @@ use std::borrow::Cow; use std::collections::BTreeMap; use std::fmt; -use crate::expr::*; -use crate::normalize::{ +use crate::phase::normalize::{ AlphaVar, NormalizationContext, Thunk, TypeThunk, Value, }; +use crate::phase::*; use crate::traits::DynamicType; use dhall_proc_macros as dhall; use dhall_syntax; @@ -16,71 +16,6 @@ use dhall_syntax::*; use self::TypeMessage::*; -impl Resolved { - pub fn typecheck(self) -> Result<Typed, TypeError> { - type_of(self.0) - } - pub fn typecheck_with(self, ty: &Type) -> Result<Typed, TypeError> { - let expr: SubExpr<_, _> = self.0; - let ty: SubExpr<_, _> = ty.to_expr().embed_absurd().note_absurd(); - type_of(expr.rewrap(ExprF::Annot(expr.clone(), ty))) - } - /// Pretends this expression has been typechecked. Use with care. - #[allow(dead_code)] - pub fn skip_typecheck(self) -> Typed { - Typed::from_thunk_untyped(Thunk::new( - NormalizationContext::new(), - self.0, - )) - } -} - -impl Normalized { - fn shift(&self, delta: isize, var: &AlphaVar) -> Self { - Normalized(self.0.shift(delta, var)) - } - pub(crate) fn to_type(self) -> Type { - self.0.to_type() - } -} - -impl Type { - pub(crate) fn to_normalized(&self) -> Normalized { - self.0.to_normalized() - } - pub(crate) fn to_expr(&self) -> SubExpr<X, X> { - self.0.to_expr() - } - pub(crate) fn to_value(&self) -> Value { - self.0.to_value() - } - pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> { - self.0.get_type() - } - fn as_const(&self) -> Option<Const> { - self.0.as_const() - } - fn internal_whnf(&self) -> Option<Value> { - self.0.whnf() - } - pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self { - Type(self.0.shift(delta, var)) - } - pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { - Type(self.0.subst_shift(var, val)) - } - - fn const_sort() -> Self { - Type(TypeInternal::Const(Const::Sort)) - } - fn const_kind() -> Self { - Type(TypeInternal::Const(Const::Kind)) - } - pub(crate) fn const_type() -> Self { - Type(TypeInternal::Const(Const::Type)) - } -} - impl TypeThunk { fn to_type(&self, ctx: &TypecheckContext) -> Result<Type, TypeError> { match self { @@ -93,74 +28,6 @@ impl TypeThunk { } } -/// A semantic type. This is partially redundant with `dhall_syntax::Expr`, on purpose. `TypeInternal` should -/// be limited to syntactic expressions: either written by the user or meant to be printed. -/// The rule is the following: we must _not_ construct values of type `Expr` while typechecking, -/// but only construct `TypeInternal`s. -#[derive(Debug, Clone)] -pub(crate) enum TypeInternal { - Const(Const), - /// This must not contain a Const value. - Typed(Box<Typed>), -} - -impl TypeInternal { - fn to_typed(&self) -> Typed { - match self { - TypeInternal::Typed(e) => e.as_ref().clone(), - TypeInternal::Const(c) => const_to_typed(*c), - } - } - fn to_normalized(&self) -> Normalized { - self.to_typed().normalize() - } - fn to_expr(&self) -> SubExpr<X, X> { - self.to_normalized().to_expr() - } - fn to_value(&self) -> Value { - self.to_typed().to_value() - } - pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> { - Ok(match self { - TypeInternal::Typed(e) => e.get_type()?, - TypeInternal::Const(c) => Cow::Owned(type_of_const(*c)?), - }) - } - fn as_const(&self) -> Option<Const> { - match self { - TypeInternal::Const(c) => Some(*c), - _ => None, - } - } - fn whnf(&self) -> Option<Value> { - match self { - TypeInternal::Typed(e) => Some(e.to_value()), - _ => None, - } - } - fn shift(&self, delta: isize, var: &AlphaVar) -> Self { - use TypeInternal::*; - match self { - Typed(e) => Typed(Box::new(e.shift(delta, var))), - Const(c) => Const(*c), - } - } - fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { - use TypeInternal::*; - match self { - Typed(e) => Typed(Box::new(e.subst_shift(var, val))), - Const(c) => Const(*c), - } - } -} - -impl Eq for TypeInternal {} -impl PartialEq for TypeInternal { - fn eq(&self, other: &Self) -> bool { - self.to_normalized() == other.to_normalized() - } -} - #[derive(Debug, Clone)] pub(crate) enum EnvItem { Type(AlphaVar, Type), @@ -237,7 +104,7 @@ where eL0.borrow().to_value() == eR0.borrow().to_value() } -fn const_to_typed(c: Const) -> Typed { +pub(crate) fn const_to_typed(c: Const) -> Typed { match c { Const::Sort => Typed::const_sort(), _ => Typed::from_thunk_and_type( @@ -251,7 +118,7 @@ fn const_to_type(c: Const) -> Type { Type(TypeInternal::Const(c)) } -fn type_of_const(c: Const) -> Result<Type, TypeError> { +pub(crate) fn type_of_const(c: Const) -> Result<Type, TypeError> { match c { Const::Type => Ok(Type::const_kind()), Const::Kind => Ok(Type::const_sort()), @@ -918,6 +785,22 @@ fn type_of(e: SubExpr<Span, Normalized>) -> Result<Typed, TypeError> { Ok(e) } +pub(crate) fn typecheck(e: Resolved) -> Result<Typed, TypeError> { + type_of(e.0) +} + +pub(crate) fn typecheck_with( + e: Resolved, + ty: &Type, +) -> Result<Typed, TypeError> { + let expr: SubExpr<_, _> = e.0; + let ty: SubExpr<_, _> = ty.to_expr().embed_absurd().note_absurd(); + type_of(expr.rewrap(ExprF::Annot(expr.clone(), ty))) +} +pub(crate) fn skip_typecheck(e: Resolved) -> Typed { + Typed::from_thunk_untyped(Thunk::new(NormalizationContext::new(), e.0)) +} + /// The specific type error #[derive(Debug)] pub(crate) enum TypeMessage { diff --git a/dhall/src/serde.rs b/dhall/src/serde.rs index 96bc765..93921ba 100644 --- a/dhall/src/serde.rs +++ b/dhall/src/serde.rs @@ -1,5 +1,5 @@ use crate::error::{Error, Result}; -use crate::expr::{Normalized, Type}; +use crate::phase::{Normalized, Type}; use crate::traits::Deserialize; use dhall_syntax::*; use std::borrow::Cow; diff --git a/dhall/src/tests.rs b/dhall/src/tests.rs index 6b2ab60..18f6917 100644 --- a/dhall/src/tests.rs +++ b/dhall/src/tests.rs @@ -34,7 +34,7 @@ macro_rules! make_spec_test { } use crate::error::{Error, Result}; -use crate::expr::Parsed; +use crate::phase::Parsed; use crate::traits::DynamicType; use std::path::PathBuf; diff --git a/dhall/src/traits/deserialize.rs b/dhall/src/traits/deserialize.rs index e3ff2d5..9673cf9 100644 --- a/dhall/src/traits/deserialize.rs +++ b/dhall/src/traits/deserialize.rs @@ -1,5 +1,5 @@ use crate::error::*; -use crate::expr::*; +use crate::phase::*; /// A data structure that can be deserialized from a Dhall expression /// diff --git a/dhall/src/traits/dynamic_type.rs b/dhall/src/traits/dynamic_type.rs index 4c8e1fd..3f8d117 100644 --- a/dhall/src/traits/dynamic_type.rs +++ b/dhall/src/traits/dynamic_type.rs @@ -1,9 +1,6 @@ -use crate::expr::*; +use crate::phase::typecheck::TypeError; +use crate::phase::*; use crate::traits::StaticType; -#[allow(unused_imports)] -use crate::typecheck::{TypeError, TypeMessage, TypecheckContext}; -#[allow(unused_imports)] -use dhall_syntax::{Const, ExprF}; use std::borrow::Cow; pub trait DynamicType { diff --git a/dhall/src/traits/static_type.rs b/dhall/src/traits/static_type.rs index f90b8df..8b141a0 100644 --- a/dhall/src/traits/static_type.rs +++ b/dhall/src/traits/static_type.rs @@ -1,4 +1,4 @@ -use crate::expr::*; +use crate::phase::*; use dhall_proc_macros as dhall; use dhall_syntax::*; @@ -38,8 +38,8 @@ fn mktype(x: SubExpr<X, X>) -> SimpleType { impl<T: SimpleStaticType> StaticType for T { fn get_static_type() -> Type { - crate::expr::Normalized::from_thunk_and_type( - crate::normalize::Thunk::from_normalized_expr( + crate::phase::Normalized::from_thunk_and_type( + crate::phase::normalize::Thunk::from_normalized_expr( T::get_simple_static_type().into(), ), Type::const_type(), |