summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dhall/src/normalize.rs41
-rw-r--r--dhall/tests/normalization.rs12
-rw-r--r--dhall_core/src/core.rs15
-rw-r--r--dhall_core/src/parser.rs21
-rw-r--r--dhall_core/src/printer.rs5
5 files changed, 74 insertions, 20 deletions
diff --git a/dhall/src/normalize.rs b/dhall/src/normalize.rs
index 4652b25..2ffa5a5 100644
--- a/dhall/src/normalize.rs
+++ b/dhall/src/normalize.rs
@@ -276,14 +276,45 @@ where
(o, _, _) => BinOp(*o, x, y),
})
}
- Field(e, x) => {
+ Merge(x, y, t) => {
+ let x = normalize_whnf(x);
+ let y = normalize_whnf(y);
+ match (x.as_ref(), y.as_ref()) {
+ (RecordLit(handlers), UnionLit(k, v, _)) => {
+ match handlers.get(&k) {
+ Some(h) => {
+ normalize_whnf(&rc(App(h.clone(), vec![v.clone()])))
+ }
+ None => rc(Merge(x, y, t.clone())),
+ }
+ }
+ _ => rc(Merge(x, y, t.clone())),
+ }
+ }
+ Field(e, l) => {
let e = normalize_whnf(e);
- match (e.as_ref(), x) {
- (RecordLit(kvs), x) => match kvs.get(&x) {
+ match e.as_ref() {
+ RecordLit(kvs) => match kvs.get(&l) {
Some(r) => normalize_whnf(r),
- None => rc(Field(e, x.clone())),
+ None => rc(Field(e, l.clone())),
},
- (_, x) => rc(Field(e, x.clone())),
+ _ => rc(Field(e, l.clone())),
+ }
+ }
+ Projection(_, ls) if ls.is_empty() => {
+ rc(RecordLit(std::collections::BTreeMap::new()))
+ }
+ Projection(e, ls) => {
+ let e = normalize_whnf(e);
+ match e.as_ref() {
+ RecordLit(kvs) => rc(RecordLit(
+ ls.iter()
+ .filter_map(|l| {
+ kvs.get(l).map(|x| (l.clone(), x.clone()))
+ })
+ .collect(),
+ )),
+ _ => rc(Projection(e, ls.clone())),
}
}
_ => Rc::clone(e),
diff --git a/dhall/tests/normalization.rs b/dhall/tests/normalization.rs
index 9ecad74..5df46a6 100644
--- a/dhall/tests/normalization.rs
+++ b/dhall/tests/normalization.rs
@@ -13,7 +13,7 @@ norm!(spec_normalization_success_haskell_tutorial_access_0, "haskell-tutorial/ac
// norm!(spec_normalization_success_haskell_tutorial_combineTypes_0, "haskell-tutorial/combineTypes/0");
// norm!(spec_normalization_success_haskell_tutorial_combineTypes_1, "haskell-tutorial/combineTypes/1");
// norm!(spec_normalization_success_haskell_tutorial_prefer_0, "haskell-tutorial/prefer/0");
-// norm!(spec_normalization_success_haskell_tutorial_projection_0, "haskell-tutorial/projection/0");
+norm!(spec_normalization_success_haskell_tutorial_projection_0, "haskell-tutorial/projection/0");
// norm!(spec_normalization_success_multiline_escape, "multiline/escape");
// norm!(spec_normalization_success_multiline_hangingIndent, "multiline/hangingIndent");
// norm!(spec_normalization_success_multiline_interesting, "multiline/interesting");
@@ -228,9 +228,9 @@ norm!(spec_normalization_success_unit_ListNormalizeTypeAnnotation, "unit/ListNor
norm!(spec_normalization_success_unit_ListReverse, "unit/ListReverse");
norm!(spec_normalization_success_unit_ListReverseEmpty, "unit/ListReverseEmpty");
norm!(spec_normalization_success_unit_ListReverseTwo, "unit/ListReverseTwo");
-// norm!(spec_normalization_success_unit_Merge, "unit/Merge");
+norm!(spec_normalization_success_unit_Merge, "unit/Merge");
norm!(spec_normalization_success_unit_MergeNormalizeArguments, "unit/MergeNormalizeArguments");
-// norm!(spec_normalization_success_unit_MergeWithType, "unit/MergeWithType");
+norm!(spec_normalization_success_unit_MergeWithType, "unit/MergeWithType");
norm!(spec_normalization_success_unit_MergeWithTypeNormalizeArguments, "unit/MergeWithTypeNormalizeArguments");
norm!(spec_normalization_success_unit_Natural, "unit/Natural");
norm!(spec_normalization_success_unit_NaturalBuild, "unit/NaturalBuild");
@@ -302,9 +302,9 @@ norm!(spec_normalization_success_unit_OptionalFoldNone, "unit/OptionalFoldNone")
norm!(spec_normalization_success_unit_OptionalFoldSome, "unit/OptionalFoldSome");
norm!(spec_normalization_success_unit_Record, "unit/Record");
norm!(spec_normalization_success_unit_RecordEmpty, "unit/RecordEmpty");
-// norm!(spec_normalization_success_unit_RecordProjection, "unit/RecordProjection");
-// norm!(spec_normalization_success_unit_RecordProjectionEmpty, "unit/RecordProjectionEmpty");
-// norm!(spec_normalization_success_unit_RecordProjectionNormalizeArguments, "unit/RecordProjectionNormalizeArguments");
+norm!(spec_normalization_success_unit_RecordProjection, "unit/RecordProjection");
+norm!(spec_normalization_success_unit_RecordProjectionEmpty, "unit/RecordProjectionEmpty");
+norm!(spec_normalization_success_unit_RecordProjectionNormalizeArguments, "unit/RecordProjectionNormalizeArguments");
norm!(spec_normalization_success_unit_RecordSelection, "unit/RecordSelection");
norm!(spec_normalization_success_unit_RecordSelectionNormalizeArguments, "unit/RecordSelectionNormalizeArguments");
norm!(spec_normalization_success_unit_RecordType, "unit/RecordType");
diff --git a/dhall_core/src/core.rs b/dhall_core/src/core.rs
index bc5a666..502a9bc 100644
--- a/dhall_core/src/core.rs
+++ b/dhall_core/src/core.rs
@@ -241,8 +241,10 @@ pub enum Expr<Note, Embed> {
SubExpr<Note, Embed>,
Option<SubExpr<Note, Embed>>,
),
- /// `Field e x ~ e.x`
+ /// e.x
Field(SubExpr<Note, Embed>, Label),
+ /// e.{ x, y, z }
+ Projection(SubExpr<Note, Embed>, Vec<Label>),
/// Annotation on the AST. Unused for now but could hold e.g. file location information
Note(Note, SubExpr<Note, Embed>),
/// Embeds an import or the result of resolving the import
@@ -353,9 +355,9 @@ where
};
match e {
Const(k) => Const(*k),
- Var(V(x, n)) => Var(V(map_label(x), *n)),
- Lam(x, t, b) => Lam(map_label(x), map(t), map_under_binder(x, b)),
- Pi(x, t, b) => Pi(map_label(x), map(t), map_under_binder(x, b)),
+ Var(V(l, n)) => Var(V(map_label(l), *n)),
+ Lam(l, t, b) => Lam(map_label(l), map(t), map_under_binder(l, b)),
+ Pi(l, t, b) => Pi(map_label(l), map(t), map_under_binder(l, b)),
App(f, args) => App(map(f), vec(args)),
Let(l, t, a, b) => {
Let(map_label(l), opt(t), map(a), map_under_binder(l, b))
@@ -378,7 +380,10 @@ where
UnionType(kts) => UnionType(btmap(kts)),
UnionLit(k, v, kvs) => UnionLit(map_label(k), map(v), btmap(kvs)),
Merge(x, y, t) => Merge(map(x), map(y), opt(t)),
- Field(r, x) => Field(map(r), map_label(x)),
+ Field(e, l) => Field(map(e), map_label(l)),
+ Projection(e, ls) => {
+ Projection(map(e), ls.iter().map(&map_label).collect())
+ }
Note(n, e) => Note(map_note(n), map(e)),
Embed(a) => Embed(map_embed(a)),
}
diff --git a/dhall_core/src/parser.rs b/dhall_core/src/parser.rs
index f959d2e..a62f861 100644
--- a/dhall_core/src/parser.rs
+++ b/dhall_core/src/parser.rs
@@ -19,6 +19,12 @@ pub type ParseError = pest::error::Error<Rule>;
pub type ParseResult<T> = Result<T, ParseError>;
+#[derive(Debug)]
+enum Either<A, B> {
+ Left(A),
+ Right(B),
+}
+
impl Builtin {
pub fn parse(s: &str) -> Option<Self> {
use self::Builtin::*;
@@ -649,13 +655,20 @@ make_parser! {
rule!(selector_expression<ParsedExpr> as expression; children!(
[expression(e)] => e,
[expression(first), selector(rest..)] => {
- rest.fold(first, |acc, e| bx(Expr::Field(acc, e)))
+ rest.fold(first, |acc, e| match e {
+ Either::Left(l) => bx(Expr::Field(acc, l)),
+ Either::Right(ls) => bx(Expr::Projection(acc, ls)),
+ })
}
));
- // TODO: handle record projection
- rule!(selector<Label>; children!(
- [label(l)] => l
+ rule!(selector<Either<Label, Vec<Label>>>; children!(
+ [label(l)] => Either::Left(l),
+ [labels(ls)] => Either::Right(ls),
+ ));
+
+ rule!(labels<Vec<Label>>; children!(
+ [label(ls..)] => ls.collect(),
));
rule!(literal_expression<ParsedExpr> as expression; children!(
diff --git a/dhall_core/src/printer.rs b/dhall_core/src/printer.rs
index 5ecf5ce..d93336e 100644
--- a/dhall_core/src/printer.rs
+++ b/dhall_core/src/printer.rs
@@ -165,6 +165,11 @@ impl<S, A: Display> Expr<S, A> {
a.fmt_e(f)?;
write!(f, ".{}", b)
}
+ &Projection(ref e, ref ls) => {
+ e.fmt_e(f)?;
+ write!(f, ".")?;
+ fmt_list("{ ", ", ", " }", ls, f, |l, f| write!(f, "{}", l))
+ }
&Note(_, ref b) => b.fmt_e(f),
a => a.fmt_f(f),
}