diff options
-rw-r--r-- | README.md | 56 | ||||
-rw-r--r-- | dhall/src/error/mod.rs | 2 | ||||
-rw-r--r-- | dhall/src/phase/normalize.rs | 19 | ||||
-rw-r--r-- | dhall/src/phase/typecheck.rs | 52 |
4 files changed, 114 insertions, 15 deletions
@@ -20,6 +20,62 @@ This is still quite unstable so use at your own risk. Documentation is severely You can see what's missing from the commented out tests in `dhall/src/normalize.rs` and `dhall/src/typecheck.rs`. +# Contributing + +This section will cover how we can get started on contributing this project. + +## Setting up the repository + +To get a copy of this repository we can run: + +```bash +$ git clone https://github.com/Nadrieril/dhall-rust.git +``` + +But we also might note that it's better practice to fork the repository to your own workspace. +There you can make changes and submit pull requests against this repository. + +After the repositry has been cloned we need to update the [git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) +in the project, i.e. `dhall-lang`. We can do this by running: + +```bash +$ git submodule update --init --recursive +``` + +## Building and Testing + +A preferred method among the Rust community for developing is to use [`rustup`](https://rustup.rs/). + +It can be installed by running: + +```bash +$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` + +or if [nix](https://nixos.org/) is your tool of choice: + +```bash +$ nix-shell -p rustup +``` + +Once `rustup` is installed we can get it to manage our toolchain by running: + +```bash +$ rustup toolchain install nightly +``` + +Then we can manage our building and testing with the [`cargo`](https://crates.io/) dependency manager: + +```bash +$ cargo build +``` + +```bash +$ cargo test +``` + +Now we can have fun and happy contributing! + ## License Licensed under the terms of the 2-Clause BSD License ([LICENSE](LICENSE) or diff --git a/dhall/src/error/mod.rs b/dhall/src/error/mod.rs index 2345348..bc5322a 100644 --- a/dhall/src/error/mod.rs +++ b/dhall/src/error/mod.rs @@ -80,8 +80,8 @@ pub(crate) enum TypeMessage { MergeHandlerReturnTypeMustNotBeDependent, ProjectionMustBeRecord, ProjectionMissingEntry, - RecordMismatch(Typed, Typed), Sort, + RecordMismatch(Typed, Typed), RecordTypeDuplicateField, UnionTypeDuplicateField, Unimplemented, diff --git a/dhall/src/phase/normalize.rs b/dhall/src/phase/normalize.rs index e3c5d68..7d86833 100644 --- a/dhall/src/phase/normalize.rs +++ b/dhall/src/phase/normalize.rs @@ -382,13 +382,13 @@ enum Ret<'a> { /// * `fu` - Will convert the values of the second map /// into the target value. /// -/// * `ftu` - Will convert the key and values from both maps +/// * `fktu` - Will convert the key and values from both maps /// into the target type. /// /// # Description /// /// If the key is present in both maps then the final value for -/// that key is computed via the `ftu` function. Otherwise, the +/// that key is computed via the `fktu` function. Otherwise, the /// final value will be calculated by either the `ft` or `fu` value /// depending on which map the key is present in. /// @@ -397,35 +397,36 @@ enum Ret<'a> { pub(crate) fn outer_join<K, T, U, V>( mut ft: impl FnMut(&T) -> V, mut fu: impl FnMut(&U) -> V, - mut ftu: impl FnMut(&K, &T, &U) -> V, + mut fktu: impl FnMut(&K, &T, &U) -> V, map1: &HashMap<K, T>, map2: &HashMap<K, U>, ) -> HashMap<K, V> where K: std::hash::Hash + Eq + Clone, { - let mut kus = HashMap::new(); + let mut kvs = HashMap::new(); + for (k1, t) in map1 { let v = if let Some(u) = map2.get(k1) { // The key exists in both maps // so use all values for computation - ftu(k1, t, u) + fktu(k1, t, u) } else { // Key only exists in map1 ft(t) }; - kus.insert(k1.clone(), v); + kvs.insert(k1.clone(), v); } for (k1, u) in map2 { // Insert if key was missing in map1 - kus.entry(k1.clone()).or_insert(fu(u)); + kvs.entry(k1.clone()).or_insert(fu(u)); } - kus + kvs } -fn merge_maps<K, V>( +pub(crate) fn merge_maps<K, V>( map1: &HashMap<K, V>, map2: &HashMap<K, V>, mut f: impl FnMut(&V, &V) -> V, diff --git a/dhall/src/phase/typecheck.rs b/dhall/src/phase/typecheck.rs index c927ae2..9bb0e32 100644 --- a/dhall/src/phase/typecheck.rs +++ b/dhall/src/phase/typecheck.rs @@ -598,6 +598,47 @@ fn type_last_layer( } Ok(RetTypeOnly(text_type)) } + BinOp(RightBiasedRecordMerge, l, r) => { + use crate::phase::normalize::merge_maps; + + let l_type = l.get_type()?; + let l_kind = l_type.get_type()?; + let r_type = r.get_type()?; + let r_kind = r_type.get_type()?; + + // 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())), + ); + + // Extract the LHS record type + let kts_x = match l_type.to_value() { + Value::RecordType(kts) => kts, + _ => return Err(mkerr(MustCombineRecord(l.clone()))), + }; + + // Extract the RHS record type + let kts_y = match r_type.to_value() { + Value::RecordType(kts) => kts, + _ => return Err(mkerr(MustCombineRecord(r.clone()))), + }; + + // Union the two records, prefering + // the values found in the RHS. + let kts = merge_maps(&kts_x, &kts_y, |_, r_t| r_t.clone()); + + // Construct the final record type from the union + Ok(RetTypeOnly(tck_record_type( + ctx, + kts.iter() + .map(|(x, v)| Ok((x.clone(), v.to_type()))), + )? + .into_type())) + } BinOp(RecursiveRecordMerge, l, r) => { // A recursive function to dig down into // records of records when merging. @@ -687,6 +728,7 @@ fn type_last_layer( NaturalTimes => Natural, TextAppend => Text, ListAppend => unreachable!(), + RightBiasedRecordMerge => unreachable!(), RecursiveRecordMerge => unreachable!(), _ => return Err(mkerr(Unimplemented)), })?; @@ -1201,11 +1243,11 @@ mod spec_tests { // 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_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"); |