use crate::push::instructions::Instruction;
use crate::push::instructions::InstructionCache;
use crate::push::item::Item;
use crate::push::random::CodeGenerator;
use crate::push::stack::PushStack;
use crate::push::state::PushState;
use crate::push::state::*;
use std::cmp;
use std::collections::HashMap;
pub fn load_code_instructions(map: &mut HashMap<String, Instruction>) {
map.insert(String::from("CODE.="), Instruction::new(code_eq));
map.insert(String::from("CODE.APPEND"), Instruction::new(code_append));
map.insert(String::from("CODE.ATOM"), Instruction::new(code_item));
map.insert(String::from("CODE.CAR"), Instruction::new(code_first));
map.insert(String::from("CODE.CDR"), Instruction::new(code_rest));
map.insert(String::from("CODE.CONS"), Instruction::new(code_cons));
map.insert(
String::from("CODE.CONTAINER"),
Instruction::new(code_container),
);
map.insert(
String::from("CODE.CONTAINS"),
Instruction::new(code_contains),
);
map.insert(String::from("CODE.DEFINE"), Instruction::new(code_define));
map.insert(
String::from("CODE.DEFINITION"),
Instruction::new(code_definition),
);
map.insert(
String::from("CODE.DISCREPANCY"),
Instruction::new(code_discrepancy),
);
map.insert(String::from("CODE.DO"), Instruction::new(code_do));
map.insert(String::from("CODE.DO*"), Instruction::new(code_pop_and_do));
map.insert(String::from("CODE.LOOP"), Instruction::new(code_loop));
map.insert(String::from("CODE.DUP"), Instruction::new(code_dup));
map.insert(String::from("CODE.EXTRACT"), Instruction::new(code_extract));
map.insert(String::from("CODE.FLUSH"), Instruction::new(code_flush));
map.insert(
String::from("CODE.FROMBOOLEAN"),
Instruction::new(code_from_bool),
);
map.insert(
String::from("CODE.FROMFLOAT"),
Instruction::new(code_from_float),
);
map.insert(
String::from("CODE.FROMINTEGER"),
Instruction::new(code_from_int),
);
map.insert(
String::from("CODE.FROMNAME"),
Instruction::new(code_from_name),
);
map.insert(String::from("CODE.ID"), Instruction::new(code_id));
map.insert(String::from("CODE.IF"), Instruction::new(code_if));
map.insert(String::from("CODE.INSERT"), Instruction::new(code_insert));
map.insert(String::from("CODE.LENGTH"), Instruction::new(code_length));
map.insert(String::from("CODE.LIST"), Instruction::new(code_list));
map.insert(String::from("CODE.MEMBER"), Instruction::new(code_member));
map.insert(String::from("CODE.NOOP"), Instruction::new(code_noop));
map.insert(String::from("CODE.NTH"), Instruction::new(code_nth));
map.insert(String::from("CODE.NULL"), Instruction::new(code_null));
map.insert(String::from("CODE.POP"), Instruction::new(code_pop));
map.insert(
String::from("CODE.POSITION"),
Instruction::new(code_position),
);
map.insert(String::from("CODE.PRINT"), Instruction::new(code_print));
map.insert(String::from("CODE.QUOTE"), Instruction::new(code_quote));
map.insert(String::from("CODE.RAND"), Instruction::new(code_rand));
map.insert(String::from("CODE.ROT"), Instruction::new(code_rot));
map.insert(String::from("CODE.SHOVE"), Instruction::new(code_shove));
map.insert(String::from("CODE.SIZE"), Instruction::new(code_size));
map.insert(
String::from("CODE.STACKDEPTH"),
Instruction::new(code_stack_depth),
);
map.insert(String::from("CODE.SUBST"), Instruction::new(code_subst));
map.insert(String::from("CODE.SWAP"), Instruction::new(code_swap));
map.insert(String::from("CODE.YANK"), Instruction::new(code_yank));
map.insert(
String::from("CODE.YANKDUP"),
Instruction::new(code_yank_dup),
);
}
pub fn code_id(push_state: &mut PushState, _instruction_set: &InstructionCache) {
push_state.int_stack.push(CODE_STACK_ID);
}
pub fn code_eq(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(pv) = push_state.code_stack.copy_vec(2) {
push_state
.bool_stack
.push(pv[0].to_string() == pv[1].to_string());
}
}
pub fn code_append(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(pv) = push_state.code_stack.pop_vec(2) {
push_state.code_stack.push(Item::List {
items: PushStack::from_vec(pv),
});
}
}
pub fn code_item(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
push_state.bool_stack.push(
push_state.code_stack.last_eq(&Item::int(0))
|| push_state.code_stack.last_eq(&Item::noop()),
);
}
pub fn code_first(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if push_state.code_stack.last_eq(&Item::empty_list()) {
match push_state.code_stack.pop() {
Some(Item::List { mut items }) => {
if let Some(item) = items.pop() {
push_state.code_stack.push(item);
}
}
_ => (),
}
}
}
pub fn code_rest(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
match push_state.code_stack.pop() {
Some(Item::List { mut items }) => {
items.pop();
push_state.code_stack.push(Item::List { items: items });
}
_ => (),
}
}
pub fn code_cons(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(pv) = push_state.code_stack.pop_vec(2) {
let mut consblock = PushStack::new();
for i in (0..2).rev() {
match &pv[i] {
Item::Literal { push_type: _ } => {
consblock.push(pv[i].clone());
}
Item::List { items: a } => {
if let Some(vec) = a.copy_vec(a.size()) {
consblock.push_vec(vec)
}
}
_ => (),
}
}
push_state.code_stack.push(Item::List { items: consblock });
}
}
pub fn code_container(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(code) = push_state.code_stack.copy_vec(2) {
match Item::container(&code[1], &code[0]) {
Ok(container) => push_state.code_stack.push(container),
Err(_) => push_state.code_stack.push(Item::empty_list()),
}
}
}
pub fn code_contains(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(ov) = push_state.code_stack.copy_vec(2) {
let first_el = ov[1].to_string();
let code_str = ov[0].to_string();
if first_el.contains(&code_str) {
push_state.bool_stack.push(true);
} else {
push_state.bool_stack.push(false);
}
}
}
pub fn code_define(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(name) = push_state.name_stack.pop() {
if let Some(instruction) = push_state.code_stack.pop() {
push_state.name_bindings.insert(name, instruction);
}
}
}
pub fn code_definition(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(name) = push_state.name_stack.pop() {
if let Some(instruction) = push_state.name_bindings.get(&*name) {
push_state.code_stack.push(instruction.clone());
}
}
}
pub fn code_discrepancy(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
let mut discrepancy = 0;
if let Some(ov) = push_state.code_stack.copy_vec(2) {
match &ov[0] {
Item::List { items: fstlist } => {
match &ov[1] {
Item::List { items: scdlist } => {
if let Some(fstvec) = fstlist.copy_vec(fstlist.size()) {
for (i, x) in fstvec.iter().rev().enumerate() {
if let Some(val) = scdlist.equal_at(i, x) {
if !val {
discrepancy += 1;
}
}
}
}
discrepancy =
discrepancy + (fstlist.size() as i32 - scdlist.size() as i32).abs();
}
_ => {
discrepancy = if ov[0].to_string() != ov[1].to_string() {
1
} else {
0
}
}
}
}
_ => {
discrepancy = if ov[0].to_string() != ov[1].to_string() {
1
} else {
0
}
}
}
push_state.int_stack.push(discrepancy);
}
}
pub fn code_do(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(instruction) = push_state.code_stack.copy(0) {
push_state.exec_stack.push(Item::InstructionMeta {
name: "CODE.POP".to_string(),
});
push_state.exec_stack.push(instruction);
}
}
pub fn code_pop_and_do(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(instruction) = push_state.code_stack.copy(0) {
push_state.exec_stack.push(instruction);
push_state.exec_stack.push(Item::InstructionMeta {
name: "CODE.POP".to_string(),
});
}
}
pub fn code_loop(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(body) = push_state.code_stack.pop() {
if let Some(index) = push_state.index_stack.copy(0) {
if index.current < index.destination {
let updated_loop = Item::list(vec![
body.clone(),
Item::instruction("CODE.LOOP".to_string()),
Item::instruction("INDEX.INCREASE".to_string()),
]);
push_state.exec_stack.push(updated_loop);
push_state.exec_stack.push(body);
} else {
push_state.index_stack.pop();
}
}
}
}
pub fn code_dup(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(instruction) = push_state.code_stack.copy(0) {
push_state.code_stack.push(instruction);
}
}
pub fn code_extract(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(sub_idx) = push_state.int_stack.pop() {
if let Some(code) = push_state.code_stack.get(0) {
let total_size = Item::size(code);
let norm_idx = sub_idx.rem_euclid(total_size as i32);
match Item::traverse(&code, norm_idx as usize) {
Ok(el) => push_state.code_stack.push(el),
Err(_) => (),
};
}
}
}
pub fn code_flush(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
push_state.code_stack.flush();
}
pub fn code_from_bool(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(bval) = push_state.bool_stack.pop() {
push_state.code_stack.push(Item::bool(bval));
}
}
pub fn code_from_float(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(fval) = push_state.float_stack.pop() {
push_state.code_stack.push(Item::float(fval));
}
}
pub fn code_from_int(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(ival) = push_state.int_stack.pop() {
push_state.code_stack.push(Item::int(ival));
}
}
pub fn code_from_name(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(nval) = push_state.name_stack.pop() {
push_state.code_stack.push(Item::id(nval.to_string()));
}
}
pub fn code_if(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(code) = push_state.code_stack.pop_vec(2) {
if let Some(exec_second) = push_state.bool_stack.pop() {
if exec_second {
push_state.exec_stack.push(code[0].clone());
} else {
push_state.exec_stack.push(code[1].clone());
}
}
}
}
pub fn code_insert(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(sub_idx) = push_state.int_stack.pop() {
if let Some(code_to_be_inserted) = push_state.code_stack.copy(1) {
let _ = Item::insert(
push_state.code_stack.get_mut(0).unwrap(),
&code_to_be_inserted,
sub_idx as usize,
);
}
}
}
pub fn code_length(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(top_item) = push_state.code_stack.get(0) {
match top_item {
Item::List { items } => push_state.int_stack.push(items.size() as i32),
_ => push_state.int_stack.push(1),
}
}
}
pub fn code_list(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(top_items) = push_state.code_stack.copy_vec(2) {
push_state
.code_stack
.push(Item::list(vec![top_items[0].clone(), top_items[1].clone()]));
}
}
pub fn code_member(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(ov) = push_state.code_stack.copy_vec(2) {
let top_el = ov[1].to_string();
let sec_el = ov[0].to_string();
if sec_el.contains(&top_el) {
push_state.bool_stack.push(true);
} else {
push_state.bool_stack.push(false);
}
}
}
pub fn code_noop(_push_state: &mut PushState, _instruction_cache: &InstructionCache) {}
pub fn code_nth(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(sub_idx) = push_state.int_stack.pop() {
if let Some(code) = push_state.code_stack.get(0) {
let total_size = Item::shallow_size(code);
let idx = sub_idx.rem_euclid(total_size as i32);
let mut item_to_push = Item::empty_list();
if idx == 0 {
item_to_push = code.clone();
}
match code {
Item::List { items } => {
if let Some(nth_item) = items.get(idx as usize - 1) {
item_to_push = nth_item.clone();
}
}
_ => (),
}
push_state.code_stack.push(item_to_push);
}
}
}
pub fn code_null(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(code) = push_state.code_stack.get(0) {
let mut is_null = false;
match code {
Item::List { items } => {
if items.size() == 0 {
is_null = true;
}
}
_ => (),
}
push_state.bool_stack.push(is_null);
}
}
pub fn code_pop(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
push_state.code_stack.pop();
}
pub fn code_position(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(code) = push_state.code_stack.copy_vec(2) {
match Item::contains(&code[1], &code[0], 0) {
Ok(pos) => push_state.int_stack.push(pos as i32),
Err(()) => push_state.int_stack.push(-1),
}
}
}
pub fn code_print(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if push_state.code_stack.size() > 0 {
let code_str = push_state.code_stack.to_string();
push_state.name_stack.push(code_str);
}
}
pub fn code_quote(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(exec_code) = push_state.exec_stack.pop() {
push_state.code_stack.push(exec_code);
}
}
pub fn code_rand(push_state: &mut PushState, instruction_cache: &InstructionCache) {
if let Some(size_limit) = push_state.int_stack.pop() {
let limit = cmp::min(
i32::abs(size_limit),
i32::abs(push_state.configuration.max_points_in_random_expressions),
);
if let Some(rand_item) =
CodeGenerator::random_code(&push_state, &instruction_cache, limit as usize)
{
push_state.code_stack.push(rand_item);
}
}
}
pub fn code_rot(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
push_state.code_stack.yank(2);
}
pub fn code_shove(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(shove_index) = push_state.int_stack.pop() {
let corr_index = i32::max(
i32::min((push_state.code_stack.size() as i32) - 1, shove_index),
0,
) as usize;
push_state.code_stack.shove(corr_index as usize);
}
}
pub fn code_size(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(code) = push_state.code_stack.get(0) {
push_state.int_stack.push(Item::size(&code) as i32);
}
}
pub fn code_stack_depth(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
push_state
.int_stack
.push(push_state.code_stack.size() as i32);
}
pub fn code_subst(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(code) = push_state.code_stack.pop_vec(3) {
let mut target = code[2].clone();
if Item::substitute(&mut target, &code[0], &code[1]) {
push_state.code_stack.push(code[1].clone());
} else {
push_state.code_stack.push(target);
}
}
}
pub fn code_swap(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
push_state.code_stack.shove(1);
}
pub fn code_yank(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(index) = push_state.int_stack.pop() {
let corr_index = i32::max(
i32::min((push_state.code_stack.size() as i32) - 1, index),
0,
) as usize;
push_state.code_stack.yank(corr_index as usize);
}
}
pub fn code_yank_dup(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(index) = push_state.int_stack.pop() {
let corr_index = i32::max(
i32::min((push_state.code_stack.size() as i32) - 1, index),
0,
) as usize;
if let Some(deep_item) = push_state.code_stack.copy(corr_index as usize) {
push_state.code_stack.push(deep_item);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::push::index::Index;
use crate::push::parser::PushParser;
use crate::push::instructions::InstructionSet;
pub fn icache() -> InstructionCache {
InstructionCache::new(vec![])
}
#[test]
fn code_eq_pushes_true_when_elements_equal() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(1));
test_state.code_stack.push(Item::int(1));
code_eq(&mut test_state, &icache());
assert_eq!(test_state.code_stack.size(), 2);
if let Some(val) = test_state.bool_stack.pop() {
assert_eq!(val, true, "Must be true in case of equality");
} else {
assert!(false, "Expected bool value");
}
}
#[test]
fn code_eq_pushes_false_when_elements_unequal() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(1));
test_state.code_stack.push(Item::int(2));
code_eq(&mut test_state, &icache());
assert_eq!(test_state.code_stack.size(), 2);
if let Some(val) = test_state.bool_stack.pop() {
assert_eq!(val, false, "Must be false in case of inequality");
} else {
assert!(false, "Expected bool value");
}
}
#[test]
fn code_append_pushes_block_when_finding_literals() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(1));
test_state.code_stack.push(Item::int(2));
code_append(&mut test_state, &icache());
assert_eq!(test_state.code_stack.size(), 1, "Excpected single element");
assert!(
test_state.code_stack.last_eq(&Item::empty_list()),
"Expected Code Block"
);
}
#[test]
fn code_item_pushes_true_when_no_list_found() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(0));
code_item(&mut test_state, &icache());
assert!(
test_state.bool_stack.last_eq(&true),
"Should push true for Literal"
);
test_state = PushState::new();
test_state.code_stack.push(Item::noop());
code_item(&mut test_state, &icache());
assert!(
test_state.bool_stack.last_eq(&true),
"Should push true for Instruction"
);
test_state = PushState::new();
test_state.code_stack.push(Item::empty_list());
code_item(&mut test_state, &icache());
assert!(
test_state.bool_stack.last_eq(&false),
"Should push false for Code Block"
);
}
#[test]
fn code_first_pushes_first_element_when_cb_is_found() {
let mut test_state = PushState::new();
test_state
.code_stack
.push(Item::list(vec![Item::int(1), Item::int(2), Item::int(3)]));
code_first(&mut test_state, &icache());
assert_eq!(test_state.code_stack.to_string(), "3");
}
#[test]
fn code_rest_pushes_all_except_first_element() {
let mut test_state = PushState::new();
test_state
.code_stack
.push(Item::list(vec![Item::int(1), Item::int(2), Item::int(3)]));
assert_eq!(
test_state.code_stack.to_string(),
"( 3 2 1 )"
);
code_rest(&mut test_state, &icache());
assert_eq!(
test_state.code_stack.to_string(),
"( 2 1 )"
);
}
#[test]
fn code_cons_appends_in_reverse_order() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(1));
test_state.code_stack.push(Item::int(2));
assert_eq!(
test_state.code_stack.to_string(),
"2 1"
);
code_cons(&mut test_state, &icache());
assert_eq!(
test_state.code_stack.to_string(),
"( 1 2 )"
);
}
#[test]
fn code_container_finds_subelement() {
let mut test_state = PushState::new();
test_state
.code_stack
.push(Item::list(vec![Item::int(1), Item::int(2)]));
test_state.code_stack.push(Item::list(vec![
Item::list(vec![
Item::int(3),
Item::list(vec![Item::int(1), Item::int(2)]),
Item::list(vec![Item::int(3), Item::int(3)]),
Item::int(3),
]),
Item::int(4),
Item::int(5),
]));
code_container(&mut test_state, &icache());
assert!(test_state
.code_stack
.to_string()
.starts_with("( 3 ( 3 3 ) ( 2 1 ) 3"));
}
#[test]
fn code_contains_pushes_true_if_second_contains_first() {
let mut test_state = PushState::new();
test_state
.code_stack
.push(Item::list(vec![Item::int(1), Item::int(2)]));
test_state.code_stack.push(Item::list(vec![
Item::list(vec![
Item::int(3),
Item::list(vec![Item::int(1), Item::int(2)]),
Item::list(vec![Item::int(3), Item::int(3)]),
Item::int(3),
]),
Item::int(4),
Item::int(5),
]));
code_contains(&mut test_state, &icache());
assert_eq!(test_state.bool_stack.to_string(), "TRUE");
}
#[test]
fn code_define_creates_name_binding() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(2));
test_state.name_stack.push(String::from("TEST"));
code_define(&mut test_state, &icache());
assert_eq!(
*test_state.name_bindings.get("TEST").unwrap().to_string(),
Item::int(2).to_string()
);
}
#[test]
fn code_definition_pushes_to_code_stack() {
let mut test_state = PushState::new();
test_state
.name_bindings
.insert(String::from("TEST"), Item::int(2));
test_state.name_stack.push(String::from("TEST"));
code_definition(&mut test_state, &icache());
assert_eq!(
test_state.code_stack.pop().unwrap().to_string(),
Item::int(2).to_string()
);
}
#[test]
fn code_discrepancy_calculates_zero_discrepancy_correctly() {
let mut test_state = PushState::new();
test_state
.code_stack
.push(Item::list(vec![Item::int(1), Item::int(2)]));
test_state
.code_stack
.push(Item::list(vec![Item::int(1), Item::int(2)]));
code_discrepancy(&mut test_state, &icache());
assert_eq!(test_state.int_stack.to_string(), "0");
}
#[test]
fn code_discrepancy_calculates_discrepancy_correctly() {
let mut test_state = PushState::new();
test_state
.code_stack
.push(Item::list(vec![Item::int(0), Item::int(2)]));
test_state
.code_stack
.push(Item::list(vec![Item::int(1), Item::int(2)]));
code_discrepancy(&mut test_state, &icache());
assert_eq!(test_state.int_stack.to_string(), "1");
}
#[test]
fn code_do_adds_instruction_to_excecution_stack() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(9));
code_do(&mut test_state, &icache());
assert_eq!(
test_state.exec_stack.to_string(),
"9 CODE.POP"
);
}
#[test]
fn code_pop_and_do_adds_instruction_to_excecution_stack() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(9));
code_pop_and_do(&mut test_state, &icache());
assert_eq!(
test_state.exec_stack.to_string(),
"CODE.POP 9"
);
}
#[test]
fn code_loop_pushes_body_and_updated_loop() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::noop());
test_state.index_stack.push(Index::new(3));
code_loop(&mut test_state, &icache());
assert_eq!(test_state.exec_stack.to_string(), "NOOP ( INDEX.INCREASE CODE.LOOP NOOP )");
}
#[test]
fn code_loop_removes_index_when_terminated() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::noop());
let mut test_index = Index::new(3);
test_index.current = 3;
test_state.index_stack.push(test_index);
code_loop(&mut test_state, &icache());
assert_eq!(test_state.index_stack.to_string(), "");
assert_eq!(test_state.exec_stack.to_string(), "");
}
#[test]
fn code_dup_duplicates_top_element() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::noop());
code_dup(&mut test_state, &icache());
assert_eq!(
test_state.code_stack.to_string(),
"NOOP NOOP"
);
}
#[test]
fn code_flush_empties_stack() {
let mut test_state = PushState::new();
test_state
.code_stack
.push(Item::list(vec![Item::int(0), Item::int(2)]));
test_state
.code_stack
.push(Item::list(vec![Item::int(1), Item::int(2)]));
code_flush(&mut test_state, &icache());
assert_eq!(test_state.code_stack.to_string(), "");
}
#[test]
fn code_from_bool_pushes_literal() {
let mut test_state = PushState::new();
test_state.bool_stack.push(true);
code_from_bool(&mut test_state, &icache());
assert_eq!(test_state.code_stack.to_string(), "TRUE");
}
#[test]
fn code_if_pushes_second_item_when_true() {
let mut test_state = PushState::new();
test_state.bool_stack.push(true);
test_state.code_stack.push(Item::int(2));
test_state.code_stack.push(Item::int(1));
code_if(&mut test_state, &icache());
assert_eq!(test_state.exec_stack.to_string(), "2");
assert_eq!(test_state.code_stack.to_string(), "");
assert_eq!(test_state.bool_stack.to_string(), "");
}
#[test]
fn code_if_pushes_first_item_when_false() {
let mut test_state = PushState::new();
test_state.bool_stack.push(false);
test_state.code_stack.push(Item::int(2));
test_state.code_stack.push(Item::int(1));
code_if(&mut test_state, &icache());
assert_eq!(test_state.exec_stack.to_string(), "1");
assert_eq!(test_state.code_stack.to_string(), "");
assert_eq!(test_state.bool_stack.to_string(), "");
}
#[test]
fn code_extract_finds_correct_subelement() {
let mut test_state = PushState::new();
let test_item = Item::list(vec![
Item::int(4),
Item::list(vec![Item::int(3)]),
Item::int(2),
Item::int(1),
]);
test_state.int_stack.push(10);
test_state.code_stack.push(test_item);
code_extract(&mut test_state, &icache());
assert_eq!(test_state.int_stack.to_string(), "");
assert_eq!(
test_state.code_stack.to_string(),
"3 ( 1 2 ( 3 ) 4 )"
);
}
#[test]
fn code_insert_replaces_element() {
let mut test_state = PushState::new();
test_state.int_stack.push(4);
let test_container = Item::list(vec![
Item::int(4),
Item::list(vec![Item::int(3)]),
Item::int(2),
Item::int(1),
]);
let test_item = Item::int(5);
test_state.code_stack.push(test_item);
test_state.code_stack.push(test_container);
code_insert(&mut test_state, &icache());
assert_eq!(test_state.int_stack.to_string(), "");
assert_eq!(
test_state.code_stack.to_string(),
"( 1 2 ( 5 ) 4 ) 5"
);
}
#[test]
fn code_insert_does_nothing_when_index_too_big() {
let mut test_state = PushState::new();
test_state.int_stack.push(4);
let test_container = Item::list(vec![Item::int(2), Item::int(1)]);
let test_item = Item::int(5);
test_state.code_stack.push(test_item);
test_state.code_stack.push(test_container);
code_insert(&mut test_state, &icache());
assert_eq!(test_state.int_stack.to_string(), "");
assert_eq!(
test_state.code_stack.to_string(),
"( 1 2 ) 5"
);
}
#[test]
fn code_length_pushes_top_list_size() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::list(vec![
Item::int(2),
Item::int(1),
Item::list(vec![Item::int(0), Item::float(2.3)]),
]));
code_length(&mut test_state, &icache());
assert_eq!(test_state.int_stack.to_string(), "3");
}
#[test]
fn code_list_pushes_lists_including_top_items() {
let mut test_state = PushState::new();
test_state
.code_stack
.push(Item::list(vec![Item::int(0), Item::float(2.3)]));
test_state.code_stack.push(Item::int(2));
code_list(&mut test_state, &icache());
assert_eq!(test_state.code_stack.to_string(), "( 2 ( 2.300 0 ) ) 2 ( 2.300 0 )");
}
#[test]
fn code_nth_ignores_nested_lists() {
let mut test_state = PushState::new();
let test_item = Item::list(vec![
Item::int(4),
Item::list(vec![Item::int(3)]),
Item::int(2),
Item::int(1),
]);
test_state.int_stack.push(9);
test_state.code_stack.push(test_item);
code_nth(&mut test_state, &icache());
assert_eq!(test_state.int_stack.to_string(), "");
assert_eq!(
test_state.code_stack.to_string(),
"4 ( 1 2 ( 3 ) 4 )"
);
}
#[test]
fn code_null_pushes_true_for_empty_list() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::empty_list());
code_null(&mut test_state, &icache());
assert_eq!(*test_state.bool_stack.get(0).unwrap(), true);
}
#[test]
fn code_pop_removes_top_element() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(1));
test_state.code_stack.push(Item::int(2));
test_state.code_stack.push(Item::int(3));
code_pop(&mut test_state, &icache());
assert_eq!(
test_state.code_stack.to_string(),
"2 1"
);
}
#[test]
fn code_position_pushes_value_when_contained() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(3));
test_state.code_stack.push(Item::list(vec![
Item::int(4),
Item::list(vec![Item::int(3)]),
Item::int(2),
Item::int(1),
]));
code_position(&mut test_state, &icache());
assert_eq!(test_state.int_stack.get(0).unwrap(), &4);
}
#[test]
fn code_print_creates_parseable_output() {
let mut test_state = PushState::new();
let mut instruction_set = InstructionSet::new();
instruction_set.load();
test_state.code_stack.push(Item::int(3));
test_state.code_stack.push(Item::list(vec![
Item::int(4),
Item::list(vec![Item::int(3)]),
Item::int(2),
Item::int(1),
]));
code_print(&mut test_state, &icache());
assert_eq!(test_state.name_stack.size(), 1);
let printed_code = test_state.name_stack.copy(0).unwrap();
PushParser::parse_program(&mut test_state, &instruction_set, &printed_code);
assert_eq!(
test_state.exec_stack.to_string(), test_state.code_stack.to_string());
}
#[test]
fn code_quote_moves_item_from_exec_to_code_stack() {
let mut test_state = PushState::new();
test_state.exec_stack.push(Item::int(2));
code_quote(&mut test_state, &icache());
assert_eq!(test_state.code_stack.to_string(), "2")
}
#[test]
fn code_rand_pushes_random_code() {
let mut test_state = PushState::new();
test_state.int_stack.push(100);
code_rand(&mut test_state, &icache());
assert_eq!(test_state.code_stack.size(), 1);
}
#[test]
fn code_rot_shuffles_elements() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(3));
test_state.code_stack.push(Item::int(2));
test_state.code_stack.push(Item::int(1));
assert_eq!(
test_state.code_stack.to_string(),
"1 2 3"
);
code_rot(&mut test_state, &icache());
assert_eq!(
test_state.code_stack.to_string(),
"3 1 2"
);
}
#[test]
fn code_shove_inserts_at_right_position() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(4));
test_state.code_stack.push(Item::int(3));
test_state.code_stack.push(Item::int(2));
test_state.code_stack.push(Item::int(1));
assert_eq!(
test_state.code_stack.to_string(),
"1 2 3 4"
);
test_state.int_stack.push(2);
code_shove(&mut test_state, &icache());
assert_eq!(
test_state.code_stack.to_string(),
"2 3 1 4"
);
}
#[test]
fn code_size_calculates_top_element() {
let mut test_state = PushState::new();
let test_item = Item::list(vec![
Item::int(4),
Item::list(vec![Item::int(3)]),
Item::int(2),
Item::int(1),
]);
test_state.code_stack.push(test_item);
code_size(&mut test_state, &icache());
assert_eq!(test_state.int_stack.to_string(), "6");
}
#[test]
fn code_substitute_code_elements() {
let mut test_state = PushState::new();
let target_item = Item::list(vec![
Item::list(vec![]),
Item::list(vec![Item::int(3)]),
Item::int(2),
Item::int(1),
]);
let substitute = Item::int(4);
let pattern = Item::list(vec![]);
test_state.code_stack.push(pattern);
test_state.code_stack.push(substitute);
test_state.code_stack.push(target_item);
code_subst(&mut test_state, &icache());
assert_eq!(
test_state.code_stack.to_string(),
"( 1 2 ( 3 ) 4 )"
);
}
#[test]
fn code_swaps_top_elements() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(0));
test_state.code_stack.push(Item::int(1));
assert_eq!(
test_state.code_stack.to_string(),
"1 0"
);
code_swap(&mut test_state, &icache());
assert_eq!(
test_state.code_stack.to_string(),
"0 1"
);
}
#[test]
fn code_yank_brings_item_to_top() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(5));
test_state.code_stack.push(Item::int(4));
test_state.code_stack.push(Item::int(3));
test_state.code_stack.push(Item::int(2));
test_state.code_stack.push(Item::int(1));
assert_eq!(
test_state.code_stack.to_string(),
"1 2 3 4 5"
);
test_state.int_stack.push(3);
code_yank(&mut test_state, &icache());
assert_eq!(
test_state.code_stack.to_string(),
"4 1 2 3 5"
);
}
#[test]
fn code_yank_dup_copies_item_to_top() {
let mut test_state = PushState::new();
test_state.code_stack.push(Item::int(5));
test_state.code_stack.push(Item::int(4));
test_state.code_stack.push(Item::int(3));
test_state.code_stack.push(Item::int(2));
test_state.code_stack.push(Item::int(1));
assert_eq!(
test_state.code_stack.to_string(),
"1 2 3 4 5"
);
test_state.int_stack.push(3);
code_yank_dup(&mut test_state, &icache());
assert_eq!(
test_state.code_stack.to_string(),
"4 1 2 3 4 5"
);
}
}