use crate::push::instructions::Instruction;
use crate::push::instructions::InstructionCache;
use crate::push::item::Item;
use crate::push::state::PushState;
use crate::push::state::*;
use std::collections::HashMap;
use std::process::Command;
use std::{thread, time::Duration};
pub fn load_exec_instructions(map: &mut HashMap<String, Instruction>) {
map.insert(String::from("EXEC.="), Instruction::new(exec_eq));
map.insert(String::from("EXEC.CMD"), Instruction::new(exec_cmd));
map.insert(String::from("EXEC.DEFINE"), Instruction::new(exec_define));
map.insert(String::from("EXEC.LOOP"), Instruction::new(exec_loop));
map.insert(String::from("EXEC.DUP"), Instruction::new(exec_dup));
map.insert(String::from("EXEC.FLUSH"), Instruction::new(exec_flush));
map.insert(String::from("EXEC.ID"), Instruction::new(exec_id));
map.insert(String::from("EXEC.IF"), Instruction::new(exec_if));
map.insert(String::from("EXEC.K"), Instruction::new(exec_k));
map.insert(String::from("EXEC.POP"), Instruction::new(exec_pop));
map.insert(String::from("EXEC.ROT"), Instruction::new(exec_rot));
map.insert(String::from("EXEC.S"), Instruction::new(exec_s));
map.insert(String::from("EXEC.SHOVE"), Instruction::new(exec_shove));
map.insert(
String::from("EXEC.STACKDEPTH"),
Instruction::new(exec_stack_depth),
);
map.insert(String::from("EXEC.SWAP"), Instruction::new(exec_swap));
map.insert(String::from("EXEC.Y"), Instruction::new(exec_y));
map.insert(String::from("EXEC.YANK"), Instruction::new(exec_yank));
map.insert(
String::from("EXEC.YANKDUP"),
Instruction::new(exec_yank_dup),
);
}
pub fn exec_id(push_state: &mut PushState, _instruction_set: &InstructionCache) {
push_state.int_stack.push(EXEC_STACK_ID);
}
pub fn exec_cmd(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(num_args) = push_state.int_stack.pop() {
if num_args > -1 {
if let Some(mut nvals) = push_state.name_stack.pop_vec((num_args+1) as usize) {
let cmd = nvals.remove(0);
thread::sleep(Duration::from_millis(1000));
let mut child = Command::new(cmd).args(nvals).spawn().expect("Command failed to start");
if let Some(stdout) = child.stdout.as_mut() {
println!("{:?}", stdout);
}
}
}
}
}
pub fn exec_eq(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(pv) = push_state.exec_stack.copy_vec(2) {
push_state
.bool_stack
.push(pv[0].to_string() == pv[1].to_string());
}
}
pub fn exec_define(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(name) = push_state.name_stack.pop() {
if let Some(instruction) = push_state.exec_stack.pop() {
push_state.name_bindings.insert(name, instruction);
}
}
}
pub fn exec_loop(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(body) = push_state.exec_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("EXEC.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 exec_dup(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(instruction) = push_state.exec_stack.copy(0) {
push_state.exec_stack.push(instruction);
}
}
pub fn exec_flush(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
push_state.exec_stack.flush();
}
pub fn exec_if(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(code) = push_state.exec_stack.pop_vec(2) {
if let Some(exec_first) = push_state.bool_stack.pop() {
if exec_first {
push_state.exec_stack.push(code[1].clone());
} else {
push_state.exec_stack.push(code[0].clone());
}
}
}
}
pub fn exec_k(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(code) = push_state.exec_stack.pop_vec(2) {
push_state.exec_stack.push(code[1].clone());
}
}
pub fn exec_pop(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
push_state.exec_stack.pop();
}
pub fn exec_rot(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
push_state.exec_stack.yank(2);
}
pub fn exec_s(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(code) = push_state.exec_stack.pop_vec(3) {
let a = &code[2];
let b = &code[1];
let c = &code[0];
let bc = Item::list(vec![c.clone(), b.clone()]);
push_state.exec_stack.push(bc);
push_state.exec_stack.push(c.clone());
push_state.exec_stack.push(a.clone());
}
}
pub fn exec_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.exec_stack.size() as i32) - 1, shove_index),
0,
) as usize;
push_state.exec_stack.shove(corr_index as usize);
}
}
pub fn exec_stack_depth(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
push_state
.int_stack
.push(push_state.exec_stack.size() as i32);
}
pub fn exec_swap(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
push_state.exec_stack.shove(1);
}
pub fn exec_y(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
if let Some(top_item) = push_state.exec_stack.copy(0) {
push_state.exec_stack.push(Item::list(vec![
top_item,
Item::instruction("EXEC.Y".to_string()),
]));
push_state.exec_stack.shove(1);
}
}
pub fn exec_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.exec_stack.size() as i32) - 1, index),
0,
) as usize;
push_state.exec_stack.yank(corr_index);
}
}
pub fn exec_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.exec_stack.size() as i32) - 1, index),
0,
) as usize;
if let Some(deep_item) = push_state.exec_stack.copy(corr_index as usize) {
push_state.exec_stack.push(deep_item);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::push::index::Index;
pub fn icache() -> InstructionCache {
InstructionCache::new(vec![])
}
#[test]
fn exec_eq_pushes_true_when_elements_equal() {
let mut test_state = PushState::new();
test_state.exec_stack.push(Item::int(1));
test_state.exec_stack.push(Item::int(1));
exec_eq(&mut test_state, &icache());
assert_eq!(test_state.exec_stack.size(), 2);
assert_eq!(test_state.bool_stack.to_string(), "TRUE");
}
#[test]
fn exec_eq_pushes_false_when_elements_unequal() {
let mut test_state = PushState::new();
test_state.exec_stack.push(Item::int(1));
test_state.exec_stack.push(Item::int(2));
exec_eq(&mut test_state, &icache());
assert_eq!(test_state.exec_stack.size(), 2);
assert_eq!(test_state.bool_stack.to_string(), "FALSE");
}
#[test]
fn exec_define_creates_name_binding() {
let mut test_state = PushState::new();
test_state.exec_stack.push(Item::int(2));
test_state.name_stack.push(String::from("TEST"));
exec_define(&mut test_state, &icache());
assert_eq!(
*test_state.name_bindings.get("TEST").unwrap().to_string(),
Item::int(2).to_string()
);
}
#[test]
fn exec_loop_pushes_body_and_updated_loop() {
let mut test_state = PushState::new();
test_state.exec_stack.push(Item::noop());
test_state.index_stack.push(Index::new(3));
exec_loop(&mut test_state, &icache());
assert_eq!(test_state.exec_stack.to_string(), "NOOP ( INDEX.INCREASE EXEC.LOOP NOOP )");
}
#[test]
fn exec_loop_removes_index_when_terminated() {
let mut test_state = PushState::new();
test_state.exec_stack.push(Item::noop());
let mut test_index = Index::new(3);
test_index.current = 3;
test_state.index_stack.push(test_index);
exec_loop(&mut test_state, &icache());
assert_eq!(test_state.index_stack.to_string(), "");
assert_eq!(test_state.exec_stack.to_string(), "");
}
#[test]
fn exec_dup_duplicates_top_element() {
let mut test_state = PushState::new();
test_state.exec_stack.push(Item::noop());
exec_dup(&mut test_state, &icache());
assert_eq!(
test_state.exec_stack.to_string(),
"NOOP NOOP"
);
}
#[test]
fn exec_flush_empties_stack() {
let mut test_state = PushState::new();
test_state
.exec_stack
.push(Item::list(vec![Item::int(0), Item::int(2)]));
test_state
.exec_stack
.push(Item::list(vec![Item::int(1), Item::int(2)]));
exec_flush(&mut test_state, &icache());
assert_eq!(test_state.int_stack.to_string(), "");
}
#[test]
fn exec_if_pushes_first_item_when_true() {
let mut test_state = PushState::new();
test_state.bool_stack.push(true);
test_state.exec_stack.push(Item::int(2));
test_state.exec_stack.push(Item::int(1));
exec_if(&mut test_state, &icache());
assert_eq!(test_state.exec_stack.to_string(), "1");
assert_eq!(test_state.bool_stack.to_string(), "");
}
#[test]
fn exec_if_pushes_second_item_when_false() {
let mut test_state = PushState::new();
test_state.bool_stack.push(false);
test_state.exec_stack.push(Item::int(2));
test_state.exec_stack.push(Item::int(1));
exec_if(&mut test_state, &icache());
assert_eq!(test_state.exec_stack.to_string(), "2");
assert_eq!(test_state.bool_stack.to_string(), "");
}
#[test]
fn exec_k_removes_second_item() {
let mut test_state = PushState::new();
test_state.bool_stack.push(false);
test_state.exec_stack.push(Item::int(2));
test_state.exec_stack.push(Item::int(1));
exec_k(&mut test_state, &icache());
assert_eq!(test_state.exec_stack.to_string(), "1");
}
#[test]
fn exec_pop_removes_first_item() {
let mut test_state = PushState::new();
test_state.bool_stack.push(false);
test_state.exec_stack.push(Item::int(2));
test_state.exec_stack.push(Item::int(1));
exec_pop(&mut test_state, &icache());
assert_eq!(test_state.exec_stack.to_string(), "2");
}
#[test]
fn exec_rot_shuffles_elements() {
let mut test_state = PushState::new();
test_state.exec_stack.push(Item::int(3));
test_state.exec_stack.push(Item::int(2));
test_state.exec_stack.push(Item::int(1));
assert_eq!(
test_state.exec_stack.to_string(),
"1 2 3"
);
exec_rot(&mut test_state, &icache());
assert_eq!(
test_state.exec_stack.to_string(),
"3 1 2"
);
}
#[test]
fn exec_s_pushes_elements_in_right_order() {
let mut test_state = PushState::new();
test_state.exec_stack.push(Item::int(3));
test_state.exec_stack.push(Item::int(2));
test_state.exec_stack.push(Item::int(1));
assert_eq!(
test_state.exec_stack.to_string(),
"1 2 3"
);
exec_s(&mut test_state, &icache());
assert_eq!(
test_state.exec_stack.to_string(),
"1 3 ( 2 3 )"
);
}
#[test]
fn exec_shove_inserts_at_right_position() {
let mut test_state = PushState::new();
test_state.exec_stack.push(Item::int(4));
test_state.exec_stack.push(Item::int(3));
test_state.exec_stack.push(Item::int(2));
test_state.exec_stack.push(Item::int(1));
assert_eq!(
test_state.exec_stack.to_string(),
"1 2 3 4"
);
test_state.int_stack.push(2);
exec_shove(&mut test_state, &icache());
assert_eq!(
test_state.exec_stack.to_string(),
"2 3 1 4"
);
}
#[test]
fn exec_stack_depth_pushes_size() {
let mut test_state = PushState::new();
test_state
.exec_stack
.push(Item::list(vec![Item::int(0), Item::int(2)]));
test_state
.exec_stack
.push(Item::list(vec![Item::int(1), Item::int(2)]));
exec_stack_depth(&mut test_state, &icache());
assert_eq!(test_state.int_stack.to_string(), "2");
}
#[test]
fn exec_swaps_top_elements() {
let mut test_state = PushState::new();
test_state.exec_stack.push(Item::int(0));
test_state.exec_stack.push(Item::int(1));
exec_swap(&mut test_state, &icache());
assert_eq!(
test_state.exec_stack.to_string(),
"0 1"
);
}
#[test]
fn exec_y_inserts_y_copy_beneath_top_element() {
let mut test_state = PushState::new();
test_state.exec_stack.push(Item::int(0));
exec_y(&mut test_state, &icache());
assert_eq!(
test_state.exec_stack.to_string(),
"0 ( EXEC.Y 0 )"
);
}
#[test]
fn exec_yank_brings_item_to_top() {
let mut test_state = PushState::new();
test_state.exec_stack.push(Item::int(5));
test_state.exec_stack.push(Item::int(4));
test_state.exec_stack.push(Item::int(3));
test_state.exec_stack.push(Item::int(2));
test_state.exec_stack.push(Item::int(1));
assert_eq!(
test_state.exec_stack.to_string(),
"1 2 3 4 5"
);
test_state.int_stack.push(3);
exec_yank(&mut test_state, &icache());
assert_eq!(
test_state.exec_stack.to_string(),
"4 1 2 3 5"
);
}
#[test]
fn exec_yank_dup_copies_item_to_top() {
let mut test_state = PushState::new();
test_state.exec_stack.push(Item::int(5));
test_state.exec_stack.push(Item::int(4));
test_state.exec_stack.push(Item::int(3));
test_state.exec_stack.push(Item::int(2));
test_state.exec_stack.push(Item::int(1));
assert_eq!(
test_state.exec_stack.to_string(),
"1 2 3 4 5"
);
test_state.int_stack.push(3);
exec_yank_dup(&mut test_state, &icache());
assert_eq!(
test_state.exec_stack.to_string(),
"4 1 2 3 4 5"
);
}
}