summaryrefslogtreecommitdiff
path: root/dhall/src
diff options
context:
space:
mode:
authorNadrieril2020-02-20 18:52:34 +0000
committerNadrieril2020-02-20 18:54:11 +0000
commit86508a3ad59a0bfc1e24448d0a0126b1a1c645e1 (patch)
tree05db6aab6499ba2a3d91e0253571ee3cf5fefc8f /dhall/src
parent26ed5a1d4d43573ac8ad2f8de9e4dd411650aa9a (diff)
Add support for duplicate record fields
Diffstat (limited to 'dhall/src')
-rw-r--r--dhall/src/syntax/ast/expr.rs4
-rw-r--r--dhall/src/syntax/ast/span.rs2
-rw-r--r--dhall/src/syntax/binary/encode.rs13
-rw-r--r--dhall/src/syntax/text/parser.rs48
4 files changed, 46 insertions, 21 deletions
diff --git a/dhall/src/syntax/ast/expr.rs b/dhall/src/syntax/ast/expr.rs
index 089c178..ce0a3d2 100644
--- a/dhall/src/syntax/ast/expr.rs
+++ b/dhall/src/syntax/ast/expr.rs
@@ -1,3 +1,5 @@
+use std::collections::BTreeMap;
+
use crate::semantics::Universe;
use crate::syntax::map::{DupTreeMap, DupTreeSet};
use crate::syntax::visitor;
@@ -165,7 +167,7 @@ pub enum ExprKind<SubExpr> {
/// `{ k1 : t1, k2 : t1 }`
RecordType(DupTreeMap<Label, SubExpr>),
/// `{ k1 = v1, k2 = v2 }`
- RecordLit(DupTreeMap<Label, SubExpr>),
+ RecordLit(BTreeMap<Label, SubExpr>),
/// `< k1 : t1, k2 >`
UnionType(DupTreeMap<Label, Option<SubExpr>>),
/// `merge x y : t`
diff --git a/dhall/src/syntax/ast/span.rs b/dhall/src/syntax/ast/span.rs
index e75ea53..6d017f6 100644
--- a/dhall/src/syntax/ast/span.rs
+++ b/dhall/src/syntax/ast/span.rs
@@ -18,6 +18,8 @@ pub(crate) struct ParsedSpan {
pub(crate) enum Span {
/// A location in the source text
Parsed(ParsedSpan),
+ /// Desugaring of duplicate fields
+ DuplicateRecordFieldsSugar,
/// For expressions obtained from decoding binary
Decoded,
/// For expressions constructed during normalization/typecheck
diff --git a/dhall/src/syntax/binary/encode.rs b/dhall/src/syntax/binary/encode.rs
index 291ac4a..d2aa240 100644
--- a/dhall/src/syntax/binary/encode.rs
+++ b/dhall/src/syntax/binary/encode.rs
@@ -1,4 +1,5 @@
use serde_cbor::value::value as cbor;
+use std::collections::BTreeMap;
use std::vec;
use crate::error::EncodeError;
@@ -17,7 +18,8 @@ pub(crate) fn encode(expr: &Expr) -> Result<Vec<u8>, EncodeError> {
enum Serialize<'a> {
Expr(&'a Expr),
CBOR(cbor::Value),
- RecordMap(&'a DupTreeMap<Label, Expr>),
+ RecordMap(&'a BTreeMap<Label, Expr>),
+ RecordDupMap(&'a DupTreeMap<Label, Expr>),
UnionMap(&'a DupTreeMap<Label, Option<Expr>>),
}
@@ -48,7 +50,7 @@ where
use syntax::ExprKind::*;
use syntax::LitKind::*;
- use self::Serialize::{RecordMap, UnionMap};
+ use self::Serialize::{RecordDupMap, RecordMap, UnionMap};
fn expr(x: &Expr) -> self::Serialize<'_> {
self::Serialize::Expr(x)
}
@@ -127,7 +129,7 @@ where
Text(x) => cbor(String(x.clone())),
})))
}
- RecordType(map) => ser_seq!(ser; tag(7), RecordMap(map)),
+ RecordType(map) => ser_seq!(ser; tag(7), RecordDupMap(map)),
RecordLit(map) => ser_seq!(ser; tag(8), RecordMap(map)),
UnionType(map) => ser_seq!(ser; tag(11), UnionMap(map)),
Field(x, l) => ser_seq!(ser; tag(9), expr(x), label(l)),
@@ -258,6 +260,11 @@ impl<'a> serde::ser::Serialize for Serialize<'a> {
match self {
Serialize::Expr(e) => serialize_subexpr(ser, e),
Serialize::CBOR(v) => v.serialize(ser),
+ Serialize::RecordDupMap(map) => {
+ ser.collect_map(map.iter().map(|(k, v)| {
+ (cbor::Value::String(k.into()), Serialize::Expr(v))
+ }))
+ }
Serialize::RecordMap(map) => {
ser.collect_map(map.iter().map(|(k, v)| {
(cbor::Value::String(k.into()), Serialize::Expr(v))
diff --git a/dhall/src/syntax/text/parser.rs b/dhall/src/syntax/text/parser.rs
index f3ebd2b..f74794b 100644
--- a/dhall/src/syntax/text/parser.rs
+++ b/dhall/src/syntax/text/parser.rs
@@ -1,6 +1,7 @@
use itertools::Itertools;
use pest::prec_climber as pcl;
use pest::prec_climber::PrecClimber;
+use std::collections::BTreeMap;
use std::rc::Rc;
use pest_consume::{match_nodes, Parser};
@@ -124,6 +125,25 @@ fn trim_indent(lines: &mut Vec<ParsedText>) {
}
}
+/// Insert the expr into the map; in case of collision, create a RecursiveRecordMerge node.
+fn insert_recordlit_entry(map: &mut BTreeMap<Label, Expr>, l: Label, e: Expr) {
+ use crate::syntax::BinOp::RecursiveRecordMerge;
+ use std::collections::btree_map::Entry;
+ match map.entry(l) {
+ Entry::Vacant(entry) => {
+ entry.insert(e);
+ }
+ Entry::Occupied(mut entry) => {
+ let dummy = Expr::new(Lit(Bool(false)), Span::Artificial);
+ let other = entry.insert(dummy);
+ entry.insert(Expr::new(
+ BinOp(RecursiveRecordMerge, other, e),
+ Span::DuplicateRecordFieldsSugar,
+ ));
+ }
+ }
+}
+
lazy_static::lazy_static! {
static ref PRECCLIMBER: PrecClimber<Rule> = {
use Rule::*;
@@ -860,26 +880,16 @@ impl DhallParser {
input: ParseInput,
) -> ParseResult<UnspannedExpr> {
Ok(match_nodes!(input.children();
- [label(first_label), non_empty_record_type(rest)] => {
- let (first_expr, mut map) = rest;
- map.insert(first_label, first_expr);
- RecordType(map)
- },
- [label(first_label), non_empty_record_literal(rest)] => {
- let (first_expr, mut map) = rest;
- map.insert(first_label, first_expr);
- RecordLit(map)
- },
+ [non_empty_record_type(map)] => RecordType(map),
+ [non_empty_record_literal(map)] => RecordLit(map),
))
}
fn non_empty_record_type(
input: ParseInput,
- ) -> ParseResult<(Expr, DupTreeMap<Label, Expr>)> {
+ ) -> ParseResult<DupTreeMap<Label, Expr>> {
Ok(match_nodes!(input.into_children();
- [expression(expr), record_type_entry(entries)..] => {
- (expr, entries.collect())
- }
+ [record_type_entry(entries)..] => entries.collect()
))
}
@@ -891,10 +901,14 @@ impl DhallParser {
fn non_empty_record_literal(
input: ParseInput,
- ) -> ParseResult<(Expr, DupTreeMap<Label, Expr>)> {
+ ) -> ParseResult<BTreeMap<Label, Expr>> {
Ok(match_nodes!(input.into_children();
- [expression(expr), record_literal_entry(entries)..] => {
- (expr, entries.collect())
+ [record_literal_entry(entries)..] => {
+ let mut map = BTreeMap::new();
+ for (l, e) in entries {
+ insert_recordlit_entry(&mut map, l, e);
+ }
+ map
}
))
}