use std::borrow::Borrow;
use std::fmt::Debug;
use std::rc::Rc;
use std::string::String;
use chinillaclvm_rs::allocator::{Allocator, AtomBuf, NodePtr, SExp};
use chinillaclvm_rs::reduction::EvalErr;
use bls12_381::G1Affine;
use crate::classic::chinillaclvm::__type_compatibility__::{Bytes, BytesFromType, Stream};
use crate::classic::chinillaclvm::serialize::sexp_to_stream;
use crate::util::{u8_from_number, Number};
#[derive(Debug)]
pub enum CastableType {
CHINILLACLVMObject(NodePtr),
Bytes(Bytes),
String(String),
Number(Number),
G1Affine(G1Affine),
ListOf(usize, Vec<Rc<CastableType>>),
TupleOf(Rc<CastableType>, Rc<CastableType>),
}
#[derive(Debug)]
pub enum SexpStackOp {
OpConvert,
OpSetPair(bool, usize),
OpPrepend(usize),
}
pub fn to_sexp_type(allocator: &mut Allocator, value: CastableType) -> Result<NodePtr, EvalErr> {
let mut stack = vec![Rc::new(value)];
let mut ops: Vec<SexpStackOp> = vec![SexpStackOp::OpConvert];
loop {
let op = match ops.pop() {
None => {
break;
}
Some(o) => o,
};
let top = match stack.pop() {
None => {
return Err(EvalErr(allocator.null(), "empty value stack".to_string()));
}
Some(rc) => rc,
};
match op {
SexpStackOp::OpConvert => {
match top.borrow() {
CastableType::CHINILLACLVMObject(_) => {
stack.push(top.clone());
}
CastableType::TupleOf(left, right) => {
let target_index = stack.len();
match allocator.new_pair(allocator.null(), allocator.null()) {
Ok(pair) => {
stack.push(Rc::new(CastableType::CHINILLACLVMObject(pair)));
}
Err(e) => {
return Err(e);
}
};
stack.push(right.clone());
ops.push(SexpStackOp::OpSetPair(true, target_index)); ops.push(SexpStackOp::OpConvert);
stack.push(left.clone());
ops.push(SexpStackOp::OpSetPair(false, target_index));
ops.push(SexpStackOp::OpConvert);
}
CastableType::ListOf(_sel, v) => {
let target_index = stack.len();
stack.push(Rc::new(CastableType::CHINILLACLVMObject(allocator.null())));
for vi in v.iter().take(v.len() - 1) {
stack.push(vi.clone());
ops.push(SexpStackOp::OpPrepend(target_index));
ops.push(SexpStackOp::OpConvert);
}
}
CastableType::Bytes(b) => match allocator.new_atom(b.data()) {
Ok(a) => {
stack.push(Rc::new(CastableType::CHINILLACLVMObject(a)));
}
Err(e) => {
return Err(e);
}
},
CastableType::String(s) => {
match allocator.new_atom(s.as_bytes()) {
Ok(a) => {
stack.push(Rc::new(CastableType::CHINILLACLVMObject(a)));
}
Err(e) => {
return Err(e);
}
};
}
CastableType::Number(n) => {
match allocator.new_atom(&u8_from_number(n.clone())) {
Ok(a) => {
stack.push(Rc::new(CastableType::CHINILLACLVMObject(a)));
}
Err(e) => {
return Err(e);
}
}
}
CastableType::G1Affine(g) => {
let bytes_ver = Bytes::new(Some(BytesFromType::G1Element(*g)));
match allocator.new_atom(bytes_ver.data()) {
Ok(a) => {
stack.push(Rc::new(CastableType::CHINILLACLVMObject(a)));
}
Err(e) => {
return Err(e);
}
}
}
}
}
SexpStackOp::OpSetPair(toset, target) => match top.borrow() {
CastableType::CHINILLACLVMObject(new_value) => match stack[target].borrow() {
CastableType::CHINILLACLVMObject(target_value) => {
match allocator.sexp(*target_value) {
SExp::Pair(l, r) => {
if toset {
match allocator.new_pair(l, *new_value) {
Ok(pair) => {
stack[target] =
Rc::new(CastableType::CHINILLACLVMObject(pair));
}
Err(e) => {
return Err(e);
}
}
} else {
match allocator.new_pair(*new_value, r) {
Ok(pair) => {
stack[target] =
Rc::new(CastableType::CHINILLACLVMObject(pair));
}
Err(e) => {
return Err(e);
}
}
}
}
SExp::Atom(_) => {
return Err(EvalErr(
*target_value,
"attempt to set_pair in atom".to_string(),
));
}
}
}
_ => {
return Err(EvalErr(
allocator.null(),
format!("Setting wing of non pair {:?}", stack[target]),
));
}
},
_ => {
return Err(EvalErr(
allocator.null(),
format!(
"op_set_pair on atom item {:?} in vec {:?} ops {:?}",
target, stack, ops
),
));
}
},
SexpStackOp::OpPrepend(target) => match top.borrow() {
CastableType::CHINILLACLVMObject(f) => match stack[target].borrow() {
CastableType::CHINILLACLVMObject(o) => match allocator.new_pair(*f, *o) {
Ok(pair) => {
stack[target] = Rc::new(CastableType::CHINILLACLVMObject(pair));
}
Err(e) => {
return Err(e);
}
},
_ => {
return Err(EvalErr(
allocator.null(),
format!("unrealized pair prepended {:?}", stack[target]),
));
}
},
_ => {
return Err(EvalErr(
allocator.null(),
format!("unrealized prepend {:?}", top),
));
}
},
}
}
if stack.len() != 1 {
return Err(EvalErr(
allocator.null(),
format!("too many values left on op stack {:?}", stack),
));
}
return match stack.pop() {
None => Err(EvalErr(allocator.null(), "stack empty".to_string())),
Some(top) => match top.borrow() {
CastableType::CHINILLACLVMObject(o) => Ok(*o),
_ => Err(EvalErr(
allocator.null(),
format!("unimplemented {:?}", stack[0]),
)),
},
};
}
pub fn sexp_as_bin(allocator: &mut Allocator, sexp: NodePtr) -> Bytes {
let mut f = Stream::new(None);
sexp_to_stream(allocator, sexp, &mut f);
f.get_value()
}
pub fn bool_sexp(allocator: &mut Allocator, b: bool) -> NodePtr {
if b {
allocator.one()
} else {
allocator.null()
}
}
pub fn non_nil(allocator: &mut Allocator, sexp: NodePtr) -> bool {
match allocator.sexp(sexp) {
SExp::Pair(_, _) => true,
SExp::Atom(b) => !b.is_empty(),
}
}
pub fn first(allocator: &mut Allocator, sexp: NodePtr) -> Result<NodePtr, EvalErr> {
match allocator.sexp(sexp) {
SExp::Pair(f, _) => Ok(f),
_ => Err(EvalErr(sexp, "first of non-cons".to_string())),
}
}
pub fn rest(allocator: &mut Allocator, sexp: NodePtr) -> Result<NodePtr, EvalErr> {
match allocator.sexp(sexp) {
SExp::Pair(_, r) => Ok(r),
_ => Err(EvalErr(sexp, "rest of non-cons".to_string())),
}
}
pub fn atom(allocator: &mut Allocator, sexp: NodePtr) -> Result<AtomBuf, EvalErr> {
match allocator.sexp(sexp) {
SExp::Atom(abuf) => Ok(abuf),
_ => Err(EvalErr(sexp, "not an atom".to_string())),
}
}
pub fn proper_list(allocator: &mut Allocator, sexp: NodePtr, store: bool) -> Option<Vec<NodePtr>> {
let mut args = vec![];
let mut args_sexp = sexp;
loop {
match allocator.sexp(args_sexp) {
SExp::Atom(_) => {
if !non_nil(allocator, args_sexp) {
return Some(args);
} else {
return None;
}
}
SExp::Pair(f, r) => {
if store {
args.push(f);
}
args_sexp = r;
}
}
}
}
pub fn enlist(allocator: &mut Allocator, vec: &[NodePtr]) -> Result<NodePtr, EvalErr> {
let mut built = allocator.null();
for i_reverse in 0..vec.len() {
let i = vec.len() - i_reverse - 1;
match allocator.new_pair(vec[i], built) {
Err(e) => return Err(e),
Ok(v) => {
built = v;
}
}
}
Ok(built)
}
pub fn map_m<T>(
allocator: &mut Allocator,
iter: &mut impl Iterator<Item = T>,
f: &dyn Fn(&mut Allocator, T) -> Result<NodePtr, EvalErr>,
) -> Result<Vec<NodePtr>, EvalErr> {
let mut result = Vec::new();
loop {
match iter.next() {
None => {
return Ok(result);
}
Some(v) => match f(allocator, v) {
Err(e) => {
return Err(e);
}
Ok(v) => {
result.push(v);
}
},
}
}
}
pub fn fold_m<A, B, E>(
allocator: &mut Allocator,
f: &dyn Fn(&mut Allocator, A, B) -> Result<A, E>,
start_: A,
iter: &mut impl Iterator<Item = B>,
) -> Result<A, E> {
let mut start = start_;
loop {
match iter.next() {
None => {
return Ok(start);
}
Some(v) => match f(allocator, start, v) {
Err(e) => {
return Err(e);
}
Ok(v) => {
start = v;
}
},
}
}
}
pub fn equal_to(allocator: &mut Allocator, first_: NodePtr, second_: NodePtr) -> bool {
let mut first = first_;
let mut second = second_;
loop {
match (allocator.sexp(first), allocator.sexp(second)) {
(SExp::Atom(fbuf), SExp::Atom(sbuf)) => {
let fvec = allocator.buf(&fbuf).to_vec();
let svec = allocator.buf(&sbuf).to_vec();
return fvec == svec;
}
(SExp::Pair(ff, fr), SExp::Pair(rf, rr)) => {
if !equal_to(allocator, ff, rf) {
return false;
}
first = fr;
second = rr;
}
_ => {
return false;
}
}
}
}
pub fn flatten(allocator: &mut Allocator, tree_: NodePtr, res: &mut Vec<NodePtr>) {
let mut tree = tree_;
loop {
match allocator.sexp(tree) {
SExp::Atom(_) => {
if non_nil(allocator, tree) {
res.push(tree);
}
return;
}
SExp::Pair(l, r) => {
flatten(allocator, l, res);
tree = r;
}
}
}
}