mirror of
https://github.com/tweag/nickel.git
synced 2024-10-04 23:27:15 +03:00
Merge pull request #670 from tweag/fix/transform-inside-types
Fix/transform inside types
This commit is contained in:
commit
a2e9a36af7
78
src/term.rs
78
src/term.rs
@ -976,12 +976,12 @@ impl RichTerm {
|
||||
self,
|
||||
f: &mut F,
|
||||
state: &mut S,
|
||||
method: TraverseOrder,
|
||||
order: TraverseOrder,
|
||||
) -> Result<RichTerm, E>
|
||||
where
|
||||
F: FnMut(RichTerm, &mut S) -> Result<RichTerm, E>,
|
||||
{
|
||||
let rt = match method {
|
||||
let rt = match order {
|
||||
TraverseOrder::TopDown => f(self, state)?,
|
||||
TraverseOrder::BottomUp => self,
|
||||
};
|
||||
@ -989,38 +989,38 @@ impl RichTerm {
|
||||
|
||||
let result = match_sharedterm! {rt.term, with {
|
||||
Term::Fun(id, t) => {
|
||||
let t = t.traverse(f, state, method)?;
|
||||
let t = t.traverse(f, state, order)?;
|
||||
RichTerm::new(
|
||||
Term::Fun(id, t),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
Term::FunPattern(id, d, t) => {
|
||||
let t = t.traverse(f, state, method)?;
|
||||
let t = t.traverse(f, state, order)?;
|
||||
RichTerm::new(
|
||||
Term::FunPattern(id, d, t),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
Term::Let(id, t1, t2, btype) => {
|
||||
let t1 = t1.traverse(f, state, method)?;
|
||||
let t2 = t2.traverse(f, state, method)?;
|
||||
let t1 = t1.traverse(f, state, order)?;
|
||||
let t2 = t2.traverse(f, state, order)?;
|
||||
RichTerm::new(
|
||||
Term::Let(id, t1, t2, btype),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
Term::LetPattern(id, pat, t1, t2) => {
|
||||
let t1 = t1.traverse(f, state, method)?;
|
||||
let t2 = t2.traverse(f, state, method)?;
|
||||
let t1 = t1.traverse(f, state, order)?;
|
||||
let t2 = t2.traverse(f, state, order)?;
|
||||
RichTerm::new(
|
||||
Term::LetPattern(id, pat, t1, t2),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
Term::App(t1, t2) => {
|
||||
let t1 = t1.traverse(f, state, method)?;
|
||||
let t2 = t2.traverse(f, state, method)?;
|
||||
let t1 = t1.traverse(f, state, order)?;
|
||||
let t2 = t2.traverse(f, state, order)?;
|
||||
RichTerm::new(
|
||||
Term::App(t1, t2),
|
||||
pos,
|
||||
@ -1032,12 +1032,12 @@ impl RichTerm {
|
||||
let cases_res: Result<HashMap<Ident, RichTerm>, E> = cases
|
||||
.into_iter()
|
||||
// For the conversion to work, note that we need a Result<(Ident,RichTerm), E>
|
||||
.map(|(id, t)| t.traverse(f, state, method).map(|t_ok| (id.clone(), t_ok)))
|
||||
.map(|(id, t)| t.traverse(f, state, order).map(|t_ok| (id.clone(), t_ok)))
|
||||
.collect();
|
||||
|
||||
let default = default.map(|t| t.traverse(f, state, method)).transpose()?;
|
||||
let default = default.map(|t| t.traverse(f, state, order)).transpose()?;
|
||||
|
||||
let t = t.traverse(f, state, method)?;
|
||||
let t = t.traverse(f, state, order)?;
|
||||
|
||||
RichTerm::new(
|
||||
Term::Switch(t, cases_res?, default),
|
||||
@ -1045,15 +1045,15 @@ impl RichTerm {
|
||||
)
|
||||
},
|
||||
Term::Op1(op, t) => {
|
||||
let t = t.traverse(f, state, method)?;
|
||||
let t = t.traverse(f, state, order)?;
|
||||
RichTerm::new(
|
||||
Term::Op1(op, t),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
Term::Op2(op, t1, t2) => {
|
||||
let t1 = t1.traverse(f, state, method)?;
|
||||
let t2 = t2.traverse(f, state, method)?;
|
||||
let t1 = t1.traverse(f, state, order)?;
|
||||
let t2 = t2.traverse(f, state, order)?;
|
||||
RichTerm::new(Term::Op2(op, t1, t2),
|
||||
pos,
|
||||
)
|
||||
@ -1061,7 +1061,7 @@ impl RichTerm {
|
||||
Term::OpN(op, ts) => {
|
||||
let ts_res: Result<Vec<RichTerm>, E> = ts
|
||||
.into_iter()
|
||||
.map(|t| t.traverse(f, state, method))
|
||||
.map(|t| t.traverse(f, state, order))
|
||||
.collect();
|
||||
RichTerm::new(
|
||||
Term::OpN(op, ts_res?),
|
||||
@ -1069,7 +1069,7 @@ impl RichTerm {
|
||||
)
|
||||
},
|
||||
Term::Wrapped(i, t) => {
|
||||
let t = t.traverse(f, state, method)?;
|
||||
let t = t.traverse(f, state, order)?;
|
||||
RichTerm::new(
|
||||
Term::Wrapped(i, t),
|
||||
pos,
|
||||
@ -1081,7 +1081,7 @@ impl RichTerm {
|
||||
let map_res: Result<HashMap<Ident, RichTerm>, E> = map
|
||||
.into_iter()
|
||||
// For the conversion to work, note that we need a Result<(Ident,RichTerm), E>
|
||||
.map(|(id, t)| t.traverse(f, state, method).map(|t_ok| (id.clone(), t_ok)))
|
||||
.map(|(id, t)| t.traverse(f, state, order).map(|t_ok| (id.clone(), t_ok)))
|
||||
.collect();
|
||||
RichTerm::new(
|
||||
Term::Record(map_res?, attrs),
|
||||
@ -1094,14 +1094,14 @@ impl RichTerm {
|
||||
let map_res: Result<HashMap<Ident, RichTerm>, E> = map
|
||||
.into_iter()
|
||||
// For the conversion to work, note that we need a Result<(Ident,RichTerm), E>
|
||||
.map(|(id, t)| Ok((id, t.traverse(f, state, method)?)))
|
||||
.map(|(id, t)| Ok((id, t.traverse(f, state, order)?)))
|
||||
.collect();
|
||||
let dyn_fields_res: Result<Vec<(RichTerm, RichTerm)>, E> = dyn_fields
|
||||
.into_iter()
|
||||
.map(|(id_t, t)| {
|
||||
Ok((
|
||||
id_t.traverse(f, state, method)?,
|
||||
t.traverse(f, state, method)?,
|
||||
id_t.traverse(f, state, order)?,
|
||||
t.traverse(f, state, order)?,
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
@ -1113,7 +1113,7 @@ impl RichTerm {
|
||||
Term::Array(ts) => {
|
||||
let ts_res: Result<Vec<RichTerm>, E> = ts
|
||||
.into_iter()
|
||||
.map(|t| t.traverse(f, state, method))
|
||||
.map(|t| t.traverse(f, state, order))
|
||||
.collect();
|
||||
|
||||
RichTerm::new(
|
||||
@ -1127,7 +1127,7 @@ impl RichTerm {
|
||||
.map(|chunk| match chunk {
|
||||
chunk @ StrChunk::Literal(_) => Ok(chunk),
|
||||
StrChunk::Expr(t, indent) => {
|
||||
Ok(StrChunk::Expr(t.traverse(f, state, method)?, indent))
|
||||
Ok(StrChunk::Expr(t.traverse(f, state, order)?, indent))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
@ -1138,17 +1138,19 @@ impl RichTerm {
|
||||
)
|
||||
},
|
||||
Term::MetaValue(meta) => {
|
||||
let mut f_on_type = |ty: Types, s: &mut S| {
|
||||
match ty.0 {
|
||||
AbsType::Flat(t) => t.traverse(f, s, order).map(|t| Types(AbsType::Flat(t))),
|
||||
_ => Ok(ty),
|
||||
}
|
||||
};
|
||||
|
||||
let contracts: Result<Vec<Contract>, _> = meta
|
||||
.contracts
|
||||
.into_iter()
|
||||
.map(|ctr| {
|
||||
let types = match ctr.types {
|
||||
Types(AbsType::Flat(t)) => {
|
||||
Types(AbsType::Flat(t.traverse(f, state, method)?))
|
||||
}
|
||||
ty => ty,
|
||||
};
|
||||
Ok(Contract { types, ..ctr })
|
||||
let Contract {types, label} = ctr;
|
||||
types.traverse(&mut f_on_type, state, order).map(|types| Contract { types, label })
|
||||
})
|
||||
.collect();
|
||||
let contracts = contracts?;
|
||||
@ -1156,19 +1158,14 @@ impl RichTerm {
|
||||
let types = meta
|
||||
.types
|
||||
.map(|ctr| {
|
||||
let types = match ctr.types {
|
||||
Types(AbsType::Flat(t)) => {
|
||||
Types(AbsType::Flat(t.traverse(f, state, method)?))
|
||||
}
|
||||
ty => ty,
|
||||
};
|
||||
Ok(Contract { types, ..ctr })
|
||||
let Contract {types, label} = ctr;
|
||||
types.traverse(&mut f_on_type, state, order).map(|types| Contract { types, label })
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let value = meta
|
||||
.value
|
||||
.map(|t| t.traverse(f, state, method))
|
||||
.map(|t| t.traverse(f, state, order))
|
||||
.map_or(Ok(None), |res| res.map(Some))?;
|
||||
let meta = MetaValue {
|
||||
doc: meta.doc,
|
||||
@ -1177,6 +1174,7 @@ impl RichTerm {
|
||||
priority: meta.priority,
|
||||
value,
|
||||
};
|
||||
|
||||
RichTerm::new(
|
||||
Term::MetaValue(meta),
|
||||
pos,
|
||||
@ -1184,7 +1182,7 @@ impl RichTerm {
|
||||
}} else rt
|
||||
};
|
||||
|
||||
match method {
|
||||
match order {
|
||||
TraverseOrder::TopDown => Ok(result),
|
||||
TraverseOrder::BottomUp => f(result, state),
|
||||
}
|
||||
|
69
src/types.rs
69
src/types.rs
@ -54,7 +54,7 @@
|
||||
use crate::error::{ParseError, ParseErrors, TypecheckError};
|
||||
use crate::identifier::Ident;
|
||||
use crate::term::make as mk_term;
|
||||
use crate::term::{RichTerm, Term};
|
||||
use crate::term::{RichTerm, Term, TraverseOrder};
|
||||
use crate::{mk_app, mk_fun, mk_switch};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
@ -366,6 +366,73 @@ impl Types {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply a transformation on a whole type by mapping a faillible function `f` on each node in
|
||||
/// manner as prescribed by the order.
|
||||
/// `f` may return a generic error `E` and use the state `S` which is passed around.
|
||||
pub fn traverse<F, S, E>(
|
||||
self: Types,
|
||||
f: &mut F,
|
||||
state: &mut S,
|
||||
order: TraverseOrder,
|
||||
) -> Result<Types, E>
|
||||
where
|
||||
F: FnMut(Types, &mut S) -> Result<Types, E>,
|
||||
{
|
||||
let ty = match order {
|
||||
TraverseOrder::TopDown => f(self, state)?,
|
||||
TraverseOrder::BottomUp => self,
|
||||
};
|
||||
|
||||
let abs_ty = match ty.0 {
|
||||
AbsType::Dyn()
|
||||
| AbsType::Num()
|
||||
| AbsType::Bool()
|
||||
| AbsType::Str()
|
||||
| AbsType::Sym()
|
||||
| AbsType::Var(_)
|
||||
| AbsType::RowEmpty()
|
||||
| AbsType::Flat(_) => Ok(ty.0),
|
||||
AbsType::Forall(id, ty_inner) => (*ty_inner)
|
||||
.traverse(f, state, order)
|
||||
.map(|ty| AbsType::Forall(id, Box::new(ty))),
|
||||
AbsType::Enum(ty_inner) => (*ty_inner)
|
||||
.traverse(f, state, order)
|
||||
.map(Box::new)
|
||||
.map(AbsType::Enum),
|
||||
AbsType::StaticRecord(ty_inner) => (*ty_inner)
|
||||
.traverse(f, state, order)
|
||||
.map(Box::new)
|
||||
.map(AbsType::StaticRecord),
|
||||
AbsType::DynRecord(ty_inner) => (*ty_inner)
|
||||
.traverse(f, state, order)
|
||||
.map(Box::new)
|
||||
.map(AbsType::DynRecord),
|
||||
AbsType::Array(ty_inner) => (*ty_inner)
|
||||
.traverse(f, state, order)
|
||||
.map(Box::new)
|
||||
.map(AbsType::Array),
|
||||
AbsType::Arrow(ty1, ty2) => {
|
||||
let ty1 = (*ty1).traverse(f, state, order)?;
|
||||
let ty2 = (*ty2).traverse(f, state, order)?;
|
||||
Ok(AbsType::Arrow(Box::new(ty1), Box::new(ty2)))
|
||||
}
|
||||
AbsType::RowExtend(id, ty_row, tail) => {
|
||||
let ty_row = ty_row
|
||||
.map(|ty| (*ty).traverse(f, state, order))
|
||||
.transpose()?;
|
||||
let tail = (*tail).traverse(f, state, order)?;
|
||||
Ok(AbsType::RowExtend(id, ty_row.map(Box::new), Box::new(tail)))
|
||||
}
|
||||
}?;
|
||||
|
||||
let result = Types(abs_ty);
|
||||
|
||||
match order {
|
||||
TraverseOrder::TopDown => Ok(result),
|
||||
TraverseOrder::BottomUp => f(result, state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Types {
|
||||
|
@ -28,5 +28,10 @@ let assertDeserInv = fun x =>
|
||||
bar = ["str", true],
|
||||
baz = {subfoo = true, subbar = 0}
|
||||
},
|
||||
|
||||
# regression test for issue #668 (https://github.com/tweag/nickel/issues/668)
|
||||
let base = {foo | {_: {bar | default = 2}}} in
|
||||
let ext = {foo = {some = {}}} in
|
||||
assertSerInv (base & ext),
|
||||
]
|
||||
|> array.foldl (fun x y => (x | Assert) && y) true
|
||||
|
Loading…
Reference in New Issue
Block a user