|
|
|
use std::collections::{HashMap, VecDeque};
|
|
|
|
|
|
|
|
use crsn::asm::data::literal::Value;
|
|
|
|
use crsn::asm::instr::cond::Flag;
|
|
|
|
use crsn::module::{EvalRes, OpTrait};
|
|
|
|
use crsn::runtime::fault::Fault;
|
|
|
|
use crsn::runtime::run_thread::{state::RunState, ThreadInfo};
|
|
|
|
use crsn::sexp;
|
|
|
|
use crsn::sexp::{atom_qs, Sexp};
|
|
|
|
use crsn::utils::A;
|
|
|
|
|
|
|
|
use crate::defs::{BufIoMode, BufOps, BufValue};
|
|
|
|
|
|
|
|
#[derive(Debug, Default, Clone)]
|
|
|
|
struct Buffer {
|
|
|
|
data: VecDeque<Value>,
|
|
|
|
iomode: BufIoMode,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
struct ExtData {
|
|
|
|
buffers: HashMap<Value, Buffer>,
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO optimize and DRY
|
|
|
|
|
|
|
|
impl OpTrait for BufOps {
|
|
|
|
fn execute(&self, info: &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault> {
|
|
|
|
let eres = EvalRes::default();
|
|
|
|
match self {
|
|
|
|
BufOps::New { dst, value } => {
|
|
|
|
let id = info.unique_handle();
|
|
|
|
|
|
|
|
let que = match value {
|
|
|
|
BufValue::Zeros(len) => {
|
|
|
|
let len = state.read(len)? as usize;
|
|
|
|
let mut que = VecDeque::with_capacity(len);
|
|
|
|
que.resize(len, 0);
|
|
|
|
que
|
|
|
|
}
|
|
|
|
BufValue::Values(vals) => {
|
|
|
|
let mut que = VecDeque::with_capacity(vals.len());
|
|
|
|
for val in vals {
|
|
|
|
que.push_back(state.read(val)?);
|
|
|
|
}
|
|
|
|
que
|
|
|
|
}
|
|
|
|
BufValue::Chars(string) => {
|
|
|
|
let mut que = VecDeque::new();
|
|
|
|
for val in string.chars() {
|
|
|
|
que.push_back(val as Value);
|
|
|
|
}
|
|
|
|
que
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let data: &mut ExtData = state.ext_mut();
|
|
|
|
data.buffers.insert(id, Buffer {
|
|
|
|
data: que,
|
|
|
|
iomode: Default::default()
|
|
|
|
});
|
|
|
|
state.write(dst, id)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::SetIoMode { obj, mode } => {
|
|
|
|
state.clear_status();
|
|
|
|
let handle = state.read_obj(obj)?;
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
|
|
|
|
|
|
|
|
buf.iomode = *mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::Push { obj, src } => {
|
|
|
|
state.clear_status();
|
|
|
|
let val = state.read(src)?;
|
|
|
|
let handle = state.read_obj(obj)?;
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
|
|
|
|
buf.data.push_back(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::Pop { dst, obj } => {
|
|
|
|
state.clear_status();
|
|
|
|
let handle = state.read_obj(obj)?;
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
|
|
|
|
if let Some(val) = buf.data.pop_back() {
|
|
|
|
let empty = buf.data.is_empty();
|
|
|
|
state.write(dst, val)?;
|
|
|
|
state.update_status(val);
|
|
|
|
state.set_flag(Flag::Empty, empty);
|
|
|
|
} else {
|
|
|
|
state.set_flag(Flag::Empty, true);
|
|
|
|
state.set_flag(Flag::Overflow, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::Read { dst, obj, idx } => {
|
|
|
|
state.clear_status();
|
|
|
|
let handle = state.read_obj(obj)?;
|
|
|
|
let idx = state.read(idx)?;
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
let buf = store.buffers.get(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
|
|
|
|
if let Some(val) = buf.data.get(idx as usize) {
|
|
|
|
let val = *val;
|
|
|
|
state.update_status(val);
|
|
|
|
state.write(dst, val)?;
|
|
|
|
} else {
|
|
|
|
state.set_flag(Flag::Overflow, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::GetLen { dst, obj } => {
|
|
|
|
state.clear_status();
|
|
|
|
let handle = state.read_obj(obj)?;
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
let buf = store.buffers.get(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
|
|
|
|
let val = buf.data.len() as Value;
|
|
|
|
state.write(dst, val)?;
|
|
|
|
state.update_status(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::Write { obj, idx, value } => {
|
|
|
|
state.clear_status();
|
|
|
|
let handle = state.read_obj(obj)?;
|
|
|
|
let val = state.read(value)?;
|
|
|
|
let idx = state.read(idx)? as usize;
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
|
|
|
|
if idx < buf.data.len() {
|
|
|
|
buf.data[idx] = val;
|
|
|
|
} else if idx == buf.data.len() {
|
|
|
|
buf.data.push_back(val);
|
|
|
|
} else {
|
|
|
|
state.set_flag(Flag::Overflow, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::Insert { obj, idx, value } => {
|
|
|
|
state.clear_status();
|
|
|
|
let handle = state.read_obj(obj)?;
|
|
|
|
let val = state.read(value)?;
|
|
|
|
let idx = state.read(idx)? as usize;
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
|
|
|
|
if idx < buf.data.len() {
|
|
|
|
buf.data.insert(idx, val);
|
|
|
|
} else if idx == buf.data.len() {
|
|
|
|
buf.data.push_back(val);
|
|
|
|
} else {
|
|
|
|
state.set_flag(Flag::Overflow, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::Remove { dst, obj, idx } => {
|
|
|
|
state.clear_status();
|
|
|
|
let handle = state.read_obj(obj)?;
|
|
|
|
let idx = state.read(idx)? as usize;
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
|
|
|
|
if idx < buf.data.len() {
|
|
|
|
let val = buf.data.remove(idx).unwrap();
|
|
|
|
let empty = buf.data.is_empty();
|
|
|
|
state.update_status(val);
|
|
|
|
state.write(dst, val)?;
|
|
|
|
state.set_flag(Flag::Empty, empty);
|
|
|
|
} else {
|
|
|
|
state.set_flag(Flag::Overflow, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::Resize { obj, len } => {
|
|
|
|
let handle = state.read_obj(obj)?;
|
|
|
|
let len = state.read(len)? as usize;
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
|
|
|
|
buf.data.resize(len, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::Reverse { obj } => {
|
|
|
|
let handle = state.read_obj(obj)?;
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
|
|
|
|
let len = buf.data.len();
|
|
|
|
if len > 1 {
|
|
|
|
let mut start = 0;
|
|
|
|
let mut end = len - 1;
|
|
|
|
while start < end {
|
|
|
|
buf.data.swap(start, end);
|
|
|
|
start += 1;
|
|
|
|
end -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::AppendBuf { obj, obj2 } => {
|
|
|
|
let handle = state.read_obj(obj)?;
|
|
|
|
let handle2 = state.read_obj(obj2)?;
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
// TODO figure out how to avoid cloning
|
|
|
|
let buf2 = store.buffers.get(&handle2).ok_or_else(|| Fault::ObjectNotExist(handle))?.data.clone();
|
|
|
|
let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
|
|
|
|
buf.data.extend(buf2);
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::PrependBuf { obj, obj2 } => {
|
|
|
|
let handle = state.read_obj(obj)?;
|
|
|
|
let handle2 = state.read_obj(obj2)?;
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
// TODO figure out how to avoid cloning
|
|
|
|
let buf2 = store.buffers.get(&handle2).ok_or_else(|| Fault::ObjectNotExist(handle))?.data.clone();
|
|
|
|
let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
|
|
|
|
for v in buf2.into_iter().rev() {
|
|
|
|
buf.data.push_front(v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(eres)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_sexp(&self) -> Sexp {
|
|
|
|
match self {
|
|
|
|
BufOps::Push { obj, src } => sexp::list(&[A("push"), A(obj), A(src)]),
|
|
|
|
BufOps::Pop { dst, obj } => sexp::list(&[A("pop"), A(dst), A(obj)]),
|
|
|
|
|
|
|
|
BufOps::New { dst, value } => {
|
|
|
|
match value {
|
|
|
|
BufValue::Zeros(n) => {
|
|
|
|
sexp::list(&[A("mkbf"), A(dst), A(n)])
|
|
|
|
}
|
|
|
|
BufValue::Values(vals) => {
|
|
|
|
let mut vals: Vec<_> = vals.iter().map(A).collect();
|
|
|
|
vals.insert(0, A(dst));
|
|
|
|
vals.insert(0, A("mkbfv"));
|
|
|
|
sexp::list(&vals)
|
|
|
|
}
|
|
|
|
BufValue::Chars(str) => {
|
|
|
|
sexp::list(&[A("mkbfv"), A(dst), atom_qs(str)])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::Read { dst, obj, idx } => {
|
|
|
|
sexp::list(&[A("bfrd"), A(dst), A(obj), A(idx)])
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::GetLen { dst, obj } => {
|
|
|
|
sexp::list(&[A("bfsz"), A(dst), A(obj)])
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::Write { obj, idx, value } => {
|
|
|
|
sexp::list(&[A("bfwr"), A(obj), A(idx), A(value)])
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::Insert { obj, idx, value } => {
|
|
|
|
sexp::list(&[A("bfins"), A(obj), A(idx), A(value)])
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::Remove { dst, obj, idx } => {
|
|
|
|
sexp::list(&[A("bfrm"), A(dst), A(obj), A(idx)])
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::Resize { obj, len } => {
|
|
|
|
sexp::list(&[A("bfrsz"), A(obj), A(len)])
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::Reverse { obj } => {
|
|
|
|
sexp::list(&[A("bfrev"), A(obj)])
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::AppendBuf { obj, obj2 } => {
|
|
|
|
sexp::list(&[A("bfapp"), A(obj), A(obj2)])
|
|
|
|
}
|
|
|
|
|
|
|
|
BufOps::PrependBuf { obj, obj2 } => {
|
|
|
|
sexp::list(&[A("bfprep"), A(obj), A(obj2)])
|
|
|
|
}
|
|
|
|
BufOps::SetIoMode { obj, mode } => {
|
|
|
|
sexp::list(&[A("bfio"), A(obj), A(mode.to_value())])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub(crate) fn drop_obj(state: &mut RunState, handle: Value) -> Result<Option<()>, Fault> {
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
Ok(store.buffers.remove(&handle).map(|_| ()))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Run-time method called to read an object (using the object handle syntax)
|
|
|
|
pub(crate) fn read_obj(state: &mut RunState, handle: Value)
|
|
|
|
-> Result<Option<Value>, Fault>
|
|
|
|
{
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
if let Some(buf) = store.buffers.get_mut(&handle) {
|
|
|
|
match match buf.iomode {
|
|
|
|
BufIoMode::Queue => buf.data.pop_front(),
|
|
|
|
BufIoMode::ReverseQueue => buf.data.pop_back(),
|
|
|
|
BufIoMode::Stack => buf.data.pop_back(),
|
|
|
|
BufIoMode::ReverseStack => buf.data.pop_front()
|
|
|
|
} {
|
|
|
|
None => {
|
|
|
|
state.set_flag(Flag::Overflow, true);
|
|
|
|
state.set_flag(Flag::Empty, true);
|
|
|
|
Ok(Some(0))
|
|
|
|
}
|
|
|
|
Some(val) => {
|
|
|
|
let em = buf.data.is_empty();
|
|
|
|
state.set_flag(Flag::Empty, em);
|
|
|
|
Ok(Some(val))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Run-time method called to write an object (using the object handle syntax)
|
|
|
|
pub(crate) fn write_obj(state: &mut RunState, handle: Value, value: Value) -> Result<Option<()>, Fault>
|
|
|
|
{
|
|
|
|
let store: &mut ExtData = state.ext_mut();
|
|
|
|
if let Some(buf) = store.buffers.get_mut(&handle) {
|
|
|
|
match buf.iomode {
|
|
|
|
BufIoMode::Queue => buf.data.push_back(value),
|
|
|
|
BufIoMode::ReverseQueue => buf.data.push_front(value),
|
|
|
|
BufIoMode::Stack => buf.data.push_back(value),
|
|
|
|
BufIoMode::ReverseStack => buf.data.push_front(value)
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Some(()))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
}
|