1
1
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:
Yann Hamdaoui 2022-04-04 19:33:39 +02:00 committed by GitHub
commit a2e9a36af7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 111 additions and 41 deletions

View File

@ -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),
}

View File

@ -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 {

View File

@ -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