1
1
mirror of https://github.com/tweag/nickel.git synced 2024-09-20 08:05:15 +03:00

Add a generate primitive operator

This commit is contained in:
Yann Hamdaoui 2021-02-16 16:10:09 +01:00
parent 922b950231
commit e57bb043fe
6 changed files with 51 additions and 5 deletions

View File

@ -324,6 +324,7 @@ UOp: UnaryOp = {
"wrap" => UnaryOp::Wrap(),
"embed" <Ident> => UnaryOp::Embed(<>),
"map" => UnaryOp::ListMap(),
"generate" => UnaryOp::ListGen(),
"recordMap" => UnaryOp::RecordMap(),
"seq" => UnaryOp::Seq(),
"deepSeq" => UnaryOp::DeepSeq(),
@ -626,6 +627,7 @@ extern {
"hasField" => Token::Normal(NormalToken::HasField),
"map" => Token::Normal(NormalToken::Map),
"generate" => Token::Normal(NormalToken::ListGen),
"elemAt" => Token::Normal(NormalToken::ElemAt),
"merge" => Token::Normal(NormalToken::Merge),
"default" => Token::Normal(NormalToken::Default),

View File

@ -477,6 +477,43 @@ fn process_unary_operation(
))
}
}
UnaryOp::ListGen() => {
let (f, _) = stack
.pop_arg()
.ok_or_else(|| EvalError::NotEnoughArgs(2, String::from("generate"), pos_op.clone()))?;
if let Term::Num(n) = *t {
let n_int = n as usize;
if n < 0.0 || n.fract() != 0.0 {
Err(EvalError::Other(format!("generate: expected the 1st agument to be a positive integer, got {}", n), pos_op))
} else {
let mut shared_env = Environment::new();
let f_as_var = f.body.closurize(&mut env, f.env);
// List elements are closurized to preserve lazyness of data structures. It
// maintains the invariant that any data structure only contain thunks (that is,
// currently, variables).
let ts = (0..n_int)
.map(|n| {
mk_app!(f_as_var.clone(), Term::Num(n as f64))
.closurize(&mut shared_env, env.clone())
})
.collect();
Ok(Closure {
body: Term::List(ts).into(),
env: shared_env,
})
}
} else {
Err(EvalError::TypeError(
String::from("Num"),
String::from("generate, 1st argument"),
arg_pos,
RichTerm { term: t, pos },
))
}
}
UnaryOp::RecordMap() => {
let (f, ..) = stack
.pop_arg()

View File

@ -198,6 +198,8 @@ pub enum NormalToken<'input> {
Map,
#[token("%elemAt%")]
ElemAt,
#[token("%generate%")]
ListGen,
#[token("merge")]
Merge,
#[token("default")]

View File

@ -592,6 +592,8 @@ pub enum UnaryOp {
ListTail(),
/// Return the length of a list.
ListLength(),
/// Generate a list of a given length by mapping a `Num -> Num` function onto `[1,..,n]`.
ListGen(),
/// Generated by the evaluation of a string with interpolated expressions. `ChunksConcat`
/// applied to the current chunk to evaluate. As additional state, it uses a string

View File

@ -1565,6 +1565,13 @@ pub fn get_uop_type(state: &mut State, op: &UnaryOp) -> Result<TypeWrapper, Type
let f_type = mk_tyw_arrow!(a.clone(), b.clone());
mk_tyw_arrow!(mk_typewrapper::list(a), f_type, mk_typewrapper::list(b))
}
// forall a. Num -> (Num -> a) -> List a
UnaryOp::ListGen() => {
let a = TypeWrapper::Ptr(new_var(state.table));
let f_type = mk_tyw_arrow!(AbsType::Num(), a.clone());
mk_tyw_arrow!(AbsType::Num(), f_type, mk_typewrapper::list(a))
}
// forall a b. { _ : a} -> (Str -> a -> b) -> { _ : b }
UnaryOp::RecordMap() => {
// Assuming f has type Str -> a -> b,

View File

@ -60,11 +60,7 @@
foldl aux {right = []; wrong = []} l,
generate : forall a. (Num -> a) -> Num -> List a =
fun gen n =>
if n <= 0 then
[]
else
generate gen (n-1) @ [gen n],
fun f n => generate% n f,
sort : forall a. (a -> a -> <Less, Equal, Greater>) -> List a -> List a =
fun cmp l =>