diff options
author | FintanH | 2019-08-12 22:34:21 +0100 |
---|---|---|
committer | FintanH | 2019-08-12 22:34:21 +0100 |
commit | 4a86274878d5ab0ef4f9d8597606226adfd048de (patch) | |
tree | 68153eb0e4283740f49042e059402da6eaaf551e | |
parent | 405bc3d80c0e169ea74dd12422b9504b7383dab3 (diff) | |
parent | 7d17d39005531cb77d8eaf32ed7de8938c66f874 (diff) |
Merge remote-tracking branch 'origin/master' into fintan/canonicalize
Diffstat (limited to '')
-rw-r--r-- | codecov.yml | 5 | ||||
m--------- | dhall-lang | 0 | ||||
-rw-r--r-- | dhall/build.rs | 207 | ||||
-rw-r--r-- | dhall/src/api/mod.rs | 4 | ||||
-rw-r--r-- | dhall/src/core/thunk.rs | 8 | ||||
-rw-r--r-- | dhall/src/core/value.rs | 90 | ||||
-rw-r--r-- | dhall/src/error/mod.rs | 6 | ||||
-rw-r--r-- | dhall/src/phase/binary.rs | 178 | ||||
-rw-r--r-- | dhall/src/phase/mod.rs | 3 | ||||
-rw-r--r-- | dhall/src/phase/normalize.rs | 63 | ||||
-rw-r--r-- | dhall/src/phase/resolve.rs | 19 | ||||
-rw-r--r-- | dhall/src/phase/typecheck.rs | 824 | ||||
-rw-r--r-- | dhall/src/tests.rs | 48 | ||||
-rw-r--r-- | dhall/tests/traits.rs | 30 | ||||
-rw-r--r-- | dhall_generated_parser/build.rs | 15 | ||||
-rw-r--r-- | dhall_generated_parser/src/dhall.pest.visibility | 21 | ||||
-rw-r--r-- | dhall_proc_macros/src/lib.rs | 11 | ||||
-rw-r--r-- | dhall_proc_macros/src/quote.rs | 223 | ||||
-rw-r--r-- | dhall_syntax/src/core/expr.rs | 58 | ||||
-rw-r--r-- | dhall_syntax/src/core/import.rs | 2 | ||||
-rw-r--r-- | dhall_syntax/src/core/map.rs | 95 | ||||
-rw-r--r-- | dhall_syntax/src/core/visitor.rs | 33 | ||||
-rw-r--r-- | dhall_syntax/src/parser.rs | 146 | ||||
-rw-r--r-- | dhall_syntax/src/printer.rs | 27 | ||||
-rw-r--r-- | tests_buffer | 107 |
25 files changed, 1016 insertions, 1207 deletions
diff --git a/codecov.yml b/codecov.yml index db24720..35d97bf 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1 +1,6 @@ comment: off +coverage: + precision: 1 + status: + project: no + patch: no diff --git a/dhall-lang b/dhall-lang -Subproject ee528e5a89f78bce2c28167b5dfb7e7ea6b3d6c +Subproject 235d2c0b11a539003d2de6110f8666e93ae1ccd diff --git a/dhall/build.rs b/dhall/build.rs index 3f09d47..790ad8e 100644 --- a/dhall/build.rs +++ b/dhall/build.rs @@ -15,11 +15,15 @@ fn dhall_files_in_dir<'a>( .filter_map(move |path| { let path = path.path(); let path = path.strip_prefix(dir).unwrap(); - if path.extension() != Some(&OsString::from("dhall")) { + let ext = path.extension(); + if ext != Some(&OsString::from("dhall")) + && ext != Some(&OsString::from("dhallb")) + { return None; } + let ext = ext.unwrap(); let path = path.to_string_lossy(); - let path = &path[..path.len() - 6]; + let path = &path[..path.len() - 1 - ext.len()]; let path = if take_a_suffix { if &path[path.len() - 1..] != "A" { return None; @@ -37,29 +41,37 @@ fn dhall_files_in_dir<'a>( fn make_test_module( w: &mut impl Write, // Where to output the generated code mod_name: &str, // Name of the module, used in the output of `cargo test` - dir: &Path, // Directory containing the tests files + subdir: &str, // Directory containing the tests files feature: &str, // Relevant variant of `dhall::tests::Feature` mut exclude: impl FnMut(&str) -> bool, // Given a file name, whether to exclude it ) -> std::io::Result<()> { + let all_tests_dir = Path::new("../dhall-lang/tests/"); + let tests_dir = all_tests_dir.join(subdir); writeln!(w, "mod {} {{", mod_name)?; - for (name, path) in dhall_files_in_dir(&dir.join("success/"), true) { + for (name, path) in dhall_files_in_dir(&tests_dir.join("success/"), true) { if exclude(&("success/".to_owned() + &path)) { continue; } writeln!( w, - r#"make_spec_test!({}, Success, success_{}, "success/{}");"#, - feature, name, path + r#"make_spec_test!({}, Success, success_{}, "{}/success/{}");"#, + feature, + name, + tests_dir.to_string_lossy(), + path )?; } - for (name, path) in dhall_files_in_dir(&dir.join("failure/"), false) { + for (name, path) in dhall_files_in_dir(&tests_dir.join("failure/"), false) { if exclude(&("failure/".to_owned() + &path)) { continue; } writeln!( w, - r#"make_spec_test!({}, Failure, failure_{}, "failure/{}");"#, - feature, name, path + r#"make_spec_test!({}, Failure, failure_{}, "{}/failure/{}");"#, + feature, + name, + tests_dir.to_string_lossy(), + path )?; } writeln!(w, "}}")?; @@ -67,83 +79,198 @@ fn make_test_module( } fn main() -> std::io::Result<()> { - // Tries to detect when the submodule gets updated; doesn't really work. + // Tries to detect when the submodule gets updated. // To force regeneration of the test list, just `touch dhall-lang/.git` println!("cargo:rerun-if-changed=../dhall-lang/.git"); println!( "cargo:rerun-if-changed=../.git/modules/dhall-lang/refs/heads/master" ); let out_dir = env::var("OUT_DIR").unwrap(); - let tests_dir = Path::new("../dhall-lang/tests/"); let parser_tests_path = Path::new(&out_dir).join("spec_tests.rs"); let mut file = File::create(parser_tests_path)?; - make_test_module( - &mut file, - "parse", - &tests_dir.join("parser/"), - "Parser", - |path| { - // Too slow in debug mode - path == "success/largeExpression" - }, - )?; + make_test_module(&mut file, "parse", "parser/", "Parser", |path| { + // Too slow in debug mode + path == "success/largeExpression" + // TODO: Inline headers + || path == "success/unit/import/inlineUsing" + || path == "success/unit/import/Headers" + || path == "success/unit/import/HeadersDoubleHash" + || path == "success/unit/import/HeadersDoubleHashPrecedence" + || path == "success/unit/import/HeadersHashPrecedence" + || path == "success/unit/import/HeadersInteriorHash" + // TODO: projection by expression + || path == "success/recordProjectionByExpression" + || path == "success/RecordProjectionByType" + || path == "success/unit/RecordProjectionByType" + || path == "success/unit/RecordProjectionByTypeEmpty" + || path == "success/unit/RecordProjectFields" + // TODO: RFC3986 URLs + || path == "success/unit/import/urls/emptyPath0" + || path == "success/unit/import/urls/emptyPath1" + || path == "success/unit/import/urls/emptyPathSegment" + // TODO: toMap + || path == "success/toMap" + })?; - make_test_module( - &mut file, - "printer", - &tests_dir.join("parser/"), - "Printer", - |path| { - // Failure tests are only for the parser - path.starts_with("failure/") + make_test_module(&mut file, "printer", "parser/", "Printer", |path| { + // Failure tests are only for the parser + path.starts_with("failure/") // Too slow in debug mode || path == "success/largeExpression" - }, - )?; + // TODO: Inline headers + || path == "success/unit/import/inlineUsing" + || path == "success/unit/import/Headers" + // TODO: projection by expression + || path == "success/recordProjectionByExpression" + || path == "success/RecordProjectionByType" + || path == "success/unit/RecordProjectionByType" + || path == "success/unit/RecordProjectionByTypeEmpty" + // TODO: RFC3986 URLs + || path == "success/unit/import/urls/emptyPath0" + || path == "success/unit/import/urls/emptyPath1" + || path == "success/unit/import/urls/emptyPathSegment" + // TODO: toMap + || path == "success/toMap" + })?; make_test_module( &mut file, "binary_encoding", - &tests_dir.join("parser/"), + "parser/", "BinaryEncoding", |path| { // Failure tests are only for the parser path.starts_with("failure/") // Too slow in debug mode || path == "success/largeExpression" - // Too much of a pain to implement; shouldn't make a difference - // since lets disappear on normalization. - || path == "success/multilet" // See https://github.com/pyfisch/cbor/issues/109 || path == "success/double" + || path == "success/unit/DoubleLitExponentNoDot" + || path == "success/unit/DoubleLitSecretelyInt" + // TODO: Inline headers + || path == "success/unit/import/inlineUsing" + || path == "success/unit/import/Headers" + // TODO: projection by expression + || path == "success/recordProjectionByExpression" + || path == "success/RecordProjectionByType" + || path == "success/unit/RecordProjectionByType" + || path == "success/unit/RecordProjectionByTypeEmpty" + // TODO: RFC3986 URLs + || path == "success/unit/import/urls/emptyPath0" + || path == "success/unit/import/urls/emptyPath1" + || path == "success/unit/import/urls/emptyPathSegment" + // TODO: toMap + || path == "success/toMap" + }, + )?; + + make_test_module( + &mut file, + "binary_decoding", + "binary-decode/", + "BinaryDecoding", + |path| { + false + // TODO: projection by expression + || path == "success/unit/RecordProjectFields" + || path == "success/unit/recordProjectionByExpression" + // TODO: toMap + || path == "success/unit/ToMap" + || path == "success/unit/ToMapAnnotated" }, )?; make_test_module( &mut file, "beta_normalize", - &tests_dir.join("normalization/"), + "normalization/", "Normalization", |path| { // We don't support bignums path == "success/simple/integerToDouble" // Too slow || path == "success/remoteSystems" - // TODO: selection by expression - || path == "success/unit/RecordProjectionTypeEmpty" - || path == "success/unit/RecordProjectionTypeNonEmpty" + // TODO: projection by expression + || path == "success/unit/RecordProjectionByTypeEmpty" + || path == "success/unit/RecordProjectionByTypeNonEmpty" + || path == "success/unit/RecordProjectionByTypeNormalizeProjection" + // TODO: fix Double/show + || path == "success/prelude/JSON/number/1" + // TODO: toMap + || path == "success/unit/EmptyToMap" + || path == "success/unit/ToMap" + || path == "success/unit/ToMapWithType" + // TODO: Normalize field selection further by inspecting the argument + || path == "success/simplifications/rightBiasedMergeWithinRecordProjectionWithinFieldSelection0" + || path == "success/simplifications/rightBiasedMergeWithinRecordProjectionWithinFieldSelection1" + || path == "success/simplifications/rightBiasedMergeWithinRecursiveRecordMergeWithinFieldselection" + || path == "success/unit/RecordProjectionByTypeWithinFieldSelection" + || path == "success/unit/RecordProjectionWithinFieldSelection" + || path == "success/unit/RecursiveRecordMergeWithinFieldSelection0" + || path == "success/unit/RecursiveRecordMergeWithinFieldSelection1" + || path == "success/unit/RecursiveRecordMergeWithinFieldSelection2" + || path == "success/unit/RecursiveRecordMergeWithinFieldSelection3" + || path == "success/unit/RightBiasedMergeWithinFieldSelection0" + || path == "success/unit/RightBiasedMergeWithinFieldSelection1" + || path == "success/unit/RightBiasedMergeWithinFieldSelection2" + || path == "success/unit/RightBiasedMergeWithinFieldSelection3" + || path == "success/unit/RightBiasedMergeEquivalentArguments" }, )?; make_test_module( &mut file, "alpha_normalize", - &tests_dir.join("alpha-normalization/"), + "alpha-normalization/", "AlphaNormalization", |_| false, )?; + make_test_module( + &mut file, + "typecheck", + "typecheck/", + "Typecheck", + |path| { + false + // TODO: Enable imports in typecheck tests + || path == "failure/importBoundary" + // Too slow + || path == "success/prelude" + // TODO: Inline headers + || path == "failure/customHeadersUsingBoundVariable" + // TODO: projection by expression + || path == "failure/unit/RecordProjectionByTypeFieldTypeMismatch" + || path == "failure/unit/RecordProjectionByTypeNotPresent" + // TODO: toMap + || path == "failure/unit/EmptyToMap" + || path == "failure/unit/HeterogenousToMap" + || path == "failure/unit/MistypedToMap1" + || path == "failure/unit/MistypedToMap2" + || path == "failure/unit/MistypedToMap3" + || path == "failure/unit/MistypedToMap4" + || path == "failure/unit/NonRecordToMap" + }, + )?; + + make_test_module( + &mut file, + "type_inference", + "type-inference/", + "TypeInference", + |path| { + false + // TODO: projection by expression + || path == "success/unit/RecordProjectionByType" + || path == "success/unit/RecordProjectionByTypeEmpty" + || path == "success/unit/RecordProjectionByTypeJudgmentalEquality" + // TODO: toMap + || path == "success/unit/ToMap" + || path == "success/unit/ToMapAnnotated" + }, + )?; + Ok(()) } diff --git a/dhall/src/api/mod.rs b/dhall/src/api/mod.rs index 233d7cf..188b6c0 100644 --- a/dhall/src/api/mod.rs +++ b/dhall/src/api/mod.rs @@ -66,10 +66,6 @@ mod typ { )) } #[doc(hidden)] - pub fn from_normalized_expr(e: NormalizedSubExpr) -> Self { - Type(Typed::from_normalized_expr_untyped(e)) - } - #[doc(hidden)] pub fn make_record_type( kts: impl Iterator<Item = (String, Type)>, ) -> Self { diff --git a/dhall/src/core/thunk.rs b/dhall/src/core/thunk.rs index 5c569e1..f41579c 100644 --- a/dhall/src/core/thunk.rs +++ b/dhall/src/core/thunk.rs @@ -111,10 +111,6 @@ impl Thunk { ThunkInternal::Value(WHNF, v).into_thunk() } - pub fn from_normalized_expr(e: OutputSubExpr) -> Thunk { - Thunk::new(NormalizationContext::new(), e.absurd()) - } - pub fn from_partial_expr(e: ExprF<Thunk, X>) -> Thunk { ThunkInternal::PartialExpr(e).into_thunk() } @@ -221,6 +217,10 @@ impl TypeThunk { self.0.to_type() } + pub fn to_typed(&self) -> Typed { + self.0.clone() + } + pub fn normalize_to_expr_maybe_alpha(&self, alpha: bool) -> OutputSubExpr { self.normalize_nf().normalize_to_expr_maybe_alpha(alpha) } diff --git a/dhall/src/core/value.rs b/dhall/src/core/value.rs index bc8fa34..0b68bf6 100644 --- a/dhall/src/core/value.rs +++ b/dhall/src/core/value.rs @@ -1,6 +1,5 @@ use std::collections::HashMap; -use dhall_proc_macros as dhall; use dhall_syntax::{ rc, Builtin, Const, ExprF, Integer, InterpolatedTextContents, Label, NaiveDouble, Natural, X, @@ -47,6 +46,7 @@ pub enum Value { DoubleLit(NaiveDouble), EmptyOptionalLit(TypeThunk), NEOptionalLit(Thunk), + // EmptyListLit(t) means `[] : List t`, not `[] : t` EmptyListLit(TypeThunk), NEListLit(Vec<Thunk>), RecordLit(HashMap<Label, Thunk>), @@ -57,6 +57,7 @@ pub enum Value { // Invariant: this must not contain interpolations that are themselves TextLits, and // contiguous text values must be merged. TextLit(Vec<InterpolatedTextContents<Thunk>>), + Equivalence(TypeThunk, TypeThunk), // Invariant: this must not contain a value captured by one of the variants above. PartialExpr(ExprF<Thunk, X>), } @@ -73,6 +74,47 @@ impl Value { /// Convert the value to a fully normalized syntactic expression. Also alpha-normalize /// if alpha is `true` pub fn normalize_to_expr_maybe_alpha(&self, alpha: bool) -> OutputSubExpr { + // Ad-hoc macro to help construct the unapplied closures + macro_rules! make_expr { + (Natural) => { rc(ExprF::Builtin(Builtin::Natural)) }; + (var($var:ident)) => { + rc(ExprF::Var(dhall_syntax::V(stringify!($var).into(), 0))) + }; + ($var:ident) => { $var }; + (List $($rest:tt)*) => { + rc(ExprF::App( + rc(ExprF::Builtin(Builtin::List)), + make_expr!($($rest)*) + )) + }; + (Some $($rest:tt)*) => { + rc(ExprF::SomeLit( + make_expr!($($rest)*) + )) + }; + (1 + $($rest:tt)*) => { + rc(ExprF::BinOp( + dhall_syntax::BinOp::NaturalPlus, + rc(ExprF::NaturalLit(1)), + make_expr!($($rest)*) + )) + }; + ([ $($head:tt)* ] # $($tail:tt)*) => { + rc(ExprF::BinOp( + dhall_syntax::BinOp::ListAppend, + rc(ExprF::NEListLit(vec![make_expr!($($head)*)])), + make_expr!($($tail)*) + )) + }; + (λ($var:ident : $($ty:tt)*) -> $($rest:tt)*) => { + rc(ExprF::Pi( + stringify!($var).into(), + make_expr!($($ty)*), + make_expr!($($rest)*) + )) + }; + } + match self { Value::Lam(x, t, e) => rc(ExprF::Lam( x.to_label_maybe_alpha(alpha), @@ -91,24 +133,24 @@ impl Value { } Value::OptionalSomeClosure(n) => { let a = n.normalize_to_expr_maybe_alpha(alpha); - dhall::subexpr!(λ(x: a) -> Some x) + make_expr!(λ(x: a) -> Some var(x)) } Value::ListConsClosure(a, None) => { // Avoid accidental capture of the new `x` variable let a1 = a.under_binder(Label::from("x")); let a1 = a1.normalize_to_expr_maybe_alpha(alpha); let a = a.normalize_to_expr_maybe_alpha(alpha); - dhall::subexpr!(λ(x : a) -> λ(xs : List a1) -> [ x ] # xs) + make_expr!(λ(x : a) -> λ(xs : List a1) -> [ var(x) ] # var(xs)) } Value::ListConsClosure(n, Some(v)) => { // Avoid accidental capture of the new `xs` variable let v = v.under_binder(Label::from("xs")); let v = v.normalize_to_expr_maybe_alpha(alpha); let a = n.normalize_to_expr_maybe_alpha(alpha); - dhall::subexpr!(λ(xs : List a) -> [ v ] # xs) + make_expr!(λ(xs : List a) -> [ v ] # var(xs)) } Value::NaturalSuccClosure => { - dhall::subexpr!(λ(x : Natural) -> x + 1) + make_expr!(λ(x : Natural) -> 1 + var(x)) } Value::Pi(x, t, e) => rc(ExprF::Pi( x.to_label_maybe_alpha(alpha), @@ -128,9 +170,10 @@ impl Value { Value::NEOptionalLit(n) => { rc(ExprF::SomeLit(n.normalize_to_expr_maybe_alpha(alpha))) } - Value::EmptyListLit(n) => { - rc(ExprF::EmptyListLit(n.normalize_to_expr_maybe_alpha(alpha))) - } + Value::EmptyListLit(n) => rc(ExprF::EmptyListLit(rc(ExprF::App( + rc(ExprF::Builtin(Builtin::List)), + n.normalize_to_expr_maybe_alpha(alpha), + )))), Value::NEListLit(elts) => rc(ExprF::NEListLit( elts.iter() .map(|n| n.normalize_to_expr_maybe_alpha(alpha)) @@ -176,19 +219,10 @@ impl Value { .collect(); rc(ExprF::Field(rc(ExprF::UnionType(kts)), l.clone())) } - Value::UnionLit(l, v, kts) => rc(ExprF::UnionLit( - l.clone(), + Value::UnionLit(l, v, kts) => rc(ExprF::App( + Value::UnionConstructor(l.clone(), kts.clone()) + .normalize_to_expr_maybe_alpha(alpha), v.normalize_to_expr_maybe_alpha(alpha), - kts.iter() - .map(|(k, v)| { - ( - k.clone(), - v.as_ref().map(|v| { - v.normalize_to_expr_maybe_alpha(alpha) - }), - ) - }) - .collect(), )), Value::TextLit(elts) => { use InterpolatedTextContents::{Expr, Text}; @@ -203,6 +237,11 @@ impl Value { .collect(), )) } + Value::Equivalence(x, y) => rc(ExprF::BinOp( + dhall_syntax::BinOp::Equivalence, + x.normalize_to_expr_maybe_alpha(alpha), + y.normalize_to_expr_maybe_alpha(alpha), + )), Value::PartialExpr(e) => { rc(e.map_ref_simple(|v| v.normalize_to_expr_maybe_alpha(alpha))) } @@ -289,6 +328,10 @@ impl Value { } } } + Value::Equivalence(x, y) => { + x.normalize_mut(); + y.normalize_mut(); + } Value::PartialExpr(e) => { // TODO: need map_mut_simple e.map_ref_simple(|v| { @@ -425,6 +468,9 @@ impl Shift for Value { }) .collect::<Result<_, _>>()?, ), + Value::Equivalence(x, y) => { + Value::Equivalence(x.shift(delta, var)?, y.shift(delta, var)?) + } Value::PartialExpr(e) => Value::PartialExpr( e.traverse_ref_with_special_handling_of_binders( |v| Ok(v.shift(delta, var)?), @@ -540,6 +586,10 @@ impl Subst<Typed> for Value { }) .collect(), ), + Value::Equivalence(x, y) => Value::Equivalence( + x.subst_shift(var, val), + y.subst_shift(var, val), + ), } } } diff --git a/dhall/src/error/mod.rs b/dhall/src/error/mod.rs index aed6ccd..3c00017 100644 --- a/dhall/src/error/mod.rs +++ b/dhall/src/error/mod.rs @@ -68,7 +68,6 @@ pub(crate) enum TypeMessage { MissingRecordField(Label, Typed), MissingUnionField(Label, Normalized), BinOpTypeMismatch(BinOp, Typed), - NoDependentTypes(Normalized, Normalized), InvalidTextInterpolation(Typed), Merge1ArgMustBeRecord(Typed), Merge2ArgMustBeUnion(Typed), @@ -86,7 +85,10 @@ pub(crate) enum TypeMessage { RecordTypeMergeRequiresRecordType(Type), RecordTypeMismatch(Type, Type, Type, Type), UnionTypeDuplicateField, - Unimplemented, + EquivalenceArgumentMustBeTerm(bool, Typed), + EquivalenceTypeMismatch(Typed, Typed), + AssertMismatch(Typed, Typed), + AssertMustTakeEquivalence, } impl TypeError { diff --git a/dhall/src/phase/binary.rs b/dhall/src/phase/binary.rs index 3acd2d4..6bdc3f9 100644 --- a/dhall/src/phase/binary.rs +++ b/dhall/src/phase/binary.rs @@ -49,10 +49,21 @@ fn cbor_value_to_dhall( Bool(b) => BoolLit(*b), Array(vec) => match vec.as_slice() { [String(l), U64(n)] => { + if l.as_str() == "_" { + Err(DecodeError::WrongFormatError( + "`_` variable was encoded incorrectly".to_owned(), + ))? + } let l = Label::from(l.as_str()); Var(V(l, *n as usize)) } [U64(0), f, args..] => { + if args.is_empty() { + Err(DecodeError::WrongFormatError( + "Function application must have at least one argument" + .to_owned(), + ))? + } let mut f = cbor_value_to_dhall(&f)?; for a in args { let a = cbor_value_to_dhall(&a)?; @@ -66,6 +77,11 @@ fn cbor_value_to_dhall( Lam(Label::from("_"), x, y) } [U64(1), String(l), x, y] => { + if l.as_str() == "_" { + Err(DecodeError::WrongFormatError( + "`_` variable was encoded incorrectly".to_owned(), + ))? + } let x = cbor_value_to_dhall(&x)?; let y = cbor_value_to_dhall(&y)?; let l = Label::from(l.as_str()); @@ -77,6 +93,11 @@ fn cbor_value_to_dhall( Pi(Label::from("_"), x, y) } [U64(2), String(l), x, y] => { + if l.as_str() == "_" { + Err(DecodeError::WrongFormatError( + "`_` variable was encoded incorrectly".to_owned(), + ))? + } let x = cbor_value_to_dhall(&x)?; let y = cbor_value_to_dhall(&y)?; let l = Label::from(l.as_str()); @@ -99,6 +120,7 @@ fn cbor_value_to_dhall( 9 => RightBiasedRecordMerge, 10 => RecursiveRecordTypeMerge, 11 => ImportAlt, + 12 => Equivalence, _ => { Err(DecodeError::WrongFormatError("binop".to_owned()))? } @@ -107,7 +129,7 @@ fn cbor_value_to_dhall( } [U64(4), t] => { let t = cbor_value_to_dhall(&t)?; - EmptyListLit(t) + EmptyListLit(rc(App(rc(ExprF::Builtin(Builtin::List)), t))) } [U64(4), Null, rest..] => { let rest = rest @@ -116,18 +138,22 @@ fn cbor_value_to_dhall( .collect::<Result<Vec<_>, _>>()?; NEListLit(rest) } - [U64(5), t] => { - let t = cbor_value_to_dhall(&t)?; - OldOptionalLit(None, t) - } [U64(5), Null, x] => { let x = cbor_value_to_dhall(&x)?; SomeLit(x) } + // Old-style optional literals + [U64(5), t] => { + let t = cbor_value_to_dhall(&t)?; + App(rc(ExprF::Builtin(Builtin::OptionalNone)), t) + } [U64(5), t, x] => { let x = cbor_value_to_dhall(&x)?; let t = cbor_value_to_dhall(&t)?; - OldOptionalLit(Some(x), t) + Annot( + rc(SomeLit(x)), + rc(App(rc(ExprF::Builtin(Builtin::Optional)), t)), + ) } [U64(6), x, y] => { let x = cbor_value_to_dhall(&x)?; @@ -153,16 +179,26 @@ fn cbor_value_to_dhall( let l = Label::from(l.as_str()); Field(x, l) } + [U64(10), x, rest..] => { + let x = cbor_value_to_dhall(&x)?; + let labels = rest + .iter() + .map(|s| match s { + String(s) => Ok(Label::from(s.as_str())), + _ => Err(DecodeError::WrongFormatError( + "projection".to_owned(), + )), + }) + .collect::<Result<_, _>>()?; + Projection(x, labels) + } [U64(11), Object(map)] => { let map = cbor_map_to_dhall_opt_map(map)?; UnionType(map) } - [U64(12), String(l), x, Object(map)] => { - let map = cbor_map_to_dhall_opt_map(map)?; - let x = cbor_value_to_dhall(&x)?; - let l = Label::from(l.as_str()); - UnionLit(l, x, map) - } + [U64(12), ..] => Err(DecodeError::WrongFormatError( + "Union literals are not supported anymore".to_owned(), + ))?, [U64(14), x, y, z] => { let x = cbor_value_to_dhall(&x)?; let y = cbor_value_to_dhall(&y)?; @@ -190,10 +226,19 @@ fn cbor_value_to_dhall( .collect::<Result<_, _>>()?, ))) } + [U64(19), t] => { + let t = cbor_value_to_dhall(&t)?; + Assert(t) + } [U64(24), hash, U64(mode), U64(scheme), rest..] => { let mode = match mode { + 0 => ImportMode::Code, 1 => ImportMode::RawText, - _ => ImportMode::Code, + 2 => ImportMode::Location, + _ => Err(DecodeError::WrongFormatError(format!( + "import/mode/unknown_mode: {:?}", + mode + )))?, }; let hash = match hash { Null => None, @@ -217,18 +262,20 @@ fn cbor_value_to_dhall( }; let headers = match rest.next() { Some(Null) => None, - Some(x) => { - match cbor_value_to_dhall(&x)?.as_ref() { - Embed(import) => Some(Box::new( - import.location_hashed.clone(), - )), - _ => Err(DecodeError::WrongFormatError( - "import/remote/headers".to_owned(), - ))?, - } - } + // TODO + // Some(x) => { + // match cbor_value_to_dhall(&x)?.as_ref() { + // Embed(import) => Some(Box::new( + // import.location_hashed.clone(), + // )), + // _ => Err(DecodeError::WrongFormatError( + // "import/remote/headers".to_owned(), + // ))?, + // } + // } _ => Err(DecodeError::WrongFormatError( - "import/remote/headers".to_owned(), + "import/remote/headers is unimplemented" + .to_owned(), ))?, }; let authority = match rest.next() { @@ -341,6 +388,10 @@ fn cbor_value_to_dhall( let y = cbor_value_to_dhall(&y)?; Annot(x, y) } + [U64(28), x] => { + let x = cbor_value_to_dhall(&x)?; + EmptyListLit(x) + } _ => Err(DecodeError::WrongFormatError(format!("{:?}", data)))?, }, _ => Err(DecodeError::WrongFormatError(format!("{:?}", data)))?, @@ -389,7 +440,6 @@ enum Serialize<'a> { CBOR(cbor::Value), RecordMap(&'a DupTreeMap<Label, ParsedSubExpr>), UnionMap(&'a DupTreeMap<Label, Option<ParsedSubExpr>>), - Import(&'a Import), } macro_rules! count { @@ -414,6 +464,7 @@ where S: serde::ser::Serializer, { use cbor::Value::{String, I64, U64}; + use dhall_syntax::Builtin; use dhall_syntax::ExprF::*; use std::iter::once; @@ -455,12 +506,23 @@ where ser_seq!(ser; tag(2), expr(x), expr(y)) } Pi(l, x, y) => ser_seq!(ser; tag(2), label(l), expr(x), expr(y)), - // TODO: multilet - Let(l, None, x, y) => { - ser_seq!(ser; tag(25), label(l), null(), expr(x), expr(y)) - } - Let(l, Some(t), x, y) => { - ser_seq!(ser; tag(25), label(l), expr(t), expr(x), expr(y)) + Let(_, _, _, _) => { + let (bound_e, bindings) = collect_nested_lets(e); + let count = 1 + 3 * bindings.len() + 1; + + use serde::ser::SerializeSeq; + let mut ser_seq = ser.serialize_seq(Some(count))?; + ser_seq.serialize_element(&tag(25))?; + for (l, t, v) in bindings { + ser_seq.serialize_element(&label(l))?; + match t { + Some(t) => ser_seq.serialize_element(&expr(t))?, + None => ser_seq.serialize_element(&null())?, + } + ser_seq.serialize_element(&expr(v))?; + } + ser_seq.serialize_element(&expr(bound_e))?; + ser_seq.end() } App(_, _) => { let (f, args) = collect_nested_applications(e); @@ -471,10 +533,15 @@ where ) } Annot(x, y) => ser_seq!(ser; tag(26), expr(x), expr(y)), - OldOptionalLit(None, t) => ser_seq!(ser; tag(5), expr(t)), - OldOptionalLit(Some(x), t) => ser_seq!(ser; tag(5), expr(t), expr(x)), + Assert(x) => ser_seq!(ser; tag(19), expr(x)), SomeLit(x) => ser_seq!(ser; tag(5), null(), expr(x)), - EmptyListLit(x) => ser_seq!(ser; tag(4), expr(x)), + EmptyListLit(x) => match x.as_ref() { + App(f, a) => match f.as_ref() { + ExprF::Builtin(Builtin::List) => ser_seq!(ser; tag(4), expr(a)), + _ => ser_seq!(ser; tag(28), expr(x)), + }, + _ => ser_seq!(ser; tag(28), expr(x)), + }, NEListLit(xs) => ser.collect_seq( once(tag(4)).chain(once(null())).chain(xs.iter().map(expr)), ), @@ -488,9 +555,6 @@ where RecordType(map) => ser_seq!(ser; tag(7), RecordMap(map)), RecordLit(map) => ser_seq!(ser; tag(8), RecordMap(map)), UnionType(map) => ser_seq!(ser; tag(11), UnionMap(map)), - UnionLit(l, x, map) => { - ser_seq!(ser; tag(12), label(l), expr(x), UnionMap(map)) - } Field(x, l) => ser_seq!(ser; tag(9), expr(x), label(l)), BinOp(op, x, y) => { use dhall_syntax::BinOp::*; @@ -507,6 +571,7 @@ where RightBiasedRecordMerge => 9, RecursiveRecordTypeMerge => 10, ImportAlt => 11, + Equivalence => 12, }; ser_seq!(ser; tag(3), U64(op), expr(x), expr(y)) } @@ -553,6 +618,7 @@ where let mode = match import.mode { ImportMode::Code => 0, ImportMode::RawText => 1, + ImportMode::Location => 2, }; ser_seq.serialize_element(&U64(mode))?; @@ -576,12 +642,14 @@ where ImportLocation::Remote(url) => { match &url.headers { None => ser_seq.serialize_element(&Null)?, - Some(location_hashed) => ser_seq.serialize_element( - &self::Serialize::Import(&Import { - mode: ImportMode::Code, - location_hashed: location_hashed.as_ref().clone(), - }), - )?, + Some(location_hashed) => { + ser_seq.serialize_element(&self::Serialize::Expr( + &SubExpr::from_expr_no_note(ExprF::Embed(Import { + mode: ImportMode::Code, + location_hashed: location_hashed.as_ref().clone(), + })), + ))? + } }; ser_seq.serialize_element(&url.authority)?; for p in url.path.clone().into_iter() { @@ -628,7 +696,6 @@ impl<'a> serde::ser::Serialize for Serialize<'a> { (cbor::Value::String(k.into()), v) })) } - Serialize::Import(import) => serialize_import(ser, import), } } } @@ -652,3 +719,26 @@ fn collect_nested_applications<'a, N, E>( let e = go(e, &mut vec); (e, vec) } + +type LetBinding<'a, N, E> = + (&'a Label, &'a Option<SubExpr<N, E>>, &'a SubExpr<N, E>); + +fn collect_nested_lets<'a, N, E>( + e: &'a SubExpr<N, E>, +) -> (&'a SubExpr<N, E>, Vec<LetBinding<'a, N, E>>) { + fn go<'a, N, E>( + e: &'a SubExpr<N, E>, + vec: &mut Vec<LetBinding<'a, N, E>>, + ) -> &'a SubExpr<N, E> { + match e.as_ref() { + ExprF::Let(l, t, v, e) => { + vec.push((l, t, v)); + go(e, vec) + } + _ => e, + } + } + let mut vec = vec![]; + let e = go(e, &mut vec); + (e, vec) +} diff --git a/dhall/src/phase/mod.rs b/dhall/src/phase/mod.rs index 681b7fe..ccedff2 100644 --- a/dhall/src/phase/mod.rs +++ b/dhall/src/phase/mod.rs @@ -127,9 +127,6 @@ impl Typed { pub fn from_value_untyped(v: Value) -> Self { Typed::from_thunk_untyped(Thunk::from_value(v)) } - pub fn from_normalized_expr_untyped(e: NormalizedSubExpr) -> Self { - Typed::from_thunk_untyped(Thunk::from_normalized_expr(e)) - } // TODO: Avoid cloning if possible pub fn to_value(&self) -> Value { diff --git a/dhall/src/phase/normalize.rs b/dhall/src/phase/normalize.rs index 35d32cb..405677a 100644 --- a/dhall/src/phase/normalize.rs +++ b/dhall/src/phase/normalize.rs @@ -47,6 +47,17 @@ pub fn apply_builtin(b: Builtin, args: Vec<Thunk>) -> Value { )), _ => Err(()), }, + (NaturalSubtract, [a, b, r..]) => { + match (&*a.as_value(), &*b.as_value()) { + (NaturalLit(a), NaturalLit(b)) => { + Ok((r, NaturalLit(if b > a { b - a } else { 0 }))) + } + (NaturalLit(0), b) => Ok((r, b.clone())), + (_, NaturalLit(0)) => Ok((r, NaturalLit(0))), + _ if a == b => Ok((r, NaturalLit(0))), + _ => Err(()), + } + } (IntegerShow, [n, r..]) => match &*n.as_value() { IntegerLit(n) => { let s = if *n < 0 { @@ -72,6 +83,17 @@ pub fn apply_builtin(b: Builtin, args: Vec<Thunk>) -> Value { (TextShow, [v, r..]) => match &*v.as_value() { TextLit(elts) => { match elts.as_slice() { + // Empty string literal. + [] => { + // Printing InterpolatedText takes care of all the escaping + let txt: InterpolatedText<X> = + std::iter::empty().collect(); + let s = txt.to_string(); + Ok(( + r, + TextLit(vec![InterpolatedTextContents::Text(s)]), + )) + } // If there are no interpolations (invariants ensure that when there are no // interpolations, there is a single Text item) in the literal. [InterpolatedTextContents::Text(s)] => { @@ -487,9 +509,9 @@ where fn apply_binop<'a>(o: BinOp, x: &'a Thunk, y: &'a Thunk) -> Option<Ret<'a>> { use BinOp::{ - BoolAnd, BoolEQ, BoolNE, BoolOr, ListAppend, NaturalPlus, NaturalTimes, - RecursiveRecordMerge, RecursiveRecordTypeMerge, RightBiasedRecordMerge, - TextAppend, + BoolAnd, BoolEQ, BoolNE, BoolOr, Equivalence, ListAppend, NaturalPlus, + NaturalTimes, RecursiveRecordMerge, RecursiveRecordTypeMerge, + RightBiasedRecordMerge, TextAppend, }; use Value::{ BoolLit, EmptyListLit, NEListLit, NaturalLit, RecordLit, RecordType, @@ -605,13 +627,18 @@ fn apply_binop<'a>(o: BinOp, x: &'a Thunk, y: &'a Thunk) -> Option<Ret<'a>> { Ret::Value(RecordType(kvs)) } + (Equivalence, _, _) => Ret::Value(Value::Equivalence( + TypeThunk::from_thunk(x.clone()), + TypeThunk::from_thunk(y.clone()), + )), + _ => return None, }) } pub fn normalize_one_layer(expr: ExprF<Thunk, X>) -> Value { use Value::{ - BoolLit, DoubleLit, EmptyListLit, EmptyOptionalLit, IntegerLit, Lam, + AppliedBuiltin, BoolLit, DoubleLit, EmptyListLit, IntegerLit, Lam, NEListLit, NEOptionalLit, NaturalLit, Pi, RecordLit, RecordType, TextLit, UnionConstructor, UnionLit, UnionType, }; @@ -620,6 +647,7 @@ pub fn normalize_one_layer(expr: ExprF<Thunk, X>) -> Value { ExprF::Embed(_) => unreachable!(), ExprF::Var(_) => unreachable!(), ExprF::Annot(x, _) => Ret::Thunk(x), + ExprF::Assert(_) => Ret::Expr(expr), ExprF::Lam(x, t, e) => { Ret::Value(Lam(x.into(), TypeThunk::from_thunk(t), e)) } @@ -639,13 +667,21 @@ pub fn normalize_one_layer(expr: ExprF<Thunk, X>) -> Value { ExprF::NaturalLit(n) => Ret::Value(NaturalLit(n)), ExprF::IntegerLit(n) => Ret::Value(IntegerLit(n)), ExprF::DoubleLit(n) => Ret::Value(DoubleLit(n)), - ExprF::OldOptionalLit(None, t) => { - Ret::Value(EmptyOptionalLit(TypeThunk::from_thunk(t))) - } - ExprF::OldOptionalLit(Some(e), _) => Ret::Value(NEOptionalLit(e)), ExprF::SomeLit(e) => Ret::Value(NEOptionalLit(e)), - ExprF::EmptyListLit(t) => { - Ret::Value(EmptyListLit(TypeThunk::from_thunk(t))) + ExprF::EmptyListLit(ref t) => { + // Check if the type is of the form `List x` + let t_borrow = t.as_value(); + match &*t_borrow { + AppliedBuiltin(Builtin::List, args) if args.len() == 1 => { + Ret::Value(EmptyListLit(TypeThunk::from_thunk( + args[0].clone(), + ))) + } + _ => { + drop(t_borrow); + Ret::Expr(expr) + } + } } ExprF::NEListLit(elts) => { Ret::Value(NEListLit(elts.into_iter().collect())) @@ -658,13 +694,6 @@ pub fn normalize_one_layer(expr: ExprF<Thunk, X>) -> Value { .map(|(k, t)| (k, TypeThunk::from_thunk(t))) .collect(), )), - ExprF::UnionLit(l, x, kts) => Ret::Value(UnionLit( - l, - x, - kts.into_iter() - .map(|(k, t)| (k, t.map(|t| TypeThunk::from_thunk(t)))) - .collect(), - )), ExprF::UnionType(kts) => Ret::Value(UnionType( kts.into_iter() .map(|(k, t)| (k, t.map(|t| TypeThunk::from_thunk(t)))) diff --git a/dhall/src/phase/resolve.rs b/dhall/src/phase/resolve.rs index ba75be4..ac264e6 100644 --- a/dhall/src/phase/resolve.rs +++ b/dhall/src/phase/resolve.rs @@ -87,7 +87,7 @@ fn do_resolve_expr( } } }; - let expr = expr.traverse_embed(resolve)?; + let expr = expr.traverse_resolve(resolve)?; Ok(Resolved(expr)) } @@ -101,7 +101,7 @@ pub fn skip_resolve_expr( let resolve = |import: &Import| -> Result<Normalized, ImportError> { Err(ImportError::UnexpectedImport(import.clone())) }; - let expr = expr.traverse_embed(resolve)?; + let expr = expr.traverse_resolve(resolve)?; Ok(Resolved(expr)) } @@ -111,24 +111,35 @@ mod spec_tests { macro_rules! import_success { ($name:ident, $path:expr) => { - make_spec_test!(Import, Success, $name, &("success/".to_owned() + $path)); + make_spec_test!(Import, Success, $name, &("../dhall-lang/tests/import/success/".to_owned() + $path)); }; } // macro_rules! import_failure { // ($name:ident, $path:expr) => { - // make_spec_test!(Import, Failure, $name, &("failure/".to_owned() + $path)); + // make_spec_test!(Import, Failure, $name, &("../dhall-lang/tests/import/failure/".to_owned() + $path)); // }; // } // import_success!(success_alternativeEnvNatural, "alternativeEnvNatural"); // import_success!(success_alternativeEnvSimple, "alternativeEnvSimple"); + // import_success!(success_alternativeHashMismatch, "alternativeHashMismatch"); // import_success!(success_alternativeNatural, "alternativeNatural"); + // import_success!(success_alternativeParseError, "alternativeParseError"); + // import_success!(success_alternativeTypeError, "alternativeTypeError"); + // import_success!(success_asLocation, "asLocation"); // import_success!(success_asText, "asText"); + // import_success!(success_customHeaders, "customHeaders"); import_success!(success_fieldOrder, "fieldOrder"); + // note: this one needs special setup with env variables + // import_success!(success_hashFromCache, "hashFromCache"); + // import_success!(success_headerForwarding, "headerForwarding"); + // import_success!(success_nestedHash, "nestedHash"); + // import_success!(success_noHeaderForwarding, "noHeaderForwarding"); // import_failure!(failure_alternativeEnv, "alternativeEnv"); // import_failure!(failure_alternativeEnvMissing, "alternativeEnvMissing"); // import_failure!(failure_cycle, "cycle"); + // import_failure!(failure_hashMismatch, "hashMismatch"); // import_failure!(failure_missing, "missing"); // import_failure!(failure_referentiallyInsane, "referentiallyInsane"); } diff --git a/dhall/src/phase/typecheck.rs b/dhall/src/phase/typecheck.rs index a3f676c..299997a 100644 --- a/dhall/src/phase/typecheck.rs +++ b/dhall/src/phase/typecheck.rs @@ -1,6 +1,5 @@ use std::collections::HashMap; -use dhall_proc_macros as dhall; use dhall_syntax::{ rc, Builtin, Const, Expr, ExprF, InterpolatedTextContents, Label, Span, SubExpr, X, @@ -13,24 +12,6 @@ use crate::core::var::{Shift, Subst}; use crate::error::{TypeError, TypeMessage}; use crate::phase::{Normalized, Resolved, Type, Typed}; -macro_rules! ensure_equal { - ($x:expr, $y:expr, $err:expr $(,)*) => { - if $x.to_value() != $y.to_value() { - return Err($err); - } - }; -} - -// Ensure the provided type has type `Type` -macro_rules! ensure_simple_type { - ($x:expr, $err:expr $(,)*) => {{ - match $x.get_type()?.as_const() { - Some(dhall_syntax::Const::Type) => {} - _ => return Err($err), - } - }}; -} - fn tck_pi_type( ctx: &TypecheckContext, x: Label, @@ -60,18 +41,7 @@ fn tck_pi_type( } }; - let k = match function_check(ka, kb) { - Ok(k) => k, - Err(()) => { - return Err(TypeError::new( - ctx, - NoDependentTypes( - tx.to_normalized(), - te.get_type()?.to_normalized(), - ), - )) - } - }; + let k = function_check(ka, kb); Ok(Typed::from_thunk_and_type( Value::Pi(x.into(), TypeThunk::from_type(tx), TypeThunk::from_type(te)) @@ -164,45 +134,13 @@ fn tck_union_type( )) } -fn tck_list_type(ctx: &TypecheckContext, t: Type) -> Result<Typed, TypeError> { - use crate::error::TypeMessage::*; - ensure_simple_type!( - t, - TypeError::new(ctx, InvalidListType(t.to_normalized())), - ); - Ok(Typed::from_thunk_and_type( - Value::from_builtin(Builtin::List) - .app(t.to_value()) - .into_thunk(), - Type::from_const(Const::Type), - )) -} - -fn tck_optional_type( - ctx: &TypecheckContext, - t: Type, -) -> Result<Typed, TypeError> { - use crate::error::TypeMessage::*; - ensure_simple_type!( - t, - TypeError::new(ctx, InvalidOptionalType(t.to_normalized())), - ); - Ok(Typed::from_thunk_and_type( - Value::from_builtin(Builtin::Optional) - .app(t.to_value()) - .into_thunk(), - Type::from_const(Const::Type), - )) -} - -fn function_check(a: Const, b: Const) -> Result<Const, ()> { - use dhall_syntax::Const::*; - match (a, b) { - (_, Type) => Ok(Type), - (Kind, Kind) => Ok(Kind), - (Sort, Sort) => Ok(Sort), - (Sort, Kind) => Ok(Sort), - _ => Err(()), +fn function_check(a: Const, b: Const) -> Const { + use dhall_syntax::Const::Type; + use std::cmp::max; + if b == Type { + Type + } else { + max(a, b) } } @@ -216,40 +154,97 @@ pub fn type_of_const(c: Const) -> Result<Type, TypeError> { } } +// Ad-hoc macro to help construct the types of builtins +macro_rules! make_type { + (Type) => { ExprF::Const(Const::Type) }; + (Bool) => { ExprF::Builtin(Builtin::Bool) }; + (Natural) => { ExprF::Builtin(Builtin::Natural) }; + (Integer) => { ExprF::Builtin(Builtin::Integer) }; + (Double) => { ExprF::Builtin(Builtin::Double) }; + (Text) => { ExprF::Builtin(Builtin::Text) }; + ($var:ident) => { + ExprF::Var(dhall_syntax::V(stringify!($var).into(), 0)) + }; + (Optional $ty:ident) => { + ExprF::App( + rc(ExprF::Builtin(Builtin::Optional)), + rc(make_type!($ty)) + ) + }; + (List $($rest:tt)*) => { + ExprF::App( + rc(ExprF::Builtin(Builtin::List)), + rc(make_type!($($rest)*)) + ) + }; + ({ $($label:ident : $ty:ident),* }) => {{ + let mut kts = dhall_syntax::map::DupTreeMap::new(); + $( + kts.insert( + Label::from(stringify!($label)), + rc(make_type!($ty)), + ); + )* + ExprF::RecordType(kts) + }}; + ($ty:ident -> $($rest:tt)*) => { + ExprF::Pi( + "_".into(), + rc(make_type!($ty)), + rc(make_type!($($rest)*)) + ) + }; + (($($arg:tt)*) -> $($rest:tt)*) => { + ExprF::Pi( + "_".into(), + rc(make_type!($($arg)*)), + rc(make_type!($($rest)*)) + ) + }; + (forall ($var:ident : $($ty:tt)*) -> $($rest:tt)*) => { + ExprF::Pi( + stringify!($var).into(), + rc(make_type!($($ty)*)), + rc(make_type!($($rest)*)) + ) + }; +} + fn type_of_builtin(b: Builtin) -> Expr<X, X> { use dhall_syntax::Builtin::*; match b { - Bool | Natural | Integer | Double | Text => dhall::expr!(Type), - List | Optional => dhall::expr!( + Bool | Natural | Integer | Double | Text => make_type!(Type), + List | Optional => make_type!( Type -> Type ), - NaturalFold => dhall::expr!( + NaturalFold => make_type!( Natural -> forall (natural: Type) -> forall (succ: natural -> natural) -> forall (zero: natural) -> natural ), - NaturalBuild => dhall::expr!( + NaturalBuild => make_type!( (forall (natural: Type) -> forall (succ: natural -> natural) -> forall (zero: natural) -> natural) -> Natural ), - NaturalIsZero | NaturalEven | NaturalOdd => dhall::expr!( + NaturalIsZero | NaturalEven | NaturalOdd => make_type!( Natural -> Bool ), - NaturalToInteger => dhall::expr!(Natural -> Integer), - NaturalShow => dhall::expr!(Natural -> Text), + NaturalToInteger => make_type!(Natural -> Integer), + NaturalShow => make_type!(Natural -> Text), + NaturalSubtract => make_type!(Natural -> Natural -> Natural), - IntegerToDouble => dhall::expr!(Integer -> Double), - IntegerShow => dhall::expr!(Integer -> Text), - DoubleShow => dhall::expr!(Double -> Text), - TextShow => dhall::expr!(Text -> Text), + IntegerToDouble => make_type!(Integer -> Double), + IntegerShow => make_type!(Integer -> Text), + DoubleShow => make_type!(Double -> Text), + TextShow => make_type!(Text -> Text), - ListBuild => dhall::expr!( + ListBuild => make_type!( forall (a: Type) -> (forall (list: Type) -> forall (cons: a -> list -> list) -> @@ -257,28 +252,28 @@ fn type_of_builtin(b: Builtin) -> Expr<X, X> { list) -> List a ), - ListFold => dhall::expr!( + ListFold => make_type!( forall (a: Type) -> - List a -> + (List a) -> forall (list: Type) -> forall (cons: a -> list -> list) -> forall (nil: list) -> list ), - ListLength => dhall::expr!(forall (a: Type) -> List a -> Natural), + ListLength => make_type!(forall (a: Type) -> (List a) -> Natural), ListHead | ListLast => { - dhall::expr!(forall (a: Type) -> List a -> Optional a) + make_type!(forall (a: Type) -> (List a) -> Optional a) } - ListIndexed => dhall::expr!( + ListIndexed => make_type!( forall (a: Type) -> - List a -> + (List a) -> List { index: Natural, value: a } ), - ListReverse => dhall::expr!( - forall (a: Type) -> List a -> List a + ListReverse => make_type!( + forall (a: Type) -> (List a) -> List a ), - OptionalBuild => dhall::expr!( + OptionalBuild => make_type!( forall (a: Type) -> (forall (optional: Type) -> forall (just: a -> optional) -> @@ -286,15 +281,15 @@ fn type_of_builtin(b: Builtin) -> Expr<X, X> { optional) -> Optional a ), - OptionalFold => dhall::expr!( + OptionalFold => make_type!( forall (a: Type) -> - Optional a -> + (Optional a) -> forall (optional: Type) -> forall (just: a -> optional) -> forall (nothing: optional) -> optional ), - OptionalNone => dhall::expr!( + OptionalNone => make_type!( forall (a: Type) -> Optional a ), } @@ -329,9 +324,7 @@ fn type_with( ctx: &TypecheckContext, e: SubExpr<Span, Normalized>, ) -> Result<Typed, TypeError> { - use dhall_syntax::ExprF::{ - Annot, App, Embed, Lam, Let, OldOptionalLit, Pi, SomeLit, Var, - }; + use dhall_syntax::ExprF::{Annot, Embed, Lam, Let, Pi, Var}; use Ret::*; Ok(match e.as_ref() { @@ -364,18 +357,6 @@ fn type_with( let v = type_with(ctx, v)?; return type_with(&ctx.insert_value(x, v.clone())?, e.clone()); } - OldOptionalLit(None, t) => { - let none = SubExpr::from_builtin(Builtin::OptionalNone); - let e = e.rewrap(App(none, t.clone())); - return type_with(ctx, e); - } - OldOptionalLit(Some(x), t) => { - let optional = SubExpr::from_builtin(Builtin::Optional); - let x = x.rewrap(SomeLit(x.clone())); - let t = t.rewrap(App(optional, t.clone())); - let e = e.rewrap(Annot(x, t)); - return type_with(ctx, e); - } Embed(p) => p.clone().into_typed(), Var(var) => match ctx.lookup(&var) { Some(typed) => typed, @@ -418,87 +399,132 @@ fn type_last_layer( use crate::error::TypeMessage::*; use dhall_syntax::BinOp::*; use dhall_syntax::Builtin::*; + use dhall_syntax::Const::Type; use dhall_syntax::ExprF::*; use Ret::*; let mkerr = |msg: TypeMessage| TypeError::new(ctx, msg); match e { - Lam(_, _, _) - | Pi(_, _, _) - | Let(_, _, _, _) - | OldOptionalLit(_, _) - | Embed(_) - | Var(_) => unreachable!(), + Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => { + unreachable!() + } App(f, a) => { let tf = f.get_type()?; let (x, tx, tb) = match &tf.to_value() { Value::Pi(x, tx, tb) => (x.clone(), tx.to_type(), tb.to_type()), _ => return Err(mkerr(NotAFunction(f.clone()))), }; - ensure_equal!(a.get_type()?, &tx, { - mkerr(TypeMismatch(f.clone(), tx.to_normalized(), a.clone())) - }); + if a.get_type()?.as_ref() != &tx { + return Err(mkerr(TypeMismatch( + f.clone(), + tx.to_normalized(), + a.clone(), + ))); + } Ok(RetTypeOnly(tb.subst_shift(&x.into(), &a))) } Annot(x, t) => { let t = t.to_type(); - ensure_equal!( - &t, - x.get_type()?, - mkerr(AnnotMismatch(x.clone(), t.to_normalized())) - ); + if &t != x.get_type()?.as_ref() { + return Err(mkerr(AnnotMismatch(x.clone(), t.to_normalized()))); + } Ok(RetTypeOnly(x.get_type()?.into_owned())) } + Assert(t) => { + match t.to_value() { + Value::Equivalence(x, y) if x == y => {} + Value::Equivalence(x, y) => { + return Err(mkerr(AssertMismatch( + x.to_typed(), + y.to_typed(), + ))) + } + _ => return Err(mkerr(AssertMustTakeEquivalence)), + } + Ok(RetTypeOnly(t.to_type())) + } BoolIf(x, y, z) => { - ensure_equal!( - x.get_type()?, - &builtin_to_type(Bool)?, - mkerr(InvalidPredicate(x.clone())), - ); + if x.get_type()?.as_ref() != &builtin_to_type(Bool)? { + return Err(mkerr(InvalidPredicate(x.clone()))); + } - ensure_simple_type!( - y.get_type()?, - mkerr(IfBranchMustBeTerm(true, y.clone())), - ); + if y.get_type()?.get_type()?.as_const() != Some(Type) { + return Err(mkerr(IfBranchMustBeTerm(true, y.clone()))); + } - ensure_simple_type!( - z.get_type()?, - mkerr(IfBranchMustBeTerm(false, z.clone())), - ); + if z.get_type()?.get_type()?.as_const() != Some(Type) { + return Err(mkerr(IfBranchMustBeTerm(false, z.clone()))); + } - ensure_equal!( - y.get_type()?, - z.get_type()?, - mkerr(IfBranchMismatch(y.clone(), z.clone())) - ); + if y.get_type()? != z.get_type()? { + return Err(mkerr(IfBranchMismatch(y.clone(), z.clone()))); + } Ok(RetTypeOnly(y.get_type()?.into_owned())) } EmptyListLit(t) => { let t = t.to_type(); - Ok(RetTypeOnly(tck_list_type(ctx, t)?.to_type())) + match &t.to_value() { + Value::AppliedBuiltin(dhall_syntax::Builtin::List, args) + if args.len() == 1 => {} + _ => { + return Err(TypeError::new( + ctx, + InvalidListType(t.to_normalized()), + )) + } + } + Ok(RetTypeOnly(t)) } NEListLit(xs) => { let mut iter = xs.iter().enumerate(); let (_, x) = iter.next().unwrap(); for (i, y) in iter { - ensure_equal!( - x.get_type()?, - y.get_type()?, - mkerr(InvalidListElement( + if x.get_type()? != y.get_type()? { + return Err(mkerr(InvalidListElement( i, x.get_type()?.to_normalized(), - y.clone() - )) - ); + y.clone(), + ))); + } } - let t = x.get_type()?.into_owned(); - Ok(RetTypeOnly(tck_list_type(ctx, t)?.to_type())) + let t = x.get_type()?; + if t.get_type()?.as_const() != Some(Type) { + return Err(TypeError::new( + ctx, + InvalidListType(t.to_normalized()), + )); + } + + Ok(RetTypeOnly( + Typed::from_thunk_and_type( + Value::from_builtin(dhall_syntax::Builtin::List) + .app(t.to_value()) + .into_thunk(), + Typed::from_const(Type), + ) + .to_type(), + )) } SomeLit(x) => { let t = x.get_type()?.into_owned(); - Ok(RetTypeOnly(tck_optional_type(ctx, t)?.to_type())) + if t.get_type()?.as_const() != Some(Type) { + return Err(TypeError::new( + ctx, + InvalidOptionalType(t.to_normalized()), + )); + } + + Ok(RetTypeOnly( + Typed::from_thunk_and_type( + Value::from_builtin(dhall_syntax::Builtin::Optional) + .app(t.to_value()) + .into_thunk(), + Typed::from_const(Type).into_type(), + ) + .to_type(), + )) } RecordType(kts) => Ok(RetWhole(tck_record_type( ctx, @@ -517,15 +543,6 @@ fn type_last_layer( )? .into_type(), )), - UnionLit(x, v, kvs) => { - use std::iter::once; - let kts = kvs - .iter() - .map(|(x, v)| Ok((x.clone(), v.as_ref().map(|v| v.to_type())))); - let t = v.get_type()?.into_owned(); - let kts = kts.chain(once(Ok((x.clone(), Some(t))))); - Ok(RetTypeOnly(tck_union_type(ctx, kts)?.to_type())) - } Field(r, x) => { match &r.get_type()?.to_value() { Value::RecordType(kts) => match kts.get(&x) { @@ -589,11 +606,9 @@ fn type_last_layer( for contents in interpolated.iter() { use InterpolatedTextContents::Expr; if let Expr(x) = contents { - ensure_equal!( - x.get_type()?, - &text_type, - mkerr(InvalidTextInterpolation(x.clone())), - ); + if x.get_type()?.as_ref() != &text_type { + return Err(mkerr(InvalidTextInterpolation(x.clone()))); + } } } Ok(RetTypeOnly(text_type)) @@ -609,11 +624,9 @@ fn type_last_layer( // Check the equality of kinds. // This is to disallow expression such as: // "{ x = Text } // { y = 1 }" - ensure_equal!( - l_kind, - r_kind, - mkerr(RecordMismatch(l.clone(), r.clone())), - ); + if l_kind != r_kind { + return Err(mkerr(RecordMismatch(l.clone(), r.clone()))); + } // Extract the LHS record type let kts_x = match l_type.to_value() { @@ -693,11 +706,9 @@ fn type_last_layer( // Check the equality of kinds. // This is to disallow expression such as: // "{ x = Text } // { y = 1 }" - ensure_equal!( - l_kind, - r_kind, - mkerr(RecordMismatch(l.clone(), r.clone())), - ); + if l_kind != r_kind { + return Err(mkerr(RecordMismatch(l.clone(), r.clone()))); + } // Extract the LHS record type let kts_x = match l_type.to_value() { @@ -819,14 +830,35 @@ fn type_last_layer( _ => return Err(mkerr(BinOpTypeMismatch(*o, l.clone()))), } - ensure_equal!( - l.get_type()?, - r.get_type()?, - mkerr(BinOpTypeMismatch(*o, r.clone())) - ); + if l.get_type()? != r.get_type()? { + return Err(mkerr(BinOpTypeMismatch(*o, r.clone()))); + } Ok(RetTypeOnly(l.get_type()?.into_owned())) } + BinOp(Equivalence, l, r) => { + if l.get_type()?.get_type()?.as_const() != Some(Type) { + return Err(mkerr(EquivalenceArgumentMustBeTerm( + true, + l.clone(), + ))); + } + if r.get_type()?.get_type()?.as_const() != Some(Type) { + return Err(mkerr(EquivalenceArgumentMustBeTerm( + false, + r.clone(), + ))); + } + + if l.get_type()? != r.get_type()? { + return Err(mkerr(EquivalenceTypeMismatch( + r.clone(), + l.clone(), + ))); + } + + Ok(RetTypeOnly(Typed::from_const(Type).into_type())) + } BinOp(o, l, r) => { let t = builtin_to_type(match o { BoolAnd => Bool, @@ -840,20 +872,17 @@ fn type_last_layer( RightBiasedRecordMerge => unreachable!(), RecursiveRecordMerge => unreachable!(), RecursiveRecordTypeMerge => unreachable!(), - _ => return Err(mkerr(Unimplemented)), + ImportAlt => unreachable!("There should remain no import alternatives in a resolved expression"), + Equivalence => unreachable!(), })?; - ensure_equal!( - l.get_type()?, - &t, - mkerr(BinOpTypeMismatch(*o, l.clone())) - ); + if l.get_type()?.as_ref() != &t { + return Err(mkerr(BinOpTypeMismatch(*o, l.clone()))); + } - ensure_equal!( - r.get_type()?, - &t, - mkerr(BinOpTypeMismatch(*o, r.clone())) - ); + if r.get_type()?.as_ref() != &t { + return Err(mkerr(BinOpTypeMismatch(*o, r.clone()))); + } Ok(RetTypeOnly(t)) } @@ -882,13 +911,13 @@ fn type_last_layer( _ => return Err(mkerr(NotAFunction(handler_type))), }; - ensure_equal!(&variant_type, &tx, { - mkerr(TypeMismatch( + if &variant_type != &tx { + return Err(mkerr(TypeMismatch( handler_type, tx.to_normalized(), variant_type, - )) - }); + ))); + } // Extract `tb` from under the `x` binder. Fails is `x` was free in `tb`. match tb.over_binder(x) { @@ -911,11 +940,9 @@ fn type_last_layer( match &inferred_type { None => inferred_type = Some(handler_return_type), Some(t) => { - ensure_equal!( - t, - &handler_return_type, - mkerr(MergeHandlerTypeMismatch) - ); + if t != &handler_return_type { + return Err(mkerr(MergeHandlerTypeMismatch)); + } } } } @@ -928,7 +955,9 @@ fn type_last_layer( match (inferred_type, type_annot) { (Some(ref t1), Some(t2)) => { let t2 = t2.to_type(); - ensure_equal!(t1, &t2, mkerr(MergeAnnotMismatch)); + if t1 != &t2 { + return Err(mkerr(MergeAnnotMismatch)); + } Ok(RetTypeOnly(t2)) } (Some(t), None) => Ok(RetTypeOnly(t)), @@ -985,394 +1014,3 @@ pub fn typecheck_with(e: Resolved, ty: &Type) -> Result<Typed, TypeError> { pub fn skip_typecheck(e: Resolved) -> Typed { Typed::from_thunk_untyped(Thunk::new(NormalizationContext::new(), e.0)) } - -#[cfg(test)] -mod spec_tests { - #![rustfmt::skip] - - macro_rules! tc_success { - ($name:ident, $path:expr) => { - make_spec_test!(Typecheck, Success, $name, &("success/".to_owned() + $path)); - }; - } - macro_rules! tc_failure { - ($name:ident, $path:expr) => { - make_spec_test!(Typecheck, Failure, $name, &("failure/".to_owned() + $path)); - }; - } - - macro_rules! ti_success { - ($name:ident, $path:expr) => { - make_spec_test!(TypeInference, Success, $name, &("success/".to_owned() + $path)); - }; - } - // macro_rules! ti_failure { - // ($name:ident, $path:expr) => { - // make_spec_test!(TypeInference, Failure, $name, &("failure/".to_owned() + $path)); - // }; - // } - - // tc_success!(tc_success_accessEncodedType, "accessEncodedType"); - // tc_success!(tc_success_accessType, "accessType"); - tc_success!(tc_success_prelude_Bool_and_0, "prelude/Bool/and/0"); - tc_success!(tc_success_prelude_Bool_and_1, "prelude/Bool/and/1"); - tc_success!(tc_success_prelude_Bool_build_0, "prelude/Bool/build/0"); - tc_success!(tc_success_prelude_Bool_build_1, "prelude/Bool/build/1"); - tc_success!(tc_success_prelude_Bool_even_0, "prelude/Bool/even/0"); - tc_success!(tc_success_prelude_Bool_even_1, "prelude/Bool/even/1"); - tc_success!(tc_success_prelude_Bool_even_2, "prelude/Bool/even/2"); - tc_success!(tc_success_prelude_Bool_even_3, "prelude/Bool/even/3"); - tc_success!(tc_success_prelude_Bool_fold_0, "prelude/Bool/fold/0"); - tc_success!(tc_success_prelude_Bool_fold_1, "prelude/Bool/fold/1"); - tc_success!(tc_success_prelude_Bool_not_0, "prelude/Bool/not/0"); - tc_success!(tc_success_prelude_Bool_not_1, "prelude/Bool/not/1"); - tc_success!(tc_success_prelude_Bool_odd_0, "prelude/Bool/odd/0"); - tc_success!(tc_success_prelude_Bool_odd_1, "prelude/Bool/odd/1"); - tc_success!(tc_success_prelude_Bool_odd_2, "prelude/Bool/odd/2"); - tc_success!(tc_success_prelude_Bool_odd_3, "prelude/Bool/odd/3"); - tc_success!(tc_success_prelude_Bool_or_0, "prelude/Bool/or/0"); - tc_success!(tc_success_prelude_Bool_or_1, "prelude/Bool/or/1"); - tc_success!(tc_success_prelude_Bool_show_0, "prelude/Bool/show/0"); - tc_success!(tc_success_prelude_Bool_show_1, "prelude/Bool/show/1"); - tc_success!(tc_success_prelude_Double_show_0, "prelude/Double/show/0"); - tc_success!(tc_success_prelude_Double_show_1, "prelude/Double/show/1"); - tc_success!(tc_success_prelude_Integer_show_0, "prelude/Integer/show/0"); - tc_success!(tc_success_prelude_Integer_show_1, "prelude/Integer/show/1"); - tc_success!(tc_success_prelude_Integer_toDouble_0, "prelude/Integer/toDouble/0"); - tc_success!(tc_success_prelude_Integer_toDouble_1, "prelude/Integer/toDouble/1"); - tc_success!(tc_success_prelude_List_all_0, "prelude/List/all/0"); - tc_success!(tc_success_prelude_List_all_1, "prelude/List/all/1"); - tc_success!(tc_success_prelude_List_any_0, "prelude/List/any/0"); - tc_success!(tc_success_prelude_List_any_1, "prelude/List/any/1"); - tc_success!(tc_success_prelude_List_build_0, "prelude/List/build/0"); - tc_success!(tc_success_prelude_List_build_1, "prelude/List/build/1"); - tc_success!(tc_success_prelude_List_concat_0, "prelude/List/concat/0"); - tc_success!(tc_success_prelude_List_concat_1, "prelude/List/concat/1"); - tc_success!(tc_success_prelude_List_concatMap_0, "prelude/List/concatMap/0"); - tc_success!(tc_success_prelude_List_concatMap_1, "prelude/List/concatMap/1"); - tc_success!(tc_success_prelude_List_filter_0, "prelude/List/filter/0"); - tc_success!(tc_success_prelude_List_filter_1, "prelude/List/filter/1"); - tc_success!(tc_success_prelude_List_fold_0, "prelude/List/fold/0"); - tc_success!(tc_success_prelude_List_fold_1, "prelude/List/fold/1"); - tc_success!(tc_success_prelude_List_fold_2, "prelude/List/fold/2"); - tc_success!(tc_success_prelude_List_generate_0, "prelude/List/generate/0"); - tc_success!(tc_success_prelude_List_generate_1, "prelude/List/generate/1"); - tc_success!(tc_success_prelude_List_head_0, "prelude/List/head/0"); - tc_success!(tc_success_prelude_List_head_1, "prelude/List/head/1"); - tc_success!(tc_success_prelude_List_indexed_0, "prelude/List/indexed/0"); - tc_success!(tc_success_prelude_List_indexed_1, "prelude/List/indexed/1"); - tc_success!(tc_success_prelude_List_iterate_0, "prelude/List/iterate/0"); - tc_success!(tc_success_prelude_List_iterate_1, "prelude/List/iterate/1"); - tc_success!(tc_success_prelude_List_last_0, "prelude/List/last/0"); - tc_success!(tc_success_prelude_List_last_1, "prelude/List/last/1"); - tc_success!(tc_success_prelude_List_length_0, "prelude/List/length/0"); - tc_success!(tc_success_prelude_List_length_1, "prelude/List/length/1"); - tc_success!(tc_success_prelude_List_map_0, "prelude/List/map/0"); - tc_success!(tc_success_prelude_List_map_1, "prelude/List/map/1"); - tc_success!(tc_success_prelude_List_null_0, "prelude/List/null/0"); - tc_success!(tc_success_prelude_List_null_1, "prelude/List/null/1"); - tc_success!(tc_success_prelude_List_replicate_0, "prelude/List/replicate/0"); - tc_success!(tc_success_prelude_List_replicate_1, "prelude/List/replicate/1"); - tc_success!(tc_success_prelude_List_reverse_0, "prelude/List/reverse/0"); - tc_success!(tc_success_prelude_List_reverse_1, "prelude/List/reverse/1"); - tc_success!(tc_success_prelude_List_shifted_0, "prelude/List/shifted/0"); - tc_success!(tc_success_prelude_List_shifted_1, "prelude/List/shifted/1"); - tc_success!(tc_success_prelude_List_unzip_0, "prelude/List/unzip/0"); - tc_success!(tc_success_prelude_List_unzip_1, "prelude/List/unzip/1"); - tc_success!(tc_success_prelude_Monoid_00, "prelude/Monoid/00"); - tc_success!(tc_success_prelude_Monoid_01, "prelude/Monoid/01"); - tc_success!(tc_success_prelude_Monoid_02, "prelude/Monoid/02"); - tc_success!(tc_success_prelude_Monoid_03, "prelude/Monoid/03"); - tc_success!(tc_success_prelude_Monoid_04, "prelude/Monoid/04"); - tc_success!(tc_success_prelude_Monoid_05, "prelude/Monoid/05"); - tc_success!(tc_success_prelude_Monoid_06, "prelude/Monoid/06"); - tc_success!(tc_success_prelude_Monoid_07, "prelude/Monoid/07"); - tc_success!(tc_success_prelude_Monoid_08, "prelude/Monoid/08"); - tc_success!(tc_success_prelude_Monoid_09, "prelude/Monoid/09"); - tc_success!(tc_success_prelude_Monoid_10, "prelude/Monoid/10"); - tc_success!(tc_success_prelude_Natural_build_0, "prelude/Natural/build/0"); - tc_success!(tc_success_prelude_Natural_build_1, "prelude/Natural/build/1"); - tc_success!(tc_success_prelude_Natural_enumerate_0, "prelude/Natural/enumerate/0"); - tc_success!(tc_success_prelude_Natural_enumerate_1, "prelude/Natural/enumerate/1"); - tc_success!(tc_success_prelude_Natural_even_0, "prelude/Natural/even/0"); - tc_success!(tc_success_prelude_Natural_even_1, "prelude/Natural/even/1"); - tc_success!(tc_success_prelude_Natural_fold_0, "prelude/Natural/fold/0"); - tc_success!(tc_success_prelude_Natural_fold_1, "prelude/Natural/fold/1"); - tc_success!(tc_success_prelude_Natural_fold_2, "prelude/Natural/fold/2"); - tc_success!(tc_success_prelude_Natural_isZero_0, "prelude/Natural/isZero/0"); - tc_success!(tc_success_prelude_Natural_isZero_1, "prelude/Natural/isZero/1"); - tc_success!(tc_success_prelude_Natural_odd_0, "prelude/Natural/odd/0"); - tc_success!(tc_success_prelude_Natural_odd_1, "prelude/Natural/odd/1"); - tc_success!(tc_success_prelude_Natural_product_0, "prelude/Natural/product/0"); - tc_success!(tc_success_prelude_Natural_product_1, "prelude/Natural/product/1"); - tc_success!(tc_success_prelude_Natural_show_0, "prelude/Natural/show/0"); - tc_success!(tc_success_prelude_Natural_show_1, "prelude/Natural/show/1"); - tc_success!(tc_success_prelude_Natural_sum_0, "prelude/Natural/sum/0"); - tc_success!(tc_success_prelude_Natural_sum_1, "prelude/Natural/sum/1"); - tc_success!(tc_success_prelude_Natural_toDouble_0, "prelude/Natural/toDouble/0"); - tc_success!(tc_success_prelude_Natural_toDouble_1, "prelude/Natural/toDouble/1"); - tc_success!(tc_success_prelude_Natural_toInteger_0, "prelude/Natural/toInteger/0"); - tc_success!(tc_success_prelude_Natural_toInteger_1, "prelude/Natural/toInteger/1"); - tc_success!(tc_success_prelude_Optional_all_0, "prelude/Optional/all/0"); - tc_success!(tc_success_prelude_Optional_all_1, "prelude/Optional/all/1"); - tc_success!(tc_success_prelude_Optional_any_0, "prelude/Optional/any/0"); - tc_success!(tc_success_prelude_Optional_any_1, "prelude/Optional/any/1"); - tc_success!(tc_success_prelude_Optional_build_0, "prelude/Optional/build/0"); - tc_success!(tc_success_prelude_Optional_build_1, "prelude/Optional/build/1"); - tc_success!(tc_success_prelude_Optional_concat_0, "prelude/Optional/concat/0"); - tc_success!(tc_success_prelude_Optional_concat_1, "prelude/Optional/concat/1"); - tc_success!(tc_success_prelude_Optional_concat_2, "prelude/Optional/concat/2"); - tc_success!(tc_success_prelude_Optional_filter_0, "prelude/Optional/filter/0"); - tc_success!(tc_success_prelude_Optional_filter_1, "prelude/Optional/filter/1"); - tc_success!(tc_success_prelude_Optional_fold_0, "prelude/Optional/fold/0"); - tc_success!(tc_success_prelude_Optional_fold_1, "prelude/Optional/fold/1"); - tc_success!(tc_success_prelude_Optional_head_0, "prelude/Optional/head/0"); - tc_success!(tc_success_prelude_Optional_head_1, "prelude/Optional/head/1"); - tc_success!(tc_success_prelude_Optional_head_2, "prelude/Optional/head/2"); - tc_success!(tc_success_prelude_Optional_last_0, "prelude/Optional/last/0"); - tc_success!(tc_success_prelude_Optional_last_1, "prelude/Optional/last/1"); - tc_success!(tc_success_prelude_Optional_last_2, "prelude/Optional/last/2"); - tc_success!(tc_success_prelude_Optional_length_0, "prelude/Optional/length/0"); - tc_success!(tc_success_prelude_Optional_length_1, "prelude/Optional/length/1"); - tc_success!(tc_success_prelude_Optional_map_0, "prelude/Optional/map/0"); - tc_success!(tc_success_prelude_Optional_map_1, "prelude/Optional/map/1"); - tc_success!(tc_success_prelude_Optional_null_0, "prelude/Optional/null/0"); - tc_success!(tc_success_prelude_Optional_null_1, "prelude/Optional/null/1"); - tc_success!(tc_success_prelude_Optional_toList_0, "prelude/Optional/toList/0"); - tc_success!(tc_success_prelude_Optional_toList_1, "prelude/Optional/toList/1"); - tc_success!(tc_success_prelude_Optional_unzip_0, "prelude/Optional/unzip/0"); - tc_success!(tc_success_prelude_Optional_unzip_1, "prelude/Optional/unzip/1"); - tc_success!(tc_success_prelude_Text_concat_0, "prelude/Text/concat/0"); - tc_success!(tc_success_prelude_Text_concat_1, "prelude/Text/concat/1"); - tc_success!(tc_success_prelude_Text_concatMap_0, "prelude/Text/concatMap/0"); - tc_success!(tc_success_prelude_Text_concatMap_1, "prelude/Text/concatMap/1"); - // tc_success!(tc_success_prelude_Text_concatMapSep_0, "prelude/Text/concatMapSep/0"); - // tc_success!(tc_success_prelude_Text_concatMapSep_1, "prelude/Text/concatMapSep/1"); - // tc_success!(tc_success_prelude_Text_concatSep_0, "prelude/Text/concatSep/0"); - // tc_success!(tc_success_prelude_Text_concatSep_1, "prelude/Text/concatSep/1"); - tc_success!(tc_success_recordOfRecordOfTypes, "recordOfRecordOfTypes"); - tc_success!(tc_success_recordOfTypes, "recordOfTypes"); - // tc_success!(tc_success_simple_access_0, "simple/access/0"); - // tc_success!(tc_success_simple_access_1, "simple/access/1"); - // tc_success!(tc_success_simple_anonymousFunctionsInTypes, "simple/anonymousFunctionsInTypes"); - // tc_success!(tc_success_simple_fieldsAreTypes, "simple/fieldsAreTypes"); - // tc_success!(tc_success_simple_kindParameter, "simple/kindParameter"); - tc_success!(tc_success_simple_mergeEquivalence, "simple/mergeEquivalence"); - // tc_success!(tc_success_simple_mixedFieldAccess, "simple/mixedFieldAccess"); - tc_success!(tc_success_simple_unionsOfTypes, "simple/unionsOfTypes"); - - tc_failure!(tc_failure_combineMixedRecords, "combineMixedRecords"); - tc_failure!(tc_failure_duplicateFields, "duplicateFields"); - tc_failure!(tc_failure_hurkensParadox, "hurkensParadox"); - // tc_failure!(tc_failure_importBoundary, "importBoundary"); - tc_failure!(tc_failure_mixedUnions, "mixedUnions"); - tc_failure!(tc_failure_preferMixedRecords, "preferMixedRecords"); - tc_failure!(tc_failure_unit_FunctionApplicationArgumentNotMatch, "unit/FunctionApplicationArgumentNotMatch"); - tc_failure!(tc_failure_unit_FunctionApplicationIsNotFunction, "unit/FunctionApplicationIsNotFunction"); - tc_failure!(tc_failure_unit_FunctionArgumentTypeNotAType, "unit/FunctionArgumentTypeNotAType"); - tc_failure!(tc_failure_unit_FunctionDependentType, "unit/FunctionDependentType"); - tc_failure!(tc_failure_unit_FunctionDependentType2, "unit/FunctionDependentType2"); - tc_failure!(tc_failure_unit_FunctionTypeArgumentTypeNotAType, "unit/FunctionTypeArgumentTypeNotAType"); - tc_failure!(tc_failure_unit_FunctionTypeKindSort, "unit/FunctionTypeKindSort"); - tc_failure!(tc_failure_unit_FunctionTypeTypeKind, "unit/FunctionTypeTypeKind"); - tc_failure!(tc_failure_unit_FunctionTypeTypeSort, "unit/FunctionTypeTypeSort"); - tc_failure!(tc_failure_unit_IfBranchesNotMatch, "unit/IfBranchesNotMatch"); - tc_failure!(tc_failure_unit_IfBranchesNotType, "unit/IfBranchesNotType"); - tc_failure!(tc_failure_unit_IfNotBool, "unit/IfNotBool"); - tc_failure!(tc_failure_unit_LetWithWrongAnnotation, "unit/LetWithWrongAnnotation"); - tc_failure!(tc_failure_unit_ListLiteralEmptyNotType, "unit/ListLiteralEmptyNotType"); - tc_failure!(tc_failure_unit_ListLiteralNotType, "unit/ListLiteralNotType"); - tc_failure!(tc_failure_unit_ListLiteralTypesNotMatch, "unit/ListLiteralTypesNotMatch"); - tc_failure!(tc_failure_unit_MergeAlternativeHasNoHandler, "unit/MergeAlternativeHasNoHandler"); - tc_failure!(tc_failure_unit_MergeAnnotationNotType, "unit/MergeAnnotationNotType"); - tc_failure!(tc_failure_unit_MergeEmptyWithoutAnnotation, "unit/MergeEmptyWithoutAnnotation"); - tc_failure!(tc_failure_unit_MergeHandlerNotFunction, "unit/MergeHandlerNotFunction"); - tc_failure!(tc_failure_unit_MergeHandlerNotInUnion, "unit/MergeHandlerNotInUnion"); - tc_failure!(tc_failure_unit_MergeHandlerNotMatchAlternativeType, "unit/MergeHandlerNotMatchAlternativeType"); - tc_failure!(tc_failure_unit_MergeHandlersWithDifferentType, "unit/MergeHandlersWithDifferentType"); - tc_failure!(tc_failure_unit_MergeLhsNotRecord, "unit/MergeLhsNotRecord"); - tc_failure!(tc_failure_unit_MergeRhsNotUnion, "unit/MergeRhsNotUnion"); - tc_failure!(tc_failure_unit_MergeWithWrongAnnotation, "unit/MergeWithWrongAnnotation"); - tc_failure!(tc_failure_unit_OperatorAndNotBool, "unit/OperatorAndNotBool"); - tc_failure!(tc_failure_unit_OperatorEqualNotBool, "unit/OperatorEqualNotBool"); - tc_failure!(tc_failure_unit_OperatorListConcatenateLhsNotList, "unit/OperatorListConcatenateLhsNotList"); - tc_failure!(tc_failure_unit_OperatorListConcatenateListsNotMatch, "unit/OperatorListConcatenateListsNotMatch"); - tc_failure!(tc_failure_unit_OperatorListConcatenateNotListsButMatch, "unit/OperatorListConcatenateNotListsButMatch"); - tc_failure!(tc_failure_unit_OperatorListConcatenateRhsNotList, "unit/OperatorListConcatenateRhsNotList"); - tc_failure!(tc_failure_unit_OperatorNotEqualNotBool, "unit/OperatorNotEqualNotBool"); - tc_failure!(tc_failure_unit_OperatorOrNotBool, "unit/OperatorOrNotBool"); - tc_failure!(tc_failure_unit_OperatorPlusNotNatural, "unit/OperatorPlusNotNatural"); - tc_failure!(tc_failure_unit_OperatorTextConcatenateLhsNotText, "unit/OperatorTextConcatenateLhsNotText"); - tc_failure!(tc_failure_unit_OperatorTextConcatenateRhsNotText, "unit/OperatorTextConcatenateRhsNotText"); - tc_failure!(tc_failure_unit_OperatorTimesNotNatural, "unit/OperatorTimesNotNatural"); - tc_failure!(tc_failure_unit_RecordMixedKinds, "unit/RecordMixedKinds"); - tc_failure!(tc_failure_unit_RecordMixedKinds2, "unit/RecordMixedKinds2"); - tc_failure!(tc_failure_unit_RecordMixedKinds3, "unit/RecordMixedKinds3"); - tc_failure!(tc_failure_unit_RecordProjectionEmpty, "unit/RecordProjectionEmpty"); - tc_failure!(tc_failure_unit_RecordProjectionNotPresent, "unit/RecordProjectionNotPresent"); - tc_failure!(tc_failure_unit_RecordProjectionNotRecord, "unit/RecordProjectionNotRecord"); - tc_failure!(tc_failure_unit_RecordSelectionEmpty, "unit/RecordSelectionEmpty"); - tc_failure!(tc_failure_unit_RecordSelectionNotPresent, "unit/RecordSelectionNotPresent"); - tc_failure!(tc_failure_unit_RecordSelectionNotRecord, "unit/RecordSelectionNotRecord"); - tc_failure!(tc_failure_unit_RecordSelectionTypeNotUnionType, "unit/RecordSelectionTypeNotUnionType"); - tc_failure!(tc_failure_unit_RecordTypeMixedKinds, "unit/RecordTypeMixedKinds"); - tc_failure!(tc_failure_unit_RecordTypeMixedKinds2, "unit/RecordTypeMixedKinds2"); - tc_failure!(tc_failure_unit_RecordTypeMixedKinds3, "unit/RecordTypeMixedKinds3"); - tc_failure!(tc_failure_unit_RecordTypeValueMember, "unit/RecordTypeValueMember"); - tc_failure!(tc_failure_unit_RecursiveRecordMergeLhsNotRecord, "unit/RecursiveRecordMergeLhsNotRecord"); - tc_failure!(tc_failure_unit_RecursiveRecordMergeMixedKinds, "unit/RecursiveRecordMergeMixedKinds"); - tc_failure!(tc_failure_unit_RecursiveRecordMergeOverlapping, "unit/RecursiveRecordMergeOverlapping"); - tc_failure!(tc_failure_unit_RecursiveRecordMergeRhsNotRecord, "unit/RecursiveRecordMergeRhsNotRecord"); - tc_failure!(tc_failure_unit_RecursiveRecordTypeMergeLhsNotRecordType, "unit/RecursiveRecordTypeMergeLhsNotRecordType"); - tc_failure!(tc_failure_unit_RecursiveRecordTypeMergeOverlapping, "unit/RecursiveRecordTypeMergeOverlapping"); - tc_failure!(tc_failure_unit_RecursiveRecordTypeMergeRhsNotRecordType, "unit/RecursiveRecordTypeMergeRhsNotRecordType"); - tc_failure!(tc_failure_unit_RightBiasedRecordMergeLhsNotRecord, "unit/RightBiasedRecordMergeLhsNotRecord"); - tc_failure!(tc_failure_unit_RightBiasedRecordMergeMixedKinds, "unit/RightBiasedRecordMergeMixedKinds"); - tc_failure!(tc_failure_unit_RightBiasedRecordMergeMixedKinds2, "unit/RightBiasedRecordMergeMixedKinds2"); - tc_failure!(tc_failure_unit_RightBiasedRecordMergeMixedKinds3, "unit/RightBiasedRecordMergeMixedKinds3"); - tc_failure!(tc_failure_unit_RightBiasedRecordMergeRhsNotRecord, "unit/RightBiasedRecordMergeRhsNotRecord"); - tc_failure!(tc_failure_unit_SomeNotType, "unit/SomeNotType"); - tc_failure!(tc_failure_unit_Sort, "unit/Sort"); - tc_failure!(tc_failure_unit_TextLiteralInterpolateNotText, "unit/TextLiteralInterpolateNotText"); - tc_failure!(tc_failure_unit_TypeAnnotationWrong, "unit/TypeAnnotationWrong"); - tc_failure!(tc_failure_unit_UnionConstructorFieldNotPresent, "unit/UnionConstructorFieldNotPresent"); - tc_failure!(tc_failure_unit_UnionTypeMixedKinds, "unit/UnionTypeMixedKinds"); - tc_failure!(tc_failure_unit_UnionTypeMixedKinds2, "unit/UnionTypeMixedKinds2"); - tc_failure!(tc_failure_unit_UnionTypeMixedKinds3, "unit/UnionTypeMixedKinds3"); - tc_failure!(tc_failure_unit_UnionTypeNotType, "unit/UnionTypeNotType"); - tc_failure!(tc_failure_unit_VariableFree, "unit/VariableFree"); - - ti_success!(ti_success_simple_alternativesAreTypes, "simple/alternativesAreTypes"); - ti_success!(ti_success_simple_kindParameter, "simple/kindParameter"); - ti_success!(ti_success_unit_Bool, "unit/Bool"); - ti_success!(ti_success_unit_Double, "unit/Double"); - ti_success!(ti_success_unit_DoubleLiteral, "unit/DoubleLiteral"); - ti_success!(ti_success_unit_DoubleShow, "unit/DoubleShow"); - ti_success!(ti_success_unit_False, "unit/False"); - ti_success!(ti_success_unit_Function, "unit/Function"); - ti_success!(ti_success_unit_FunctionApplication, "unit/FunctionApplication"); - ti_success!(ti_success_unit_FunctionNamedArg, "unit/FunctionNamedArg"); - ti_success!(ti_success_unit_FunctionTypeKindKind, "unit/FunctionTypeKindKind"); - ti_success!(ti_success_unit_FunctionTypeKindTerm, "unit/FunctionTypeKindTerm"); - ti_success!(ti_success_unit_FunctionTypeKindType, "unit/FunctionTypeKindType"); - ti_success!(ti_success_unit_FunctionTypeTermTerm, "unit/FunctionTypeTermTerm"); - ti_success!(ti_success_unit_FunctionTypeTypeTerm, "unit/FunctionTypeTypeTerm"); - ti_success!(ti_success_unit_FunctionTypeTypeType, "unit/FunctionTypeTypeType"); - ti_success!(ti_success_unit_FunctionTypeUsingArgument, "unit/FunctionTypeUsingArgument"); - ti_success!(ti_success_unit_If, "unit/If"); - ti_success!(ti_success_unit_IfNormalizeArguments, "unit/IfNormalizeArguments"); - ti_success!(ti_success_unit_Integer, "unit/Integer"); - ti_success!(ti_success_unit_IntegerLiteral, "unit/IntegerLiteral"); - ti_success!(ti_success_unit_IntegerShow, "unit/IntegerShow"); - ti_success!(ti_success_unit_IntegerToDouble, "unit/IntegerToDouble"); - ti_success!(ti_success_unit_Kind, "unit/Kind"); - ti_success!(ti_success_unit_Let, "unit/Let"); - ti_success!(ti_success_unit_LetNestedTypeSynonym, "unit/LetNestedTypeSynonym"); - ti_success!(ti_success_unit_LetTypeSynonym, "unit/LetTypeSynonym"); - ti_success!(ti_success_unit_LetWithAnnotation, "unit/LetWithAnnotation"); - ti_success!(ti_success_unit_List, "unit/List"); - ti_success!(ti_success_unit_ListBuild, "unit/ListBuild"); - ti_success!(ti_success_unit_ListFold, "unit/ListFold"); - ti_success!(ti_success_unit_ListHead, "unit/ListHead"); - ti_success!(ti_success_unit_ListIndexed, "unit/ListIndexed"); - ti_success!(ti_success_unit_ListLast, "unit/ListLast"); - ti_success!(ti_success_unit_ListLength, "unit/ListLength"); - ti_success!(ti_success_unit_ListLiteralEmpty, "unit/ListLiteralEmpty"); - ti_success!(ti_success_unit_ListLiteralNormalizeArguments, "unit/ListLiteralNormalizeArguments"); - ti_success!(ti_success_unit_ListLiteralOne, "unit/ListLiteralOne"); - ti_success!(ti_success_unit_ListReverse, "unit/ListReverse"); - ti_success!(ti_success_unit_MergeEmptyUnion, "unit/MergeEmptyUnion"); - ti_success!(ti_success_unit_MergeOne, "unit/MergeOne"); - ti_success!(ti_success_unit_MergeOneEmpty, "unit/MergeOneEmpty"); - ti_success!(ti_success_unit_MergeOneWithAnnotation, "unit/MergeOneWithAnnotation"); - ti_success!(ti_success_unit_Natural, "unit/Natural"); - ti_success!(ti_success_unit_NaturalBuild, "unit/NaturalBuild"); - ti_success!(ti_success_unit_NaturalEven, "unit/NaturalEven"); - ti_success!(ti_success_unit_NaturalFold, "unit/NaturalFold"); - ti_success!(ti_success_unit_NaturalIsZero, "unit/NaturalIsZero"); - ti_success!(ti_success_unit_NaturalLiteral, "unit/NaturalLiteral"); - ti_success!(ti_success_unit_NaturalOdd, "unit/NaturalOdd"); - ti_success!(ti_success_unit_NaturalShow, "unit/NaturalShow"); - ti_success!(ti_success_unit_NaturalToInteger, "unit/NaturalToInteger"); - ti_success!(ti_success_unit_None, "unit/None"); - ti_success!(ti_success_unit_OldOptionalNone, "unit/OldOptionalNone"); - ti_success!(ti_success_unit_OldOptionalTrue, "unit/OldOptionalTrue"); - ti_success!(ti_success_unit_OperatorAnd, "unit/OperatorAnd"); - ti_success!(ti_success_unit_OperatorAndNormalizeArguments, "unit/OperatorAndNormalizeArguments"); - ti_success!(ti_success_unit_OperatorEqual, "unit/OperatorEqual"); - ti_success!(ti_success_unit_OperatorEqualNormalizeArguments, "unit/OperatorEqualNormalizeArguments"); - ti_success!(ti_success_unit_OperatorListConcatenate, "unit/OperatorListConcatenate"); - ti_success!(ti_success_unit_OperatorListConcatenateNormalizeArguments, "unit/OperatorListConcatenateNormalizeArguments"); - ti_success!(ti_success_unit_OperatorNotEqual, "unit/OperatorNotEqual"); - ti_success!(ti_success_unit_OperatorNotEqualNormalizeArguments, "unit/OperatorNotEqualNormalizeArguments"); - ti_success!(ti_success_unit_OperatorOr, "unit/OperatorOr"); - ti_success!(ti_success_unit_OperatorOrNormalizeArguments, "unit/OperatorOrNormalizeArguments"); - ti_success!(ti_success_unit_OperatorPlus, "unit/OperatorPlus"); - ti_success!(ti_success_unit_OperatorPlusNormalizeArguments, "unit/OperatorPlusNormalizeArguments"); - ti_success!(ti_success_unit_OperatorTextConcatenate, "unit/OperatorTextConcatenate"); - ti_success!(ti_success_unit_OperatorTextConcatenateNormalizeArguments, "unit/OperatorTextConcatenateNormalizeArguments"); - ti_success!(ti_success_unit_OperatorTimes, "unit/OperatorTimes"); - ti_success!(ti_success_unit_OperatorTimesNormalizeArguments, "unit/OperatorTimesNormalizeArguments"); - ti_success!(ti_success_unit_Optional, "unit/Optional"); - ti_success!(ti_success_unit_OptionalBuild, "unit/OptionalBuild"); - ti_success!(ti_success_unit_OptionalFold, "unit/OptionalFold"); - ti_success!(ti_success_unit_RecordEmpty, "unit/RecordEmpty"); - ti_success!(ti_success_unit_RecordNestedKind, "unit/RecordNestedKind"); - ti_success!(ti_success_unit_RecordNestedKindLike, "unit/RecordNestedKindLike"); - ti_success!(ti_success_unit_RecordNestedType, "unit/RecordNestedType"); - ti_success!(ti_success_unit_RecordNestedTypeLike, "unit/RecordNestedTypeLike"); - ti_success!(ti_success_unit_RecordOneKind, "unit/RecordOneKind"); - ti_success!(ti_success_unit_RecordOneType, "unit/RecordOneType"); - ti_success!(ti_success_unit_RecordOneValue, "unit/RecordOneValue"); - ti_success!(ti_success_unit_RecordProjectionEmpty, "unit/RecordProjectionEmpty"); - ti_success!(ti_success_unit_RecordProjectionKind, "unit/RecordProjectionKind"); - ti_success!(ti_success_unit_RecordProjectionType, "unit/RecordProjectionType"); - ti_success!(ti_success_unit_RecordProjectionValue, "unit/RecordProjectionValue"); - ti_success!(ti_success_unit_RecordSelectionKind, "unit/RecordSelectionKind"); - ti_success!(ti_success_unit_RecordSelectionType, "unit/RecordSelectionType"); - ti_success!(ti_success_unit_RecordSelectionValue, "unit/RecordSelectionValue"); - ti_success!(ti_success_unit_RecordType, "unit/RecordType"); - ti_success!(ti_success_unit_RecordTypeEmpty, "unit/RecordTypeEmpty"); - ti_success!(ti_success_unit_RecordTypeKind, "unit/RecordTypeKind"); - ti_success!(ti_success_unit_RecordTypeKindLike, "unit/RecordTypeKindLike"); - ti_success!(ti_success_unit_RecordTypeNestedKind, "unit/RecordTypeNestedKind"); - ti_success!(ti_success_unit_RecordTypeNestedKindLike, "unit/RecordTypeNestedKindLike"); - ti_success!(ti_success_unit_RecordTypeType, "unit/RecordTypeType"); - ti_success!(ti_success_unit_RecursiveRecordMergeLhsEmpty, "unit/RecursiveRecordMergeLhsEmpty"); - ti_success!(ti_success_unit_RecursiveRecordMergeRecursively, "unit/RecursiveRecordMergeRecursively"); - ti_success!(ti_success_unit_RecursiveRecordMergeRecursivelyKinds, "unit/RecursiveRecordMergeRecursivelyKinds"); - ti_success!(ti_success_unit_RecursiveRecordMergeRecursivelyTypes, "unit/RecursiveRecordMergeRecursivelyTypes"); - ti_success!(ti_success_unit_RecursiveRecordMergeRhsEmpty, "unit/RecursiveRecordMergeRhsEmpty"); - ti_success!(ti_success_unit_RecursiveRecordMergeTwo, "unit/RecursiveRecordMergeTwo"); - ti_success!(ti_success_unit_RecursiveRecordMergeTwoKinds, "unit/RecursiveRecordMergeTwoKinds"); - ti_success!(ti_success_unit_RecursiveRecordMergeTwoTypes, "unit/RecursiveRecordMergeTwoTypes"); - ti_success!(ti_success_unit_RecursiveRecordTypeMergeRecursively, "unit/RecursiveRecordTypeMergeRecursively"); - ti_success!(ti_success_unit_RecursiveRecordTypeMergeRecursivelyKinds, "unit/RecursiveRecordTypeMergeRecursivelyKinds"); - ti_success!(ti_success_unit_RecursiveRecordTypeMergeRecursivelyTypes, "unit/RecursiveRecordTypeMergeRecursivelyTypes"); - ti_success!(ti_success_unit_RecursiveRecordTypeMergeRhsEmpty, "unit/RecursiveRecordTypeMergeRhsEmpty"); - ti_success!(ti_success_unit_RecursiveRecordTypeMergeTwo, "unit/RecursiveRecordTypeMergeTwo"); - ti_success!(ti_success_unit_RecursiveRecordTypeMergeTwoKinds, "unit/RecursiveRecordTypeMergeTwoKinds"); - ti_success!(ti_success_unit_RecursiveRecordTypeMergeTwoTypes, "unit/RecursiveRecordTypeMergeTwoTypes"); - ti_success!(ti_success_unit_RightBiasedRecordMergeRhsEmpty, "unit/RightBiasedRecordMergeRhsEmpty"); - ti_success!(ti_success_unit_RightBiasedRecordMergeTwo, "unit/RightBiasedRecordMergeTwo"); - ti_success!(ti_success_unit_RightBiasedRecordMergeTwoDifferent, "unit/RightBiasedRecordMergeTwoDifferent"); - ti_success!(ti_success_unit_RightBiasedRecordMergeTwoKinds, "unit/RightBiasedRecordMergeTwoKinds"); - ti_success!(ti_success_unit_RightBiasedRecordMergeTwoTypes, "unit/RightBiasedRecordMergeTwoTypes"); - ti_success!(ti_success_unit_SomeTrue, "unit/SomeTrue"); - ti_success!(ti_success_unit_Text, "unit/Text"); - ti_success!(ti_success_unit_TextLiteral, "unit/TextLiteral"); - ti_success!(ti_success_unit_TextLiteralNormalizeArguments, "unit/TextLiteralNormalizeArguments"); - ti_success!(ti_success_unit_TextLiteralWithInterpolation, "unit/TextLiteralWithInterpolation"); - ti_success!(ti_success_unit_TextShow, "unit/TextShow"); - ti_success!(ti_success_unit_True, "unit/True"); - ti_success!(ti_success_unit_Type, "unit/Type"); - ti_success!(ti_success_unit_TypeAnnotation, "unit/TypeAnnotation"); - ti_success!(ti_success_unit_TypeAnnotationSort, "unit/TypeAnnotationSort"); - ti_success!(ti_success_unit_UnionConstructorEmptyField, "unit/UnionConstructorEmptyField"); - ti_success!(ti_success_unit_UnionConstructorField, "unit/UnionConstructorField"); - ti_success!(ti_success_unit_UnionLiteralOne, "unit/UnionLiteralOne"); - ti_success!(ti_success_unit_UnionTypeEmpty, "unit/UnionTypeEmpty"); - ti_success!(ti_success_unit_UnionTypeKind, "unit/UnionTypeKind"); - ti_success!(ti_success_unit_UnionTypeOne, "unit/UnionTypeOne"); - ti_success!(ti_success_unit_UnionTypeType, "unit/UnionTypeType"); -} diff --git a/dhall/src/tests.rs b/dhall/src/tests.rs index 2f68dac..15bc97a 100644 --- a/dhall/src/tests.rs +++ b/dhall/src/tests.rs @@ -45,6 +45,7 @@ pub enum Feature { Parser, Printer, BinaryEncoding, + BinaryDecoding, Import, Normalization, AlphaNormalization, @@ -80,25 +81,33 @@ pub fn run_test( ) -> Result<()> { use self::Feature::*; use self::Status::*; - let feature_prefix = match feature { - Parser => "parser/", - Printer => "parser/", - BinaryEncoding => "parser/", - Import => "import/", - Normalization => "normalization/", - AlphaNormalization => "alpha-normalization/", - Typecheck => "typecheck/", - TypeInference => "type-inference/", - }; - let base_path = - "../dhall-lang/tests/".to_owned() + feature_prefix + base_path; + let base_path = base_path.to_owned(); match status { Success => { + match feature { + BinaryDecoding => { + let expr_file_path = base_path.clone() + "A.dhallb"; + let expr_file_path = PathBuf::from(&expr_file_path); + let mut expr_data = Vec::new(); + { + File::open(&expr_file_path)? + .read_to_end(&mut expr_data)?; + } + let expr = Parsed::parse_binary(&expr_data)?; + let expected_file_path = base_path + "B.dhall"; + let expected = parse_file_str(&expected_file_path)?; + assert_eq_pretty!(expr, expected); + + return Ok(()); + } + _ => {} + } let expr_file_path = base_path.clone() + "A.dhall"; let expr = parse_file_str(&expr_file_path)?; match feature { Parser => { + // This exercices both parsing and binary decoding // Compare parse/decoded let expected_file_path = base_path + "B.dhallb"; let expected_file_path = PathBuf::from(&expected_file_path); @@ -164,7 +173,9 @@ pub fn run_test( .normalize(); match feature { - Parser | Printer | BinaryEncoding => unreachable!(), + Parser | Printer | BinaryEncoding | BinaryDecoding => { + unreachable!() + } Import => { let expr = expr.skip_typecheck().normalize(); assert_eq_display!(expr, expected); @@ -195,10 +206,21 @@ pub fn run_test( let err = parse_file_str(&file_path).unwrap_err(); match err { Error::Parse(_) => {} + Error::IO(e) + if e.kind() == std::io::ErrorKind::InvalidData => {} e => panic!("Expected parse error, got: {:?}", e), } } Printer | BinaryEncoding => unreachable!(), + BinaryDecoding => { + let expr_file_path = file_path + "b"; + let mut expr_data = Vec::new(); + { + File::open(&PathBuf::from(&expr_file_path))? + .read_to_end(&mut expr_data)?; + } + Parsed::parse_binary(&expr_data).unwrap_err(); + } Import => { parse_file_str(&file_path)?.resolve().unwrap_err(); } diff --git a/dhall/tests/traits.rs b/dhall/tests/traits.rs index 75eaf75..0f75553 100644 --- a/dhall/tests/traits.rs +++ b/dhall/tests/traits.rs @@ -1,23 +1,18 @@ #![feature(proc_macro_hygiene)] -use dhall::de::{StaticType, Type}; -use dhall_proc_macros::subexpr; -use dhall_syntax::{SubExpr, X}; +use dhall::de::{from_str, StaticType, Type}; #[test] fn test_static_type() { - fn mktype(x: SubExpr<X, X>) -> Type { - Type::from_normalized_expr(x) + fn parse(s: &str) -> Type { + from_str(s, None).unwrap() } - assert_eq!(bool::static_type(), mktype(subexpr!(Bool))); - assert_eq!(String::static_type(), mktype(subexpr!(Text))); - assert_eq!( - <Option<bool>>::static_type(), - mktype(subexpr!(Optional Bool)) - ); + assert_eq!(bool::static_type(), parse("Bool")); + assert_eq!(String::static_type(), parse("Text")); + assert_eq!(<Option<bool>>::static_type(), parse("Optional Bool")); assert_eq!( <(bool, Vec<String>)>::static_type(), - mktype(subexpr!({ _1: Bool, _2: List Text })) + parse("{ _1: Bool, _2: List Text }") ); #[derive(dhall::de::StaticType)] @@ -28,7 +23,7 @@ fn test_static_type() { } assert_eq!( <A as dhall::de::StaticType>::static_type(), - mktype(subexpr!({ field1: Bool, field2: Optional Bool })) + parse("{ field1: Bool, field2: Optional Bool }") ); #[derive(StaticType)] @@ -52,7 +47,7 @@ fn test_static_type() { struct D(); assert_eq!( <C<D>>::static_type(), - mktype(subexpr!({ _1: {}, _2: Optional Text })) + parse("{ _1: {}, _2: Optional Text }") ); #[derive(StaticType)] @@ -61,10 +56,7 @@ fn test_static_type() { A(T), B(String), }; - assert_eq!( - <E<bool>>::static_type(), - mktype(subexpr!(< A: Bool | B: Text >)) - ); + assert_eq!(<E<bool>>::static_type(), parse("< A: Bool | B: Text >")); #[derive(StaticType)] #[allow(dead_code)] @@ -72,5 +64,5 @@ fn test_static_type() { A, B(bool), }; - assert_eq!(F::static_type(), mktype(subexpr!(< A | B: Bool >))); + assert_eq!(F::static_type(), parse("< A | B: Bool >")); } diff --git a/dhall_generated_parser/build.rs b/dhall_generated_parser/build.rs index eab48bf..5275097 100644 --- a/dhall_generated_parser/build.rs +++ b/dhall_generated_parser/build.rs @@ -26,6 +26,8 @@ fn main() -> std::io::Result<()> { rules.get_mut(&line[2..]).map(|x| x.silent = true); } } + rules.remove("http"); + rules.remove("url_path"); rules.remove("simple_label"); rules.remove("nonreserved_label"); @@ -41,6 +43,19 @@ fn main() -> std::io::Result<()> { | !keyword ~ simple_label_first_char ~ simple_label_next_char* }}" )?; + // TODO: this is a cheat; actually implement inline headers instead + writeln!( + &mut file, + "http = {{ + http_raw + ~ (whsp + ~ using + ~ whsp1 + ~ (import_hashed | ^\"(\" ~ whsp ~ import_hashed ~ whsp ~ ^\")\"))? + }}" + )?; + // TODO: this is a cheat; properly support RFC3986 URLs instead + writeln!(&mut file, "url_path = _{{ path }}")?; writeln!( &mut file, "nonreserved_label = _{{ diff --git a/dhall_generated_parser/src/dhall.pest.visibility b/dhall_generated_parser/src/dhall.pest.visibility index 60de54d..6b4c974 100644 --- a/dhall_generated_parser/src/dhall.pest.visibility +++ b/dhall_generated_parser/src/dhall.pest.visibility @@ -1,4 +1,5 @@ # end_of_line +# valid_non_ascii # tab # block_comment # block_comment_char @@ -10,6 +11,7 @@ # whsp1 # ALPHA # DIGIT +# ALPHANUM # HEXDIG # simple_label_first_char # simple_label_next_char @@ -21,6 +23,7 @@ label # any_label double_quote_chunk double_quote_escaped +# unicode_escape double_quote_char double_quote_literal single_quote_continue @@ -42,11 +45,14 @@ missing # Infinity NaN Some_ +toMap +assert # keyword builtin Optional Text List +Location # Bool # True # False @@ -66,6 +72,7 @@ List # Natural_show # Integer_toDouble # Integer_show +# Natural_subtract # Double_show # List_build # List_fold @@ -79,6 +86,7 @@ List # Text_show # combine # combine_types +# equivalent # prefer lambda forall @@ -116,7 +124,8 @@ authority # ls32 # IPv4address # dec_octet -# reg_name +# domain +# domainlabel # pchar query # pct_encoded @@ -134,8 +143,7 @@ import expression annotated_expression let_binding -empty_collection -non_empty_optional +empty_list_literal # operator_expression import_alt_expression or_expression @@ -149,6 +157,7 @@ combine_types_expression times_expression equal_expression not_equal_expression +equivalent_expression application_expression first_application_expression # import_expression @@ -165,11 +174,9 @@ non_empty_record_type record_type_entry non_empty_record_literal record_literal_entry -union_type_or_literal +union_type empty_union_type -non_empty_union_type_or_literal -union_literal_variant_value +# non_empty_union_type union_type_entry -union_type_or_literal_variant_type non_empty_list_literal # complete_expression diff --git a/dhall_proc_macros/src/lib.rs b/dhall_proc_macros/src/lib.rs index e4aa8b5..5304429 100644 --- a/dhall_proc_macros/src/lib.rs +++ b/dhall_proc_macros/src/lib.rs @@ -6,20 +6,9 @@ extern crate proc_macro; mod derive; -mod quote; use proc_macro::TokenStream; -#[proc_macro] -pub fn expr(input: TokenStream) -> TokenStream { - quote::expr(input) -} - -#[proc_macro] -pub fn subexpr(input: TokenStream) -> TokenStream { - quote::subexpr(input) -} - #[proc_macro_derive(StaticType)] pub fn derive_static_type(input: TokenStream) -> TokenStream { derive::derive_static_type(input) diff --git a/dhall_proc_macros/src/quote.rs b/dhall_proc_macros/src/quote.rs deleted file mode 100644 index 3dbfba9..0000000 --- a/dhall_proc_macros/src/quote.rs +++ /dev/null @@ -1,223 +0,0 @@ -extern crate proc_macro; -use dhall_syntax::context::Context; -use dhall_syntax::*; -use proc_macro2::TokenStream; -use quote::quote; - -pub fn expr(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input_str = input.to_string(); - let expr: SubExpr<_, Import> = parse_expr(&input_str).unwrap(); - let no_import = - |_: &Import| -> X { panic!("Don't use import in dhall::expr!()") }; - let expr = expr.map_embed(no_import); - let output = quote_expr(expr.as_ref(), &Context::new()); - output.into() -} - -pub fn subexpr(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input_str = input.to_string(); - let expr: SubExpr<_, Import> = parse_expr(&input_str).unwrap(); - let no_import = - |_: &Import| -> X { panic!("Don't use import in dhall::subexpr!()") }; - let expr = expr.map_embed(no_import); - let output = quote_subexpr(&expr, &Context::new()); - output.into() -} - -// Returns an expression of type ExprF<T, _>, where T is the -// type of the subexpressions after interpolation. -pub fn quote_exprf<TS>(expr: ExprF<TS, X>) -> TokenStream -where - TS: quote::ToTokens + std::fmt::Debug, -{ - use dhall_syntax::ExprF::*; - match expr { - Var(_) => unreachable!(), - Pi(x, t, b) => { - let x = quote_label(&x); - quote! { dhall_syntax::ExprF::Pi(#x, #t, #b) } - } - Lam(x, t, b) => { - let x = quote_label(&x); - quote! { dhall_syntax::ExprF::Lam(#x, #t, #b) } - } - App(f, a) => { - quote! { dhall_syntax::ExprF::App(#f, #a) } - } - Annot(x, t) => { - quote! { dhall_syntax::ExprF::Annot(#x, #t) } - } - Const(c) => { - let c = quote_const(c); - quote! { dhall_syntax::ExprF::Const(#c) } - } - Builtin(b) => { - let b = quote_builtin(b); - quote! { dhall_syntax::ExprF::Builtin(#b) } - } - BinOp(o, a, b) => { - let o = quote_binop(o); - quote! { dhall_syntax::ExprF::BinOp(#o, #a, #b) } - } - NaturalLit(n) => { - quote! { dhall_syntax::ExprF::NaturalLit(#n) } - } - BoolLit(b) => { - quote! { dhall_syntax::ExprF::BoolLit(#b) } - } - SomeLit(x) => { - quote! { dhall_syntax::ExprF::SomeLit(#x) } - } - EmptyListLit(t) => { - quote! { dhall_syntax::ExprF::EmptyListLit(#t) } - } - NEListLit(es) => { - let es = quote_vec(es); - quote! { dhall_syntax::ExprF::NEListLit(#es) } - } - RecordType(m) => { - let m = quote_map(m); - quote! { dhall_syntax::ExprF::RecordType(#m) } - } - RecordLit(m) => { - let m = quote_map(m); - quote! { dhall_syntax::ExprF::RecordLit(#m) } - } - UnionType(m) => { - let m = quote_opt_map(m); - quote! { dhall_syntax::ExprF::UnionType(#m) } - } - e => unimplemented!("{:?}", e), - } -} - -// Returns an expression of type SubExpr<_, _>. Expects interpolated variables -// to be of type SubExpr<_, _>. -fn quote_subexpr<N>( - expr: &SubExpr<N, X>, - ctx: &Context<Label, ()>, -) -> TokenStream { - use dhall_syntax::ExprF::*; - match expr.as_ref().map_ref_with_special_handling_of_binders( - |e| quote_subexpr(e, ctx), - |l, e| quote_subexpr(e, &ctx.insert(l.clone(), ())), - |_| unreachable!(), - ) { - Var(V(ref s, n)) => { - match ctx.lookup(s, n) { - // Non-free variable; interpolates as itself - Some(()) => { - let s: String = s.into(); - let var = quote! { dhall_syntax::V(#s.into(), #n) }; - rc(quote! { dhall_syntax::ExprF::Var(#var) }) - } - // Free variable; interpolates as a rust variable - None => { - let s: String = s.into(); - // TODO: insert appropriate shifts ? - let v: TokenStream = s.parse().unwrap(); - quote! { { - let x: dhall_syntax::SubExpr<_, _> = #v.clone(); - x - } } - } - } - } - e => rc(quote_exprf(e)), - } -} - -// Returns an expression of type Expr<_, _>. Expects interpolated variables -// to be of type SubExpr<_, _>. -fn quote_expr<N>(expr: &Expr<N, X>, ctx: &Context<Label, ()>) -> TokenStream { - use dhall_syntax::ExprF::*; - match expr.map_ref_with_special_handling_of_binders( - |e| quote_subexpr(e, ctx), - |l, e| quote_subexpr(e, &ctx.insert(l.clone(), ())), - |_| unreachable!(), - ) { - Var(V(ref s, n)) => { - match ctx.lookup(s, n) { - // Non-free variable; interpolates as itself - Some(()) => { - let s: String = s.into(); - let var = quote! { dhall_syntax::V(#s.into(), #n) }; - quote! { dhall_syntax::ExprF::Var(#var) } - } - // Free variable; interpolates as a rust variable - None => { - let s: String = s.into(); - // TODO: insert appropriate shifts ? - let v: TokenStream = s.parse().unwrap(); - quote! { { - let x: dhall_syntax::SubExpr<_, _> = #v.clone(); - x.unroll() - } } - } - } - } - e => quote_exprf(e), - } -} - -fn quote_builtin(b: Builtin) -> TokenStream { - format!("::dhall_syntax::Builtin::{:?}", b).parse().unwrap() -} - -fn quote_const(c: Const) -> TokenStream { - format!("::dhall_syntax::Const::{:?}", c).parse().unwrap() -} - -fn quote_binop(b: BinOp) -> TokenStream { - format!("::dhall_syntax::BinOp::{:?}", b).parse().unwrap() -} - -fn quote_label(l: &Label) -> TokenStream { - let l = String::from(l); - quote! { dhall_syntax::Label::from(#l) } -} - -fn rc(x: TokenStream) -> TokenStream { - quote! { ::dhall_syntax::rc(#x) } -} - -fn quote_opt<TS>(x: Option<TS>) -> TokenStream -where - TS: quote::ToTokens + std::fmt::Debug, -{ - match x { - Some(x) => quote!(Some(#x)), - None => quote!(None), - } -} - -fn quote_vec<TS>(e: Vec<TS>) -> TokenStream -where - TS: quote::ToTokens + std::fmt::Debug, -{ - quote! { vec![ #(#e),* ] } -} - -fn quote_map<TS>(m: impl IntoIterator<Item = (Label, TS)>) -> TokenStream -where - TS: quote::ToTokens + std::fmt::Debug, -{ - let entries = m.into_iter().map(|(k, v)| { - let k = quote_label(&k); - quote!(m.insert(#k, #v);) - }); - quote! { { - let mut m = ::dhall_syntax::map::DupTreeMap::new(); - #( #entries )* - m - } } -} - -fn quote_opt_map<TS>( - m: impl IntoIterator<Item = (Label, Option<TS>)>, -) -> TokenStream -where - TS: quote::ToTokens + std::fmt::Debug, -{ - quote_map(m.into_iter().map(|(k, v)| (k, quote_opt(v)))) -} diff --git a/dhall_syntax/src/core/expr.rs b/dhall_syntax/src/core/expr.rs index da9465d..6522cb1 100644 --- a/dhall_syntax/src/core/expr.rs +++ b/dhall_syntax/src/core/expr.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use crate::map::DupTreeMap; +use crate::map::{DupTreeMap, DupTreeSet}; use crate::visitor; use crate::*; @@ -44,7 +44,7 @@ impl From<NaiveDouble> for f64 { } /// Constants for a pure type system -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum Const { Type, Kind, @@ -99,6 +99,8 @@ pub enum BinOp { BoolEQ, /// `x != y` BoolNE, + /// x === y + Equivalence, } /// Built-ins @@ -119,6 +121,7 @@ pub enum Builtin { NaturalOdd, NaturalToInteger, NaturalShow, + NaturalSubtract, IntegerToDouble, IntegerShow, DoubleShow, @@ -174,6 +177,8 @@ pub enum ExprF<SubExpr, Embed> { Let(Label, Option<SubExpr>, SubExpr, SubExpr), /// `x : t` Annot(SubExpr, SubExpr), + /// `assert : t` + Assert(SubExpr), /// Built-in values Builtin(Builtin), // Binary operations @@ -190,14 +195,10 @@ pub enum ExprF<SubExpr, Embed> { DoubleLit(Double), /// `"Some ${interpolated} text"` TextLit(InterpolatedText<SubExpr>), - /// `[] : List t` + /// `[] : 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 }` @@ -206,14 +207,12 @@ pub enum ExprF<SubExpr, Embed> { RecordLit(DupTreeMap<Label, SubExpr>), /// `< k1 : t1, k2 >` UnionType(DupTreeMap<Label, Option<SubExpr>>), - /// `< k1 = t1, k2 : t2, k3 >` - UnionLit(Label, SubExpr, DupTreeMap<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>), + Projection(SubExpr, DupTreeSet<Label>), /// Embeds an import or the result of resolving the import Embed(Embed), } @@ -311,6 +310,35 @@ impl<N, E> Expr<N, E> { { trivial_result(self.traverse_embed(|x| Ok(map_embed(x)))) } + + pub fn traverse_resolve<E2, Err>( + &self, + visit_embed: impl FnMut(&E) -> Result<E2, Err>, + ) -> Result<Expr<N, E2>, Err> + where + N: Clone, + { + self.traverse_resolve_with_visitor(&mut visitor::ResolveVisitor( + visit_embed, + )) + } + + pub(crate) fn traverse_resolve_with_visitor<E2, Err, F1>( + &self, + visitor: &mut visitor::ResolveVisitor<F1>, + ) -> Result<Expr<N, E2>, Err> + where + N: Clone, + F1: FnMut(&E) -> 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), + } + } } impl Expr<X, X> { @@ -387,6 +415,16 @@ impl<N, E> SubExpr<N, 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> { diff --git a/dhall_syntax/src/core/import.rs b/dhall_syntax/src/core/import.rs index ea42dbc..82bb7ff 100644 --- a/dhall_syntax/src/core/import.rs +++ b/dhall_syntax/src/core/import.rs @@ -57,6 +57,7 @@ pub struct URL { pub authority: String, pub path: File, pub query: Option<String>, + // TODO: implement inline headers pub headers: Option<Box<ImportHashed>>, } @@ -71,6 +72,7 @@ pub enum Scheme { pub enum ImportMode { Code, RawText, + Location, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/dhall_syntax/src/core/map.rs b/dhall_syntax/src/core/map.rs index 63f19cd..6a0ebda 100644 --- a/dhall_syntax/src/core/map.rs +++ b/dhall_syntax/src/core/map.rs @@ -1,5 +1,6 @@ /// A sorted map that allows multiple values for each key. pub use dup_tree_map::DupTreeMap; +pub use dup_tree_set::DupTreeSet; mod one_or_more { use either::Either; @@ -232,3 +233,97 @@ mod dup_tree_map { // unsafe impl<K, V> iter::TrustedLen for IntoIter<K, V> {} } + +mod dup_tree_set { + use super::DupTreeMap; + use std::iter; + + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct DupTreeSet<K> { + map: DupTreeMap<K, ()>, + } + + pub type Iter<'a, K> = iter::Map< + super::dup_tree_map::Iter<'a, K, ()>, + for<'b> fn((&'b K, &'b ())) -> &'b K, + >; + pub type IntoIter<K> = + iter::Map<super::dup_tree_map::IntoIter<K, ()>, fn((K, ())) -> K>; + + impl<K> DupTreeSet<K> { + pub fn new() -> Self + where + K: Ord, + { + DupTreeSet { + map: DupTreeMap::new(), + } + } + + pub fn len(&self) -> usize { + self.map.len() + } + pub fn is_empty(&self) -> bool { + self.map.is_empty() + } + + pub fn iter(&self) -> Iter<'_, K> + where + K: Ord, + { + fn foo<'a, K>((k, ()): (&'a K, &'a ())) -> &'a K { + k + } + self.map.iter().map(foo) + } + } + + impl<K> Default for DupTreeSet<K> + where + K: Ord, + { + fn default() -> Self { + Self::new() + } + } + + impl<K> IntoIterator for DupTreeSet<K> + where + K: Ord + Clone, + { + type Item = K; + type IntoIter = IntoIter<K>; + + fn into_iter(self) -> Self::IntoIter { + fn foo<K>((k, ()): (K, ())) -> K { + k + } + self.map.into_iter().map(foo) + } + } + + impl<'a, K> IntoIterator for &'a DupTreeSet<K> + where + K: Ord, + { + type Item = &'a K; + type IntoIter = Iter<'a, K>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } + } + + impl<K> iter::FromIterator<K> for DupTreeSet<K> + where + K: Ord, + { + fn from_iter<T>(iter: T) -> Self + where + T: IntoIterator<Item = K>, + { + let map = iter.into_iter().map(|k| (k, ())).collect(); + DupTreeSet { map } + } + } +} diff --git a/dhall_syntax/src/core/visitor.rs b/dhall_syntax/src/core/visitor.rs index 99a9c11..18f76d9 100644 --- a/dhall_syntax/src/core/visitor.rs +++ b/dhall_syntax/src/core/visitor.rs @@ -137,17 +137,10 @@ where ), 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(dupmap(kts, v)?), RecordLit(kvs) => RecordLit(dupmap(kvs, v)?), UnionType(kts) => UnionType(optdupmap(kts, v)?), - UnionLit(k, x, kts) => { - UnionLit(k.clone(), v.visit_subexpr(x)?, optdupmap(kts, v)?) - } Merge(x, y, t) => Merge( v.visit_subexpr(x)?, v.visit_subexpr(y)?, @@ -155,6 +148,7 @@ where ), 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), }) } @@ -362,6 +356,31 @@ where } } +pub struct ResolveVisitor<F1>(pub F1); + +impl<'a, 'b, N, E, E2, Err, F1> + ExprFFallibleVisitor<'a, SubExpr<N, E>, SubExpr<N, E2>, E, E2> + for &'b mut ResolveVisitor<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() + .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> diff --git a/dhall_syntax/src/parser.rs b/dhall_syntax/src/parser.rs index 7675622..a2495ee 100644 --- a/dhall_syntax/src/parser.rs +++ b/dhall_syntax/src/parser.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use dhall_generated_parser::{DhallParser, Rule}; -use crate::map::DupTreeMap; +use crate::map::{DupTreeMap, DupTreeSet}; use crate::ExprF::*; use crate::*; @@ -80,6 +80,7 @@ impl crate::Builtin { "Natural/odd" => Some(NaturalOdd), "Natural/toInteger" => Some(NaturalToInteger), "Natural/show" => Some(NaturalShow), + "Natural/subtract" => Some(NaturalSubtract), "Integer/toDouble" => Some(IntegerToDouble), "Integer/show" => Some(IntegerShow), "Double/show" => Some(DoubleShow), @@ -318,6 +319,7 @@ fn can_be_shortcutted(rule: Rule) -> bool { | times_expression | equal_expression | not_equal_expression + | equivalent_expression | application_expression | first_application_expression | selector_expression @@ -410,11 +412,50 @@ make_parser! { "n" => "\n".to_owned(), "r" => "\r".to_owned(), "t" => "\t".to_owned(), + // "uXXXX" or "u{XXXXX}" _ => { - // "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(); + use std::convert::{TryFrom, TryInto}; + + let s = &s[1..]; + let s = if &s[0..1] == "{" { + &s[1..s.len()-1] + } else { + &s[0..s.len()] + }; + + if s.len() > 8 { + Err(format!("Escape sequences can't have more than 8 chars: \"{}\"", s))? + } + + // pad with zeroes + let s: String = std::iter::repeat('0') + .take(8 - s.len()) + .chain(s.chars()) + .collect(); + + // `s` has length 8, so `bytes` has length 4 + let bytes: &[u8] = &hex::decode(s).unwrap(); + let i = u32::from_be_bytes(bytes.try_into().unwrap()); + let c = char::try_from(i).unwrap(); + match i { + 0xD800..=0xDFFF => { + let c_ecapsed = c.escape_unicode(); + Err(format!("Escape sequences can't contain surrogate pairs: \"{}\"", c_ecapsed))? + }, + 0x0FFFE..=0x0FFFF | 0x1FFFE..=0x1FFFF | + 0x2FFFE..=0x2FFFF | 0x3FFFE..=0x3FFFF | + 0x4FFFE..=0x4FFFF | 0x5FFFE..=0x5FFFF | + 0x6FFFE..=0x6FFFF | 0x7FFFE..=0x7FFFF | + 0x8FFFE..=0x8FFFF | 0x9FFFE..=0x9FFFF | + 0xAFFFE..=0xAFFFF | 0xBFFFE..=0xBFFFF | + 0xCFFFE..=0xCFFFF | 0xDFFFE..=0xDFFFF | + 0xEFFFE..=0xEFFFF | 0xFFFFE..=0xFFFFF | + 0x10FFFE..=0x10FFFF => { + let c_ecapsed = c.escape_unicode(); + Err(format!("Escape sequences can't contain non-characters: \"{}\"", c_ecapsed))? + }, + _ => {} + } std::iter::once(c).collect() } } @@ -692,6 +733,7 @@ make_parser! { )); token_rule!(Text<()>); + token_rule!(Location<()>); rule!(import<ParsedSubExpr> as expression; span; children!( [import_hashed(location_hashed)] => { @@ -706,15 +748,28 @@ make_parser! { location_hashed })) }, + [import_hashed(location_hashed), Location(_)] => { + spanned(span, Embed(Import { + mode: ImportMode::Location, + location_hashed + })) + }, )); token_rule!(lambda<()>); token_rule!(forall<()>); token_rule!(arrow<()>); token_rule!(merge<()>); + token_rule!(assert<()>); token_rule!(if_<()>); token_rule!(in_<()>); + rule!(empty_list_literal<ParsedSubExpr> as expression; span; children!( + [expression(e)] => { + spanned(span, EmptyListLit(e)) + }, + )); + rule!(expression<ParsedSubExpr> as expression; span; children!( [lambda(()), label(l), expression(typ), arrow(()), expression(body)] => { @@ -739,6 +794,9 @@ make_parser! { [merge(()), expression(x), expression(y), expression(z)] => { spanned(span, Merge(x, y, Some(z))) }, + [assert(()), expression(x)] => { + spanned(span, Assert(x)) + }, [expression(e)] => e, )); @@ -753,21 +811,6 @@ make_parser! { token_rule!(List<()>); token_rule!(Optional<()>); - rule!(empty_collection<ParsedSubExpr> as expression; span; children!( - [List(_), expression(t)] => { - spanned(span, EmptyListLit(t)) - }, - [Optional(_), expression(t)] => { - spanned(span, OldOptionalLit(None, t)) - }, - )); - - rule!(non_empty_optional<ParsedSubExpr> as expression; span; children!( - [expression(x), Optional(_), expression(t)] => { - spanned(span, OldOptionalLit(Some(x), t)) - } - )); - rule!(import_alt_expression<ParsedSubExpr> as expression; children!( [expression(e)] => e, [expression(first), expression(rest)..] => { @@ -852,6 +895,13 @@ make_parser! { rest.fold(first, |acc, e| unspanned(BinOp(o, acc, e))) }, )); + rule!(equivalent_expression<ParsedSubExpr> as expression; children!( + [expression(e)] => e, + [expression(first), expression(rest)..] => { + let o = crate::BinOp::Equivalence; + rest.fold(first, |acc, e| unspanned(BinOp(o, acc, e))) + }, + )); rule!(annotated_expression<ParsedSubExpr> as expression; span; children!( [expression(e)] => e, @@ -861,6 +911,7 @@ make_parser! { )); token_rule!(Some_<()>); + token_rule!(toMap<()>); rule!(application_expression<ParsedSubExpr> as expression; children!( [expression(e)] => e, @@ -890,13 +941,13 @@ make_parser! { } )); - rule!(selector<Either<Label, Vec<Label>>>; children!( + rule!(selector<Either<Label, DupTreeSet<Label>>>; children!( [label(l)] => Either::Left(l), [labels(ls)] => Either::Right(ls), [expression(e)] => unimplemented!("selection by expression"), // TODO )); - rule!(labels<Vec<Label>>; children!( + rule!(labels<DupTreeSet<Label>>; children!( [label(ls)..] => ls.collect(), )); @@ -953,67 +1004,22 @@ make_parser! { [label(name), expression(expr)] => (name, expr) )); - rule!(union_type_or_literal<ParsedSubExpr> as expression; span; children!( + rule!(union_type<ParsedSubExpr> as expression; span; children!( [empty_union_type(_)] => { spanned(span, UnionType(Default::default())) }, - [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)) + [union_type_entry(entries)..] => { + spanned(span, UnionType(entries.collect())) }, )); token_rule!(empty_union_type<()>); - rule!(non_empty_union_type_or_literal - <(Option<(Label, ParsedSubExpr)>, - DupTreeMap<Label, Option<ParsedSubExpr>>)>; - 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, DupTreeMap<Label, Option<ParsedSubExpr>>)>; - children!( - [expression(e), union_type_entry(entries)..] => { - (e, entries.collect()) - }, - )); - rule!(union_type_entry<(Label, Option<ParsedSubExpr>)>; children!( [label(name), expression(expr)] => (name, Some(expr)), [label(name)] => (name, None), )); - // TODO: unary union variants - rule!(union_type_or_literal_variant_type - <(Option<ParsedSubExpr>, - (Option<(Label, ParsedSubExpr)>, - DupTreeMap<Label, Option<ParsedSubExpr>>))>; - children!( - [expression(e), non_empty_union_type_or_literal(rest)] => { - (Some(e), rest) - }, - [expression(e)] => { - (Some(e), (None, Default::default())) - }, - [non_empty_union_type_or_literal(rest)] => { - (None, rest) - }, - [] => { - (None, (None, Default::default())) - }, - )); - rule!(non_empty_list_literal<ParsedSubExpr> as expression; span; children!( [expression(items)..] => spanned( diff --git a/dhall_syntax/src/printer.rs b/dhall_syntax/src/printer.rs index 8503a1e..5312f23 100644 --- a/dhall_syntax/src/printer.rs +++ b/dhall_syntax/src/printer.rs @@ -27,17 +27,11 @@ impl<SE: Display + Clone, E: Display> Display for ExprF<SE, E> { write!(f, " = {} in {}", c, d)?; } EmptyListLit(t) => { - write!(f, "[] : List {}", t)?; + write!(f, "[] : {}", 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)?; } @@ -50,6 +44,9 @@ impl<SE: Display + Clone, E: Display> Display for ExprF<SE, E> { Annot(a, b) => { write!(f, "{} : {}", a, b)?; } + Assert(a) => { + write!(f, "assert : {}", a)?; + } ExprF::BinOp(op, a, b) => { write!(f, "{} {} {}", a, op, b)?; } @@ -91,16 +88,6 @@ impl<SE: Display + Clone, E: Display> Display for ExprF<SE, E> { } 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)?, } Ok(()) @@ -154,7 +141,6 @@ impl<S: Clone, A: Display + Clone> Expr<S, A> { | Let(_, _, _, _) | EmptyListLit(_) | NEListLit(_) - | OldOptionalLit(_, _) | SomeLit(_) | Merge(_, _, _) | Annot(_, _) @@ -189,8 +175,6 @@ impl<S: Clone, A: Display + Clone> Expr<S, A> { 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), @@ -305,6 +289,7 @@ impl Display for BinOp { ImportAlt => "?", RightBiasedRecordMerge => "⫽", ListAppend => "#", + Equivalence => "≡", }) } } @@ -445,6 +430,7 @@ impl Display for Import { match self.mode { Code => {} RawText => write!(f, " as Text")?, + Location => write!(f, " as Location")?, } Ok(()) } @@ -469,6 +455,7 @@ impl Display for Builtin { NaturalOdd => "Natural/odd", NaturalToInteger => "Natural/toInteger", NaturalShow => "Natural/show", + NaturalSubtract => "Natural/subtract", IntegerToDouble => "Integer/toDouble", IntegerShow => "Integer/show", DoubleShow => "Double/show", diff --git a/tests_buffer b/tests_buffer index 1ea9a1c..c6366ba 100644 --- a/tests_buffer +++ b/tests_buffer @@ -2,90 +2,18 @@ parser: ./a%20b ./"a%20b" text interpolation and escapes -remove `double` -remove imports/parenthesizeUsing -remove multilet -selection by expression unit tests +projection by expression unit tests success/ - imports/ - Missing missing - Headers https://example.com/foo using ./headers - HeadersInteriorHash https://example.com/foo using (./headers sha256:0000000000000000000000000000000000000000000000000000000000000000) - HeadersExteriorHash (https://example.com/foo using ./headers) sha256:0000000000000000000000000000000000000000000000000000000000000000 - HeadersHashPrecedence https://example.com/foo using ./headers sha256:0000000000000000000000000000000000000000000000000000000000000000 - HeadersDoubleHashPrecedence https://example.com/foo using ./headers sha256:0000000000000000000000000000000000000000000000000000000000000000 sha256:1111111111111111111111111111111111111111111111111111111111111111 - DoubleLitPositive 1.23 - DoubleLitNegative -1.23 - DoubleLitExponent 1.23e4 - DoubleLitExponentNoDot 1e4 - DoubleLitExponentNegative 1.23e-4 - DoubleLitInfinity Infinity - DoubleLitNegInfinity -Infinity - DoubleLitNaN NaN - DoubleLitSecretelyInt 1.0 - DoubleLitZero 0.0 - BuiltinListBuild List/Build - FunctionApplicationOneArg f x - FunctionApplicationMultipleArgs f x y z - Annotation x : T - ListLitNonEmpty [x, y] - ListLitNonEmptyAnnotated [x, y] : List T - OptionalLitEmpty []: Optional T - OptionalLitNonEmpty [x]: Optional T - Field r.x - FieldBuiltinName r.List - FieldQuoted r.`x` - Projection r.{x, y, z} - Let let x: T = v in e - LetNested let x: T = v in let y: U = w in e - LetMulti let x: T = v let y: U = w in e - LambdaUnicode λ(x : T) -> y - FunctionTypePi forall(x: T) -> U - FunctionTypePiUnicode ∀(x: T) -> U - FunctionTypePiNested forall(x: T) -> ∀(y: U) -> V - FunctionTypePiUnderscore forall(_: T) -> U - FunctionTypeArrow T -> U - RecordLit { x = 1, y = 2 } - RecordType { x: T, y: U } operators/ - ImportAlt x ? y - ImportAltAssoc w ? x ? y ? z - BoolOr x || y - BoolOrAssoc w || x || y || z - NaturalPlus x + y - NaturalPlusAssoc w + x + y + z - TextAppend x ++ y - TextAppendAssoc w ++ x ++ y ++ z - ListAppend x # y - ListAppendAssoc w # x # y # z - BoolAnd x && y - BoolAndAssoc w && x && y && z - NaturalTimes x * y - NaturalTimesAssoc w * x * y * z - BoolEQ x == y - BoolEQAssoc w == x == y == z - BoolNE x != y - BoolNEAssoc w != x != y != z - RecursiveRecordMerge x //\\ y - RecursiveRecordMergeAssoc w //\\ x //\\ y //\\ z - RecursiveRecordTypeMerge x /\ y - RecursiveRecordTypeMergeAssoc w /\ x /\ y /\ z - RightBiasedRecordMerge x // y - RightBiasedRecordMergeAssoc w // x // y // z - RecursiveRecordMergeUnicode x ∧ y - RecursiveRecordMergeUnicodeAssoc w ∧ x /\ y ∧ z - RightBiasedRecordMergeUnicode x ⫽ y - RightBiasedRecordMergeUnicodeAssoc w ⫽ x // y ⫽ z - RecursiveRecordTypeMergeUnicode x ⩓ y - RecursiveRecordTypeMergeUnicodeAssoc w ⩓ x //\\ y ⩓ z PrecedenceAll1 a ? b || c + d ++ e # f && g ∧ h ⫽ i ⩓ j * k == l != m n.o PrecedenceAll2 a b != c == d * e ⩓ f ⫽ g ∧ h && i # j ++ k + l || m ? n - PrecedenceNat a + b * d + e f * (g + h) - PrecedenceBool a && b || c d == e || f != g && h || i - PrecedenceRecord a // b c /\ d ⫽ e.{x} ∧ f + LetNoAnnot let x = y in e + LetAnnot let x: T = y in e failure/ - ProjectionByExpressionNeedsParens r.{ x: T } + AssertNoAnnotation assert +binary decoding: +decode old-style optional literals ? import: success/ @@ -96,35 +24,22 @@ failure/ normalization: variables across import boundaries + Text/show "" TextLitNested1 "${""}${x}" TextLitNested2 "${"${x}"}" TextLitNested3 "${"${""}"}${x}" - EquivalenceDouble if b then NaN else NaN - EquivalenceAlpha if b then \(x: T) -> x else \(y: T) -> y typecheck: something that involves destructuring a recordtype after merge -success/ - MergeEmptyAlternative merge { x = 1 } < x >.x - MergeTrickyShadowing let _ = Bool in merge {_ = \(x: _) -> x} (<_: Bool>._ True) - EquivalenceAlpha \(TODO: forall(x: Type) -> x) -> TODO : forall(y: Type) -> y +add some of the more complicated Prelude tests back, like List/enumerate +failure on old-style optional literal failure/ - MergeEmptyNeedsDirectAnnotation1 \(x: <>) -> (merge {=} x) : Bool - MergeEmptyNeedsDirectAnnotation2 \(x: <>) -> let y: Bool = merge {=} x in 1 - MergeBoolIsNotUnion merge x True - MergeOptionalIsNotUnion merge x (Some 1) - MergeMissingHandler1 merge {=} < x >.x - MergeMissingHandler2 merge {x=...,} <x|y>.x merge { x = λ(x : Bool) → x } (< x: Bool | y: Natural >.x True) merge { x = λ(_ : Bool) → _, y = 1 } < x = True | y > merge { x = True, y = 1 } < x | y >.x merge {x=...,y=...} <x>.x merge {x=...,y=...} <x:T>.x - MergeHandlerFreeVar merge { x = None } < x = Bool > - UnionTypeDuplicateVariants1 <x, x> - UnionTypeDuplicateVariants2 <x, x:T> - UnionLitDuplicateVariants <x=1|x:T> - RecordLitDuplicateFields { x: T, x: T } - EquivalenceAlphaTrap \(TODO: forall(_: Type) -> _) -> TODO : forall(x: Type) -> _ + MergeBoolIsNotUnion merge x True + MergeOptionalIsNotUnion merge x (Some 1) equivalence: |