From 0169e9347ec7ffcf86066e1e5753ce5f77e71bc7 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 10 Mar 2020 16:34:59 +0000 Subject: Add support for `with` keyword --- README.md | 1 + dhall-lang | 2 +- dhall/build.rs | 15 +++++++- dhall/src/syntax/ast/span.rs | 1 + dhall/src/syntax/text/dhall.pest.visibility | 3 ++ dhall/src/syntax/text/parser.rs | 41 +++++++++++++++++++++- .../tests/parser/failure/spacing/MergeNoSpace2.txt | 2 +- .../parser/failure/spacing/RecordTypeNoSpace.txt | 2 +- .../failure/unit/WithInvalidOverrideA.txt | 1 + .../type-inference/failure/unit/WithUnderscore.txt | 7 ++++ 10 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 dhall/tests/type-inference/failure/unit/WithInvalidOverrideA.txt create mode 100644 dhall/tests/type-inference/failure/unit/WithUnderscore.txt diff --git a/README.md b/README.md index 1d77e95..072dd10 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,7 @@ same name as the corresponding test. [???] +- Add support for `with` keyword - Implement remote imports with conservative sanity checking - Implement `missing` and `env:VAR` imports - Implement `as Text` and `as Location` imports diff --git a/dhall-lang b/dhall-lang index 1a20024..527f391 160000 --- a/dhall-lang +++ b/dhall-lang @@ -1 +1 @@ -Subproject commit 1a20024421d4e0574e7d9c85a7b0e751b0534dc6 +Subproject commit 527f391fc8e48b3c8847b6982b76ac959808c0dd diff --git a/dhall/build.rs b/dhall/build.rs index 4b6e52b..3d062da 100644 --- a/dhall/build.rs +++ b/dhall/build.rs @@ -376,6 +376,19 @@ fn convert_abnf_to_pest() -> std::io::Result<()> { rules.remove("url_path"); writeln!(&mut file, "url_path = _{{ path }}")?; + // TODO: upstream this tweak + rules.remove("with_expression"); + writeln!( + &mut file, + r#" + with_expression = {{ application_expression ~ (whsp1 ~ with ~ whsp1 ~ with_clause)* }} + with_clause = {{ + any_label_or_some ~ (whsp ~ "." ~ whsp ~ any_label_or_some)* + ~ whsp ~ "=" ~ whsp ~ application_expression + }} + "# + )?; + // Work around some greediness issue in the grammar. rules.remove("missing"); writeln!( @@ -439,7 +452,7 @@ fn convert_abnf_to_pest() -> std::io::Result<()> { bool_or | import_alt }} - operator_expression = {{ application_expression ~ (whsp ~ operator ~ whsp ~ application_expression)* }} + operator_expression = {{ with_expression ~ (whsp ~ operator ~ whsp ~ with_expression)* }} "##)?; writeln!( diff --git a/dhall/src/syntax/ast/span.rs b/dhall/src/syntax/ast/span.rs index 7c004c5..50985e1 100644 --- a/dhall/src/syntax/ast/span.rs +++ b/dhall/src/syntax/ast/span.rs @@ -21,6 +21,7 @@ pub(crate) enum Span { /// Desugarings DuplicateRecordFieldsSugar, DottedFieldSugar, + WithSugar, /// For expressions obtained from decoding binary Decoded, /// For expressions constructed during normalization/typecheck diff --git a/dhall/src/syntax/text/dhall.pest.visibility b/dhall/src/syntax/text/dhall.pest.visibility index 03a000b..0ff1bfe 100644 --- a/dhall/src/syntax/text/dhall.pest.visibility +++ b/dhall/src/syntax/text/dhall.pest.visibility @@ -95,6 +95,7 @@ equivalent prefer lambda forall +# with arrow # complete # exponent @@ -164,6 +165,8 @@ times_expression equal_expression not_equal_expression equivalent_expression +with_expression +with_clause application_expression first_application_expression # import_expression diff --git a/dhall/src/syntax/text/parser.rs b/dhall/src/syntax/text/parser.rs index 7140332..f59cf06 100644 --- a/dhall/src/syntax/text/parser.rs +++ b/dhall/src/syntax/text/parser.rs @@ -2,6 +2,7 @@ use itertools::Itertools; use pest::prec_climber as pcl; use pest::prec_climber::PrecClimber; use std::collections::BTreeMap; +use std::iter::once; use std::rc::Rc; use pest_consume::{match_nodes, Parser}; @@ -144,6 +145,23 @@ fn insert_recordlit_entry(map: &mut BTreeMap, l: Label, e: Expr) { } } +fn desugar_with_expr(x: Expr, labels: &[Label], y: Expr) -> Expr { + use crate::syntax::BinOp::RightBiasedRecordMerge; + let expr = |k| Expr::new(k, Span::WithSugar); + match labels { + [] => y, + [l, rest @ ..] => { + let res = + desugar_with_expr(expr(Field(x.clone(), l.clone())), rest, y); + expr(BinOp( + RightBiasedRecordMerge, + x, + expr(RecordLit(once((l.clone(), res)).collect())), + )) + } + } +} + lazy_static::lazy_static! { static ref PRECCLIMBER: PrecClimber = { use Rule::*; @@ -759,6 +777,27 @@ impl DhallParser { Ok(()) } + #[alias(expression, shortcut = true)] + fn with_expression(input: ParseInput) -> ParseResult { + Ok(match_nodes!(input.children(); + [expression(e)] => e, + [expression(first), with_clause(clauses)..] => { + clauses.fold( + first, + |acc, (labels, e)| { + desugar_with_expr(acc, &labels, e) + } + ) + }, + )) + } + + fn with_clause(input: ParseInput) -> ParseResult<(Vec