summaryrefslogtreecommitdiff
path: root/dhall/src/syntax/text
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dhall/src/syntax/text/parser.rs148
-rw-r--r--dhall/src/syntax/text/printer.rs192
2 files changed, 165 insertions, 175 deletions
diff --git a/dhall/src/syntax/text/parser.rs b/dhall/src/syntax/text/parser.rs
index 6f5949f..dcaf5e4 100644
--- a/dhall/src/syntax/text/parser.rs
+++ b/dhall/src/syntax/text/parser.rs
@@ -1,13 +1,13 @@
use itertools::Itertools;
use pest::prec_climber as pcl;
use pest::prec_climber::PrecClimber;
-use std::collections::BTreeMap;
+use std::collections::{BTreeMap, BTreeSet};
use std::iter::once;
use std::rc::Rc;
use pest_consume::{match_nodes, Parser};
-use crate::syntax::map::{DupTreeMap, DupTreeSet};
+use crate::operations::OpKind::*;
use crate::syntax::ExprKind::*;
use crate::syntax::NumKind::*;
use crate::syntax::{
@@ -31,50 +31,10 @@ pub type ParseResult<T> = Result<T, ParseError>;
#[derive(Debug)]
enum Selector {
Field(Label),
- Projection(DupTreeSet<Label>),
+ Projection(BTreeSet<Label>),
ProjectionByExpr(Expr),
}
-impl crate::syntax::Builtin {
- pub fn parse(s: &str) -> Option<Self> {
- use crate::syntax::Builtin::*;
- match s {
- "Bool" => Some(Bool),
- "Natural" => Some(Natural),
- "Integer" => Some(Integer),
- "Double" => Some(Double),
- "Text" => Some(Text),
- "List" => Some(List),
- "Optional" => Some(Optional),
- "None" => Some(OptionalNone),
- "Natural/build" => Some(NaturalBuild),
- "Natural/fold" => Some(NaturalFold),
- "Natural/isZero" => Some(NaturalIsZero),
- "Natural/even" => Some(NaturalEven),
- "Natural/odd" => Some(NaturalOdd),
- "Natural/toInteger" => Some(NaturalToInteger),
- "Natural/show" => Some(NaturalShow),
- "Natural/subtract" => Some(NaturalSubtract),
- "Integer/toDouble" => Some(IntegerToDouble),
- "Integer/show" => Some(IntegerShow),
- "Integer/negate" => Some(IntegerNegate),
- "Integer/clamp" => Some(IntegerClamp),
- "Double/show" => Some(DoubleShow),
- "List/build" => Some(ListBuild),
- "List/fold" => Some(ListFold),
- "List/length" => Some(ListLength),
- "List/head" => Some(ListHead),
- "List/last" => Some(ListLast),
- "List/indexed" => Some(ListIndexed),
- "List/reverse" => Some(ListReverse),
- "Optional/fold" => Some(OptionalFold),
- "Optional/build" => Some(OptionalBuild),
- "Text/show" => Some(TextShow),
- _ => None,
- }
- }
-}
-
fn input_to_span(input: ParseInput) -> Span {
Span::make(input.user_data().clone(), input.as_pair().as_span())
}
@@ -128,7 +88,7 @@ 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 crate::operations::BinOp::RecursiveRecordMerge;
use std::collections::btree_map::Entry;
match map.entry(l) {
Entry::Vacant(entry) => {
@@ -138,7 +98,7 @@ fn insert_recordlit_entry(map: &mut BTreeMap<Label, Expr>, l: Label, e: Expr) {
let dummy = Expr::new(Num(Bool(false)), Span::Artificial);
let other = entry.insert(dummy);
entry.insert(Expr::new(
- BinOp(RecursiveRecordMerge, other, e),
+ Op(BinOp(RecursiveRecordMerge, other, e)),
Span::DuplicateRecordFieldsSugar,
));
}
@@ -146,18 +106,21 @@ fn insert_recordlit_entry(map: &mut BTreeMap<Label, Expr>, l: Label, e: Expr) {
}
fn desugar_with_expr(x: Expr, labels: &[Label], y: Expr) -> Expr {
- use crate::syntax::BinOp::RightBiasedRecordMerge;
+ use crate::operations::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(
+ let res = desugar_with_expr(
+ expr(Op(Field(x.clone(), l.clone()))),
+ rest,
+ y,
+ );
+ expr(Op(BinOp(
RightBiasedRecordMerge,
x,
expr(RecordLit(once((l.clone(), res)).collect())),
- ))
+ )))
}
}
}
@@ -387,7 +350,7 @@ impl DhallParser {
#[alias(expression)]
fn builtin(input: ParseInput) -> ParseResult<Expr> {
let s = input.as_str();
- let e = match crate::syntax::Builtin::parse(s) {
+ let e = match crate::builtins::Builtin::parse(s) {
Some(b) => Builtin(b),
None => match s {
"True" => Num(Bool(true)),
@@ -725,7 +688,7 @@ impl DhallParser {
},
[if_(()), expression(cond), expression(left),
expression(right)] => {
- spanned(input, BoolIf(cond, left, right))
+ spanned(input, Op(BoolIf(cond, left, right)))
},
[let_binding(bindings).., expression(final_expr)] => {
bindings.rev().fold(
@@ -747,13 +710,13 @@ impl DhallParser {
spanned(input, Pi("_".into(), typ, body))
},
[merge(()), expression(x), expression(y), expression(z)] => {
- spanned(input, Merge(x, y, Some(z)))
+ spanned(input, Op(Merge(x, y, Some(z))))
},
[assert(()), expression(x)] => {
spanned(input, Assert(x))
},
[toMap(()), expression(x), expression(y)] => {
- spanned(input, ToMap(x, Some(y)))
+ spanned(input, Op(ToMap(x, Some(y))))
},
[expression(e), expression(annot)] => {
spanned(input, Annot(e, annot))
@@ -780,7 +743,7 @@ impl DhallParser {
op: ParseInput,
r: Expr,
) -> ParseResult<Expr> {
- use crate::syntax::BinOp::*;
+ use crate::operations::BinOp::*;
use Rule::*;
let op = match op.as_rule() {
import_alt => ImportAlt,
@@ -801,7 +764,7 @@ impl DhallParser {
}
};
- Ok(spanned_union(l.span(), r.span(), BinOp(op, l, r)))
+ Ok(spanned_union(l.span(), r.span(), Op(BinOp(op, l, r))))
}
fn Some_(_input: ParseInput) -> ParseResult<()> {
@@ -840,7 +803,7 @@ impl DhallParser {
spanned_union(
acc.span(),
e.span(),
- App(acc, e)
+ Op(App(acc, e))
)
}
)
@@ -855,10 +818,10 @@ impl DhallParser {
spanned(input, SomeLit(e))
},
[merge(()), expression(x), expression(y)] => {
- spanned(input, Merge(x, y, None))
+ spanned(input, Op(Merge(x, y, None)))
},
[toMap(()), expression(x)] => {
- spanned(input, ToMap(x, None))
+ spanned(input, Op(ToMap(x, None)))
},
[expression(e)] => e,
))
@@ -875,7 +838,7 @@ impl DhallParser {
spanned_union(
acc.span(),
e.span(),
- Completion(acc, e),
+ Op(Completion(acc, e)),
)
}
)
@@ -895,9 +858,9 @@ impl DhallParser {
acc.span(),
e.1,
match e.0 {
- Selector::Field(l) => Field(acc, l),
- Selector::Projection(ls) => Projection(acc, ls),
- Selector::ProjectionByExpr(e) => ProjectionByExpr(acc, e)
+ Selector::Field(l) => Op(Field(acc, l)),
+ Selector::Projection(ls) => Op(Projection(acc, ls)),
+ Selector::ProjectionByExpr(e) => Op(ProjectionByExpr(acc, e))
}
)
}
@@ -915,9 +878,20 @@ impl DhallParser {
Ok((stor, input_to_span(input)))
}
- fn labels(input: ParseInput) -> ParseResult<DupTreeSet<Label>> {
- Ok(match_nodes!(input.into_children();
- [label(ls)..] => ls.collect(),
+ fn labels(input: ParseInput) -> ParseResult<BTreeSet<Label>> {
+ Ok(match_nodes!(input.children();
+ [label(ls)..] => {
+ let mut set = BTreeSet::default();
+ for l in ls {
+ if set.contains(&l) {
+ return Err(
+ input.error(format!("Duplicate field in projection"))
+ )
+ }
+ set.insert(l);
+ }
+ set
+ },
))
}
@@ -957,9 +931,26 @@ impl DhallParser {
fn non_empty_record_type(
input: ParseInput,
- ) -> ParseResult<DupTreeMap<Label, Expr>> {
- Ok(match_nodes!(input.into_children();
- [record_type_entry(entries)..] => entries.collect()
+ ) -> ParseResult<BTreeMap<Label, Expr>> {
+ Ok(match_nodes!(input.children();
+ [record_type_entry(entries)..] => {
+ let mut map = BTreeMap::default();
+ for (l, t) in entries {
+ use std::collections::btree_map::Entry;
+ match map.entry(l) {
+ Entry::Occupied(_) => {
+ return Err(input.error(
+ "Duplicate field in record type"
+ .to_string(),
+ ));
+ }
+ Entry::Vacant(e) => {
+ e.insert(t);
+ }
+ }
+ }
+ map
+ },
))
}
@@ -1008,7 +999,24 @@ impl DhallParser {
fn union_type(input: ParseInput) -> ParseResult<UnspannedExpr> {
let map = match_nodes!(input.children();
[empty_union_type(_)] => Default::default(),
- [union_type_entry(entries)..] => entries.collect(),
+ [union_type_entry(entries)..] => {
+ let mut map = BTreeMap::default();
+ for (l, t) in entries {
+ use std::collections::btree_map::Entry;
+ match map.entry(l) {
+ Entry::Occupied(_) => {
+ return Err(input.error(
+ "Duplicate variant in union type"
+ .to_string(),
+ ));
+ }
+ Entry::Vacant(e) => {
+ e.insert(t);
+ }
+ }
+ }
+ map
+ },
);
Ok(UnionType(map))
}
diff --git a/dhall/src/syntax/text/printer.rs b/dhall/src/syntax/text/printer.rs
index 378f408..9e90660 100644
--- a/dhall/src/syntax/text/printer.rs
+++ b/dhall/src/syntax/text/printer.rs
@@ -1,3 +1,5 @@
+use crate::builtins::Builtin;
+use crate::operations::{BinOp, OpKind};
use crate::syntax::*;
use itertools::Itertools;
use std::fmt::{self, Display};
@@ -5,15 +7,16 @@ use std::fmt::{self, Display};
// There is a one-to-one correspondence between the formatter and the grammar. Each phase is
// named after a corresponding grammar group, and the structure of the formatter reflects
// the relationship between the corresponding grammar rules. This leads to the nice property
-// of automatically getting all the parentheses and precedences right.
+// of automatically getting all the parentheses and precedences right (in a manner dual do Pratt
+// parsing).
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
enum PrintPhase {
// `expression`
Base,
// `operator-expression`
Operator,
- // All the operator `*-expression`s
- BinOp(ast::BinOp),
+ // All the `<operator>-expression`s
+ BinOp(self::BinOp),
// `application-expression`
App,
// `import-expression`
@@ -36,7 +39,8 @@ impl<'a> PhasedExpr<'a> {
impl UnspannedExpr {
// Annotate subexpressions with the appropriate phase, defaulting to Base
fn annotate_with_phases(&self) -> ExprKind<PhasedExpr<'_>> {
- use crate::syntax::ExprKind::*;
+ use ExprKind::*;
+ use OpKind::*;
use PrintPhase::*;
let with_base = self.map_ref(|e| PhasedExpr(e, Base));
match with_base {
@@ -47,31 +51,33 @@ impl UnspannedExpr {
Pi(a, b, c)
}
}
- Merge(a, b, c) => Merge(
+ Op(Merge(a, b, c)) => Op(Merge(
a.phase(PrintPhase::Import),
b.phase(PrintPhase::Import),
c.map(|x| x.phase(PrintPhase::App)),
- ),
- ToMap(a, b) => ToMap(
+ )),
+ Op(ToMap(a, b)) => Op(ToMap(
a.phase(PrintPhase::Import),
b.map(|x| x.phase(PrintPhase::App)),
- ),
+ )),
Annot(a, b) => Annot(a.phase(Operator), b),
- ExprKind::BinOp(op, a, b) => ExprKind::BinOp(
+ Op(OpKind::BinOp(op, a, b)) => Op(OpKind::BinOp(
op,
a.phase(PrintPhase::BinOp(op)),
b.phase(PrintPhase::BinOp(op)),
- ),
+ )),
SomeLit(e) => SomeLit(e.phase(PrintPhase::Import)),
- ExprKind::App(f, a) => ExprKind::App(
+ Op(OpKind::App(f, a)) => Op(OpKind::App(
f.phase(PrintPhase::App),
a.phase(PrintPhase::Import),
- ),
- Field(a, b) => Field(a.phase(Primitive), b),
- Projection(e, ls) => Projection(e.phase(Primitive), ls),
- ProjectionByExpr(a, b) => ProjectionByExpr(a.phase(Primitive), b),
- Completion(a, b) => {
- Completion(a.phase(Primitive), b.phase(Primitive))
+ )),
+ Op(Field(a, b)) => Op(Field(a.phase(Primitive), b)),
+ Op(Projection(e, ls)) => Op(Projection(e.phase(Primitive), ls)),
+ Op(ProjectionByExpr(a, b)) => {
+ Op(ProjectionByExpr(a.phase(Primitive), b))
+ }
+ Op(Completion(a, b)) => {
+ Op(Completion(a.phase(Primitive), b.phase(Primitive)))
}
ExprKind::Import(a) => {
ExprKind::Import(a.map_ref(|x| x.phase(PrintPhase::Import)))
@@ -85,22 +91,24 @@ impl UnspannedExpr {
f: &mut fmt::Formatter,
phase: PrintPhase,
) -> Result<(), fmt::Error> {
- use crate::syntax::ExprKind::*;
+ use ExprKind::*;
+ use OpKind::*;
let needs_paren = match self {
Lam(_, _, _)
- | BoolIf(_, _, _)
| Pi(_, _, _)
| Let(_, _, _, _)
- | EmptyListLit(_)
| SomeLit(_)
- | Merge(_, _, _)
- | ToMap(_, _)
+ | EmptyListLit(_)
+ | Op(BoolIf(_, _, _))
+ | Op(Merge(_, _, _))
+ | Op(ToMap(_, _))
| Annot(_, _) => phase > PrintPhase::Base,
- // Precedence is magically handled by the ordering of BinOps.
- ExprKind::BinOp(op, _, _) => phase > PrintPhase::BinOp(*op),
- ExprKind::App(_, _) => phase > PrintPhase::App,
- Completion(_, _) => phase > PrintPhase::Import,
+ // Precedence is magically handled by the ordering of BinOps. This is reverse Pratt
+ // parsing.
+ Op(BinOp(op, _, _)) => phase > PrintPhase::BinOp(*op),
+ Op(App(_, _)) => phase > PrintPhase::App,
+ Op(Completion(_, _)) => phase > PrintPhase::Import,
_ => false,
};
@@ -143,12 +151,10 @@ impl<SE: Display + Clone> Display for ExprKind<SE> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
use crate::syntax::ExprKind::*;
match self {
+ Var(a) => a.fmt(f)?,
Lam(a, b, c) => {
write!(f, "λ({} : {}) → {}", a, b, c)?;
}
- BoolIf(a, b, c) => {
- write!(f, "if {} then {} else {}", a, b, c)?;
- }
Pi(a, b, c) if &String::from(a) == "_" => {
write!(f, "{} → {}", b, c)?;
}
@@ -162,14 +168,62 @@ impl<SE: Display + Clone> Display for ExprKind<SE> {
}
write!(f, " = {} in {}", c, d)?;
}
+ Const(k) => k.fmt(f)?,
+ Builtin(v) => v.fmt(f)?,
+ Num(a) => a.fmt(f)?,
+ TextLit(a) => a.fmt(f)?,
+ SomeLit(e) => {
+ write!(f, "Some {}", e)?;
+ }
EmptyListLit(t) => {
write!(f, "[] : {}", t)?;
}
NEListLit(es) => {
fmt_list("[", ", ", "]", es, f, Display::fmt)?;
}
- SomeLit(e) => {
- write!(f, "Some {}", e)?;
+ RecordLit(a) if a.is_empty() => f.write_str("{=}")?,
+ RecordLit(a) => fmt_list("{ ", ", ", " }", a, f, |(k, v), f| {
+ write!(f, "{} = {}", k, v)
+ })?,
+ RecordType(a) if a.is_empty() => f.write_str("{}")?,
+ RecordType(a) => fmt_list("{ ", ", ", " }", a, f, |(k, t), f| {
+ write!(f, "{} : {}", k, t)
+ })?,
+ UnionType(a) => fmt_list("< ", " | ", " >", a, f, |(k, v), f| {
+ write!(f, "{}", k)?;
+ if let Some(v) = v {
+ write!(f, ": {}", v)?;
+ }
+ Ok(())
+ })?,
+ Op(op) => {
+ op.fmt(f)?;
+ }
+ Annot(a, b) => {
+ write!(f, "{} : {}", a, b)?;
+ }
+ Assert(a) => {
+ write!(f, "assert : {}", a)?;
+ }
+ Import(a) => a.fmt(f)?,
+ }
+ Ok(())
+ }
+}
+
+/// Generic instance that delegates to subexpressions
+impl<SE: Display + Clone> Display for OpKind<SE> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ use OpKind::*;
+ match self {
+ App(a, b) => {
+ write!(f, "{} {}", a, b)?;
+ }
+ BinOp(op, a, b) => {
+ write!(f, "{} {} {}", a, op, b)?;
+ }
+ BoolIf(a, b, c) => {
+ write!(f, "if {} then {} else {}", a, b, c)?;
}
Merge(a, b, c) => {
write!(f, "merge {} {}", a, b)?;
@@ -183,41 +237,9 @@ impl<SE: Display + Clone> Display for ExprKind<SE> {
write!(f, " : {}", b)?;
}
}
- Annot(a, b) => {
- write!(f, "{} : {}", a, b)?;
- }
- Assert(a) => {
- write!(f, "assert : {}", a)?;
- }
- ExprKind::BinOp(op, a, b) => {
- write!(f, "{} {} {}", a, op, b)?;
- }
- ExprKind::App(a, b) => {
- write!(f, "{} {}", a, b)?;
- }
Field(a, b) => {
write!(f, "{}.{}", a, b)?;
}
- Var(a) => a.fmt(f)?,
- Const(k) => k.fmt(f)?,
- Builtin(v) => v.fmt(f)?,
- Num(a) => a.fmt(f)?,
- TextLit(a) => a.fmt(f)?,
- RecordType(a) if a.is_empty() => f.write_str("{}")?,
- RecordType(a) => fmt_list("{ ", ", ", " }", a, f, |(k, t), f| {
- write!(f, "{} : {}", k, t)
- })?,
- RecordLit(a) if a.is_empty() => f.write_str("{=}")?,
- RecordLit(a) => fmt_list("{ ", ", ", " }", a, f, |(k, v), f| {
- write!(f, "{} = {}", k, v)
- })?,
- UnionType(a) => fmt_list("< ", " | ", " >", a, f, |(k, v), f| {
- write!(f, "{}", k)?;
- if let Some(v) = v {
- write!(f, ": {}", v)?;
- }
- Ok(())
- })?,
Projection(e, ls) => {
write!(f, "{}.", e)?;
fmt_list("{ ", ", ", " }", ls, f, Display::fmt)?;
@@ -228,7 +250,6 @@ impl<SE: Display + Clone> Display for ExprKind<SE> {
Completion(a, b) => {
write!(f, "{}::{}", a, b)?;
}
- Import(a) => a.fmt(f)?,
}
Ok(())
}
@@ -315,7 +336,7 @@ impl Display for Const {
impl Display for BinOp {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- use crate::syntax::BinOp::*;
+ use BinOp::*;
f.write_str(match self {
BoolOr => "||",
TextAppend => "++",
@@ -363,7 +384,7 @@ impl Display for Label {
let is_reserved = match s.as_str() {
"let" | "in" | "if" | "then" | "else" | "Type" | "Kind"
| "Sort" | "True" | "False" | "Some" => true,
- _ => crate::syntax::Builtin::parse(&s).is_some(),
+ _ => Builtin::parse(&s).is_some(),
};
if !is_reserved && s.chars().all(|c| c.is_ascii_alphanumeric()) {
write!(f, "{}", s)
@@ -461,45 +482,6 @@ impl<SubExpr: Display> Display for Import<SubExpr> {
}
}
-impl Display for Builtin {
- fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- use crate::syntax::Builtin::*;
- f.write_str(match *self {
- Bool => "Bool",
- Natural => "Natural",
- Integer => "Integer",
- Double => "Double",
- Text => "Text",
- List => "List",
- Optional => "Optional",
- OptionalNone => "None",
- NaturalBuild => "Natural/build",
- NaturalFold => "Natural/fold",
- NaturalIsZero => "Natural/isZero",
- NaturalEven => "Natural/even",
- NaturalOdd => "Natural/odd",
- NaturalToInteger => "Natural/toInteger",
- NaturalShow => "Natural/show",
- NaturalSubtract => "Natural/subtract",
- IntegerToDouble => "Integer/toDouble",
- IntegerNegate => "Integer/negate",
- IntegerClamp => "Integer/clamp",
- IntegerShow => "Integer/show",
- DoubleShow => "Double/show",
- ListBuild => "List/build",
- ListFold => "List/fold",
- ListLength => "List/length",
- ListHead => "List/head",
- ListLast => "List/last",
- ListIndexed => "List/indexed",
- ListReverse => "List/reverse",
- OptionalFold => "Optional/fold",
- OptionalBuild => "Optional/build",
- TextShow => "Text/show",
- })
- }
-}
-
impl Display for Scheme {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
use crate::syntax::Scheme::*;