DEV: filtering maps, lists, and tuples works.

This commit is contained in:
Jeremy Wall 2019-08-17 14:16:11 -05:00
parent 8d7ca3d6c1
commit b016b9294b
2 changed files with 86 additions and 26 deletions

View File

@ -419,18 +419,6 @@ impl Builtins {
} else { } else {
return Err(dbg!(Error {})); return Err(dbg!(Error {}));
}; };
// TODO(jwall): This can also be tuples or strings.
let elems = match list.as_ref() {
&C(List(ref elems)) => elems,
&C(Tuple(ref _flds)) => {
unimplemented!("TODO Tuple functional operations");
}
&P(Str(ref _s)) => {
unimplemented!("TODO String functional operations");
}
_ => return Err(dbg!(Error {})),
};
// get the func ptr from the stack // get the func ptr from the stack
let fptr = if let Some(ptr) = stack.pop() { let fptr = if let Some(ptr) = stack.pop() {
ptr ptr
@ -444,22 +432,65 @@ impl Builtins {
return dbg!(Err(Error {})); return dbg!(Err(Error {}));
}; };
let mut result_elems = Vec::new(); // TODO(jwall): This can also be tuples or strings.
for e in elems.iter() { let elems = match list.as_ref() {
// push function argument on the stack. &C(List(ref elems)) => {
stack.push(e.clone()); let mut result_elems = Vec::new();
// call function and push it's result on the stack. for e in elems.iter() {
let condition = VM::fcall_impl(path.as_ref().to_owned(), f, stack, env.clone())?; // push function argument on the stack.
// Check for empty or boolean results and only push e back in stack.push(e.clone());
// if they are non empty and true // call function and push it's result on the stack.
match condition.as_ref() { let condition =
&P(Empty) | &P(Bool(false)) => { VM::fcall_impl(path.as_ref().to_owned(), f, stack, env.clone())?;
continue; // Check for empty or boolean results and only push e back in
// if they are non empty and true
match condition.as_ref() {
&P(Empty) | &P(Bool(false)) => {
continue;
}
_ => result_elems.push(e.clone()),
}
} }
_ => result_elems.push(e.clone()), stack.push(Rc::new(C(List(result_elems))));
} }
} &C(Tuple(ref _flds)) => {
stack.push(Rc::new(C(List(result_elems)))); let mut new_fields = Vec::new();
for (ref name, ref val) in _flds {
stack.push(val.clone());
stack.push(Rc::new(P(Str(name.clone()))));
let condition =
VM::fcall_impl(path.as_ref().to_owned(), f, stack, env.clone())?;
// Check for empty or boolean results and only push e back in
// if they are non empty and true
match condition.as_ref() {
&P(Empty) | &P(Bool(false)) => {
continue;
}
_ => new_fields.push((name.clone(), val.clone())),
}
}
stack.push(Rc::new(C(Tuple(dbg!(new_fields)))));
}
&P(Str(ref s)) => {
let mut buf = String::new();
for c in s.chars() {
stack.push(Rc::new(P(Str(c.to_string()))));
// call function and push it's result on the stack.
let condition =
VM::fcall_impl(path.as_ref().to_owned(), f, stack, env.clone())?;
// Check for empty or boolean results and only push c back in
// if they are non empty and true
match condition.as_ref() {
&P(Empty) | &P(Bool(false)) => {
continue;
}
_ => buf.push(c),
}
}
stack.push(Rc::new(P(Str(buf))));
}
_ => return Err(dbg!(Error {})),
};
Ok(()) Ok(())
} }

View File

@ -798,3 +798,32 @@ fn simple_maps() {
])), ])),
]; ];
} }
#[test]
fn simple_filters() {
assert_parse_cases![
"filter(func(el) => true, [1,2,3]);" => C(List(vec![
Rc::new(P(Int(1))),
Rc::new(P(Int(2))),
Rc::new(P(Int(3))),
])),
"filter(func(el) => false, [1,2,3]);" => C(List(vec![
])),
"filter(func(el) => el != 1, [1,2,3]);" => C(List(vec![
Rc::new(P(Int(2))),
Rc::new(P(Int(3))),
])),
"filter(func(el) => true, \"foo\");" => P(Str("foo".to_owned())),
"filter(func(el) => false, \"foo\");" => P(Str("".to_owned())),
"filter(func(el) => el != \"f\", \"foo\");" => P(Str("oo".to_owned())),
"filter(func(k, v) => true, {foo = 1});" => C(Tuple(vec![
("foo".to_owned(), Rc::new(P(Int(1)))),
])),
"filter(func(k, v) => false, {foo = 1});" => C(Tuple(vec![
])),
"filter(func(k, v) => k != \"foo\", {foo = 1});" => C(Tuple(vec![
])),
"filter(func(k, v) => v != 1, {foo = 1});" => C(Tuple(vec![
])),
];
}