use std::{ops::Deref, sync::Arc};
use arcstr::{literal, ArcStr};
use imbl::Vector;
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use crate::{model::{libraries::data::ops::{data_attach, data_drop, data_drop_from, data_exists, data_from_blob, data_from_field, data_from_id, data_id, data_invalidate, data_libname, data_move, data_objs, data_to_blob, data_validate}, Data, Field, Graph, SId, INVALID_DATA_VALUE, SELF_STR_KEYWORD, SUPER_STR_KEYWORD}, runtime::{instruction::{Instruction, Instructions}, instructions::Base, proc::ProcEnv, Error, Val, ValRef, Variable}};
mod ops;
pub(self) const DATA_LIB: ArcStr = literal!("Data");
pub fn insert_data_lib(graph: &mut Graph) {
graph.insert_libfunc(data_id());
graph.insert_libfunc(data_libname());
graph.insert_libfunc(data_exists());
graph.insert_libfunc(data_objs());
graph.insert_libfunc(data_drop());
graph.insert_libfunc(data_drop_from());
graph.insert_libfunc(data_attach());
graph.insert_libfunc(data_move());
graph.insert_libfunc(data_to_blob());
graph.insert_libfunc(data_invalidate());
graph.insert_libfunc(data_validate());
graph.insert_libfunc(data_from_id());
graph.insert_libfunc(data_from_blob());
graph.insert_libfunc(data_from_field());
}
lazy_static! {
pub(self) static ref ID: Arc<dyn Instruction> = Arc::new(DataIns::Id);
pub(self) static ref TAGNAME: Arc<dyn Instruction> = Arc::new(DataIns::Tagname);
pub(self) static ref EXISTS: Arc<dyn Instruction> = Arc::new(DataIns::Exists);
pub(self) static ref OBJS: Arc<dyn Instruction> = Arc::new(DataIns::Objs);
pub(self) static ref DROP: Arc<dyn Instruction> = Arc::new(DataIns::Drop);
pub(self) static ref DROP_FROM: Arc<dyn Instruction> = Arc::new(DataIns::DropFrom);
pub(self) static ref ATTACH: Arc<dyn Instruction> = Arc::new(DataIns::Attach);
pub(self) static ref MOVE: Arc<dyn Instruction> = Arc::new(DataIns::Move);
pub(self) static ref FIELD: Arc<dyn Instruction> = Arc::new(DataIns::Field);
pub(self) static ref FROM_ID: Arc<dyn Instruction> = Arc::new(DataIns::FromId);
pub(self) static ref TO_BLOB: Arc<dyn Instruction> = Arc::new(DataIns::ToBlob);
pub(self) static ref FROM_BLOB: Arc<dyn Instruction> = Arc::new(DataIns::FromBlob);
pub(self) static ref INVALIDATE: Arc<dyn Instruction> = Arc::new(DataIns::Invalidate);
pub(self) static ref VALIDATE: Arc<dyn Instruction> = Arc::new(DataIns::Validate);
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DataIns {
Id,
Exists,
Objs,
Drop,
DropFrom,
Attach,
Move,
Tagname,
ToBlob,
Invalidate,
Validate,
Field,
FromId,
FromBlob,
}
#[typetag::serde(name = "DataIns")]
impl Instruction for DataIns {
fn exec(&self, env: &mut ProcEnv, graph: &mut Graph) -> Result<Option<Instructions>, Error> {
match self {
Self::Id => {
if let Some(var) = env.stack.pop() {
if let Some(dref) = var.try_data_or_func() {
env.stack.push(Variable::val(Val::Str(dref.as_ref().into())));
return Ok(None);
}
}
Err(Error::DataId)
},
Self::Tagname => {
if let Some(var) = env.stack.pop() {
if let Some(dref) = var.try_data_or_func() {
if let Some(tagname) = dref.tagname(&graph) {
env.stack.push(Variable::val(Val::Str(tagname.into())));
} else {
env.stack.push(Variable::val(Val::Null));
}
return Ok(None);
}
}
Err(Error::DataTagname)
},
Self::Exists => {
if let Some(var) = env.stack.pop() {
if let Some(dref) = var.try_data_or_func() {
env.stack.push(Variable::val(Val::Bool(dref.data_exists(&graph))));
return Ok(None);
}
}
Err(Error::DataExists)
},
Self::ToBlob => {
if let Some(var) = env.stack.pop() {
if let Some(dref) = var.try_data_or_func() {
if let Some(data) = dref.data(&graph) {
if let Ok(bytes) = bincode::serialize(data) {
env.stack.push(Variable::val(Val::Blob(bytes.into())));
return Ok(None);
}
}
}
}
Err(Error::DataToBlob)
},
Self::Objs => {
if let Some(var) = env.stack.pop() {
if let Some(dref) = var.try_data_or_func() {
let nodes = dref.data_nodes(&graph)
.into_iter()
.map(|nref| ValRef::new(Val::Obj(nref)))
.collect::<Vector<_>>();
env.stack.push(Variable::val(Val::List(nodes)));
return Ok(None);
}
}
Err(Error::DataObjs)
},
Self::Drop => {
if let Some(var) = env.stack.pop() {
if let Some(dref) = var.try_data_or_func() {
let dropped = graph.remove_data(&dref, None);
env.stack.push(Variable::val(Val::Bool(dropped)));
return Ok(None);
}
}
Err(Error::DataDrop)
},
Self::DropFrom => {
if let Some(from) = env.stack.pop() {
if let Some(var) = env.stack.pop() {
if let Some(dref) = var.try_data_or_func() {
if let Some(nref) = from.try_obj() {
let dropped = graph.remove_data(&dref, Some(nref));
env.stack.push(Variable::val(Val::Bool(dropped)));
return Ok(None);
}
}
}
}
Err(Error::DataDrop)
},
Self::Attach => {
if let Some(node_var) = env.stack.pop() {
if let Some(var) = env.stack.pop() {
if let Some(nref) = node_var.try_obj() {
if let Some(dref) = var.try_data_or_func() {
let attached = graph.attach_data(&nref, &dref);
env.stack.push(Variable::val(Val::Bool(attached)));
return Ok(None);
}
}
}
}
Err(Error::DataAttach)
},
Self::Move => {
if let Some(to_var) = env.stack.pop() {
if let Some(from_var) = env.stack.pop() {
if let Some(var) = env.stack.pop() {
if let Some(to_ref) = to_var.try_obj() {
if let Some(from_ref) = from_var.try_obj() {
if let Some(dref) = var.try_data_or_func() {
let mut moved = false;
if graph.attach_data(&to_ref, &dref) {
if graph.remove_data(&dref, Some(from_ref)) {
moved = true;
}
}
env.stack.push(Variable::val(Val::Bool(moved)));
return Ok(None);
}
}
}
}
}
}
Err(Error::DataMove)
},
Self::Invalidate => {
let mut symbol = INVALID_DATA_VALUE;
if let Some(symbol_var) = env.stack.pop() {
match symbol_var.val.read().deref() {
Val::Str(sym) => {
symbol = sym.clone();
},
_ => {}
}
}
if let Some(var) = env.stack.pop() {
if let Some(dref) = var.try_data_or_func() {
if let Some(data) = dref.data_mut(graph) {
let newly_inserted = data.invalidate(symbol);
env.stack.push(Variable::val(Val::Bool(newly_inserted)));
return Ok(None);
}
}
}
Err(Error::DataInvalidate)
},
Self::Validate => {
let mut symbol = None;
if let Some(symbol_var) = env.stack.pop() {
match symbol_var.val.read().deref() {
Val::Str(sym) => {
symbol = Some(sym.clone());
},
_ => {}
}
}
if let Some(var) = env.stack.pop() {
if let Some(dref) = var.try_data_or_func() {
if let Some(data) = dref.data_mut(graph) {
let was_dirty;
if let Some(symbol) = symbol {
was_dirty = data.validate(&symbol);
} else {
was_dirty = data.validate_clear();
}
env.stack.push(Variable::val(Val::Bool(was_dirty)));
return Ok(None);
}
}
}
Err(Error::DataValidate)
},
Self::FromId => {
if let Some(var) = env.stack.pop() {
match var.val.read().deref() {
Val::Str(id) => {
env.stack.push(Variable::val(Val::Data(SId::from(id.as_str()))));
return Ok(None);
},
_ => {}
}
}
Err(Error::DataFromId)
},
Self::FromBlob => {
let mut context = env.self_ptr();
if let Some(context_var) = env.stack.pop() {
match context_var.val.read().deref() {
Val::Obj(cid) => {
context = cid.clone();
},
Val::Str(path) => {
Base::LoadVariable(path.clone(), false, false).exec(env, graph)?;
if let Some(var) = env.stack.pop() {
match var.val.read().deref() {
Val::Obj(cid) => {
context = cid.clone();
},
_ => {
return Err(Error::DataFromBlob);
}
}
}
},
_ => {}
}
}
if let Some(var) = env.stack.pop() {
match var.val.read().deref() {
Val::Blob(bytes) => {
if let Ok(mut data) = bincode::deserialize::<Data>(bytes) {
if data.id.data_exists(&graph) {
data.id = SId::default();
}
data.nodes.clear();
if let Some(dref) = graph.insert_data(&context, data) {
env.stack.push(Variable::val(Val::Data(dref)));
return Ok(None);
}
}
},
_ => {}
}
}
Err(Error::DataFromBlob)
},
Self::Field => {
if let Some(var) = env.stack.pop() {
match var.val.read().deref() {
Val::Str(path) => {
let mut context = None;
if path.starts_with(SELF_STR_KEYWORD.as_str()) || path.starts_with(SUPER_STR_KEYWORD.as_str()) {
context = Some(env.self_ptr());
}
if let Some(field_ref) = Field::field_from_path(graph, path.as_str(), context) {
env.stack.push(Variable::val(Val::Data(field_ref)));
} else {
env.stack.push(Variable::val(Val::Null));
}
return Ok(None);
},
_ => {}
}
}
Err(Error::DataField)
},
}
}
}