completely changed the VData/VDataEnum system: Multiple VData can share one VDataEnum via an Arc<Mutex<VDataEnum>>. If one wants to modify the data, it copies it (Copy on Write). This copying is skipped for shared mutable data (references). This was necessary for the get_ref() function. Expect bugs!

This commit is contained in:
mark 2023-05-11 01:04:15 +02:00
parent ceddb886a9
commit 4efee9e2a2
18 changed files with 535 additions and 298 deletions

8
get_ref.mers Normal file
View File

@ -0,0 +1,8 @@
list = [1 2 3 4 5 6 7 8 9 ...]
second = &list.get_ref(2).assume1()
second.debug()
*second = 24
second.debug()
list.debug()

View File

@ -461,7 +461,7 @@ impl ByteData for VSingleType {
} }
impl ByteDataA for VData { impl ByteDataA for VData {
fn as_byte_data(&self, vec: &mut Vec<u8>) { fn as_byte_data(&self, vec: &mut Vec<u8>) {
self.data.as_byte_data(vec) self.data().0.as_byte_data(vec)
} }
} }
impl ByteData for VData { impl ByteData for VData {
@ -469,9 +469,7 @@ impl ByteData for VData {
where where
R: std::io::Read, R: std::io::Read,
{ {
Ok(Self { Ok(VDataEnum::from_byte_data(data)?.to())
data: ByteData::from_byte_data(data)?,
})
} }
} }
impl ByteDataA for VDataEnum { impl ByteDataA for VDataEnum {

View File

@ -910,8 +910,8 @@ pub mod implementation {
let args = [out].into_iter().chain(args.into_iter()).collect(); let args = [out].into_iter().chain(args.into_iter()).collect();
SStatementEnum::FunctionCall(func, args).to() SStatementEnum::FunctionCall(func, args).to()
} }
SStatementEnum::Value(vd) => match vd.data { SStatementEnum::Value(vd) => match &vd.data().0 {
VDataEnum::Int(i) => SStatementEnum::IndexFixed(out, i as _).to(), VDataEnum::Int(i) => SStatementEnum::IndexFixed(out, *i as _).to(),
_ => { _ => {
let mut context = vec![]; let mut context = vec![];
if chain_length > 0 { if chain_length > 0 {

View File

@ -76,6 +76,7 @@ pub enum BuiltinFunction {
Pop, Pop,
Remove, Remove,
Get, Get,
GetRef,
Len, Len,
// String // String
Contains, Contains,
@ -138,6 +139,7 @@ impl BuiltinFunction {
"pop" => Self::Pop, "pop" => Self::Pop,
"remove" => Self::Remove, "remove" => Self::Remove,
"get" => Self::Get, "get" => Self::Get,
"get_ref" => Self::GetRef,
"len" => Self::Len, "len" => Self::Len,
"contains" => Self::Contains, "contains" => Self::Contains,
"starts_with" => Self::StartsWith, "starts_with" => Self::StartsWith,
@ -443,7 +445,7 @@ impl BuiltinFunction {
} }
} }
// TODO! finish this // TODO! finish this
Self::Get | Self::Len => true, Self::Get | Self::GetRef | Self::Len => true,
Self::Substring => { Self::Substring => {
if input.len() >= 2 && input.len() <= 3 { if input.len() >= 2 && input.len() <= 3 {
let (s, start) = (&input[0], &input[1]); let (s, start) = (&input[0], &input[1]);
@ -598,6 +600,26 @@ impl BuiltinFunction {
unreachable!("get, pop or remove called without args") unreachable!("get, pop or remove called without args")
} }
} }
Self::GetRef => {
if let Some(v) = input.first() {
VType {
types: vec![
VSingleType::Tuple(vec![]),
VSingleType::Tuple(vec![{
let mut v = v.get_any(info).expect("cannot use get on this type");
v.types = v
.types
.into_iter()
.map(|v| VSingleType::Reference(Box::new(v)))
.collect();
v
}]),
],
}
} else {
unreachable!("get, pop or remove called without args")
}
}
Self::Exit => VType { types: vec![] }, // doesn't return Self::Exit => VType { types: vec![] }, // doesn't return
Self::FsList => VType { Self::FsList => VType {
types: vec![ types: vec![
@ -726,67 +748,87 @@ impl BuiltinFunction {
}, },
} }
} }
pub fn run( pub fn run(&self, args: &Vec<RStatement>, vars: &mut Vec<VData>, info: &GSInfo) -> VData {
&self,
args: &Vec<RStatement>,
vars: &Vec<Arc<Mutex<VData>>>,
info: &GSInfo,
) -> VData {
match self { match self {
Self::Assume1 => match args[0].run(vars, info).data { Self::Assume1 => {
VDataEnum::Tuple(mut v) => { let mut a0 = args[0].run(vars, info);
a0.make_mut();
let o = match &mut a0.data.lock().unwrap().0 {
VDataEnum::Tuple(v) => Some({
if let Some(v) = v.pop() { if let Some(v) = v.pop() {
v v
} else { } else {
panic!( let msg = if args.len() > 1 {
"ASSUMPTION FAILED: assume1 :: {}", let a1 = args[1].run(vars, info);
if args.len() > 1 { let a1 = a1.data();
if let VDataEnum::String(v) = args[1].run(vars, info).data { if let VDataEnum::String(v) = &a1.0 {
v Some(v.to_owned())
} else { } else {
String::new() None
} }
} else { } else {
String::new() None
}, };
); if let Some(m) = msg {
panic!("ASSUMPTION FAILED: assume1 :: {m}");
} else {
panic!("ASSUMPTION FAILED: assume1");
}
}
}),
_ => None,
};
if let Some(o) = o {
o
} else {
a0
} }
} }
v => v.to(),
},
Self::AssumeNoEnum => { Self::AssumeNoEnum => {
let data = args[0].run(vars, info); let msg = if args.len() > 1 {
match data.data { if let VDataEnum::String(v) = args[1].run(vars, info).inner() {
VDataEnum::EnumVariant(..) => panic!( Some(v.to_owned())
"ASSUMPTION FAILED: assume_no_enum :: found {} :: {}", } else {
data.gsi(info.clone()), None
if args.len() > 1 { }
if let VDataEnum::String(v) = args[1].run(vars, info).data { } else {
v None
};
let a0 = args[0].run(vars, info);
let a0d = a0.data();
match &a0d.0 {
VDataEnum::EnumVariant(..) => {
drop(a0d);
panic!(
"ASSUMPTION FAILED: assume_no_enum :: found {}{}",
a0.gsi(info.clone()),
if let Some(m) = msg {
format!(" :: {m}")
} else { } else {
String::new() String::new()
} }
} else { )
String::new() }
_ => {
drop(a0d);
a0
} }
),
d => d.to(),
} }
} }
Self::NoEnum => args[0].run(vars, info).noenum(), Self::NoEnum => args[0].run(vars, info).noenum(),
Self::Matches => match args[0].run(vars, info).data.matches() { Self::Matches => match args[0].run(vars, info).inner().matches() {
Some(v) => VDataEnum::Tuple(vec![v]).to(), Some(v) => VDataEnum::Tuple(vec![v]).to(),
None => VDataEnum::Tuple(vec![]).to(), None => VDataEnum::Tuple(vec![]).to(),
}, },
Self::Clone => { Self::Clone => {
if let VDataEnum::Reference(r) = args[0].run(vars, info).data { if let VDataEnum::Reference(r) = &args[0].run(vars, info).data().0 {
r.lock().unwrap().clone() r.clone()
} else { } else {
unreachable!() unreachable!()
} }
} }
BuiltinFunction::Print => { BuiltinFunction::Print => {
if let VDataEnum::String(arg) = args[0].run(vars, info).data { if let VDataEnum::String(arg) = &args[0].run(vars, info).data().0 {
print!("{}", arg); print!("{}", arg);
VDataEnum::Tuple(vec![]).to() VDataEnum::Tuple(vec![]).to()
} else { } else {
@ -794,7 +836,7 @@ impl BuiltinFunction {
} }
} }
BuiltinFunction::Println => { BuiltinFunction::Println => {
if let VDataEnum::String(arg) = args[0].run(vars, info).data { if let VDataEnum::String(arg) = &args[0].run(vars, info).data().0 {
#[cfg(not(feature = "nushell_plugin"))] #[cfg(not(feature = "nushell_plugin"))]
println!("{}", arg); println!("{}", arg);
#[cfg(feature = "nushell_plugin")] #[cfg(feature = "nushell_plugin")]
@ -831,13 +873,13 @@ impl BuiltinFunction {
VDataEnum::String(args[0].run(vars, info).gsi(info.clone()).to_string()).to() VDataEnum::String(args[0].run(vars, info).gsi(info.clone()).to_string()).to()
} }
BuiltinFunction::Format => { BuiltinFunction::Format => {
if let VDataEnum::String(mut text) = args.first().unwrap().run(vars, info).data { if let VDataEnum::String(mut text) = args.first().unwrap().run(vars, info).inner() {
for (i, arg) in args.iter().skip(1).enumerate() { for (i, arg) in args.iter().skip(1).enumerate() {
text = text.replace( text = text.replace(
&format!("{{{i}}}"), &format!("{{{i}}}"),
&format!( &format!(
"{}", "{}",
if let VDataEnum::String(v) = arg.run(vars, info).data { if let VDataEnum::String(v) = &arg.run(vars, info).data().0 {
v v
} else { } else {
unreachable!() unreachable!()
@ -852,7 +894,7 @@ impl BuiltinFunction {
} }
BuiltinFunction::ParseInt => { BuiltinFunction::ParseInt => {
if args.len() == 1 { if args.len() == 1 {
if let VDataEnum::String(s) = args[0].run(vars, info).data { if let VDataEnum::String(s) = &args[0].run(vars, info).data().0 {
if let Ok(s) = s.parse() { if let Ok(s) = s.parse() {
VDataEnum::Int(s).to() VDataEnum::Int(s).to()
} else { } else {
@ -867,7 +909,7 @@ impl BuiltinFunction {
} }
BuiltinFunction::ParseFloat => { BuiltinFunction::ParseFloat => {
if args.len() == 1 { if args.len() == 1 {
if let VDataEnum::String(s) = args[0].run(vars, info).data { if let VDataEnum::String(s) = &args[0].run(vars, info).data().0 {
if let Ok(s) = s.parse() { if let Ok(s) = s.parse() {
VDataEnum::Float(s).to() VDataEnum::Float(s).to()
} else { } else {
@ -882,13 +924,13 @@ impl BuiltinFunction {
} }
BuiltinFunction::Run => { BuiltinFunction::Run => {
if args.len() >= 1 { if args.len() >= 1 {
if let VDataEnum::Function(f) = args[0].run(vars, info).data { if let VDataEnum::Function(f) = &args[0].run(vars, info).data().0 {
if f.inputs.len() != args.len() - 1 { if f.inputs.len() != args.len() - 1 {
unreachable!() unreachable!()
} }
for (i, var) in f.inputs.iter().enumerate() { for (i, var) in f.inputs.iter().enumerate() {
let val = args[i + 1].run(vars, info); let val = args[i + 1].run(vars, info);
*vars[*var].lock().unwrap() = val; vars[*var] = val;
} }
f.run(vars, info) f.run(vars, info)
} else { } else {
@ -900,7 +942,7 @@ impl BuiltinFunction {
} }
BuiltinFunction::Thread => { BuiltinFunction::Thread => {
if args.len() >= 1 { if args.len() >= 1 {
if let VDataEnum::Function(f) = args[0].run(vars, info).data { if let VDataEnum::Function(f) = args[0].run(vars, info).inner() {
if f.inputs.len() != args.len() - 1 { if f.inputs.len() != args.len() - 1 {
unreachable!() unreachable!()
} }
@ -910,13 +952,13 @@ impl BuiltinFunction {
for (i, var) in f.inputs.iter().enumerate() { for (i, var) in f.inputs.iter().enumerate() {
let val = args[i + 1].run(vars, info); let val = args[i + 1].run(vars, info);
run_input_types.push(val.out_single()); run_input_types.push(val.out_single());
thread_vars[*var] = Arc::new(Mutex::new(val)); thread_vars[*var] = val;
} }
let out_type = f.out(&run_input_types); let out_type = f.out(&run_input_types);
let libs = info.clone(); let libs = info.clone();
VDataEnum::Thread( VDataEnum::Thread(
VDataThreadEnum::Running(std::thread::spawn(move || { VDataThreadEnum::Running(std::thread::spawn(move || {
f.run(&thread_vars, &libs) f.run(&mut thread_vars, &libs)
})) }))
.to(), .to(),
out_type, out_type,
@ -931,7 +973,7 @@ impl BuiltinFunction {
} }
BuiltinFunction::Await => { BuiltinFunction::Await => {
if args.len() == 1 { if args.len() == 1 {
if let VDataEnum::Thread(t, _) = args[0].run(vars, info).data { if let VDataEnum::Thread(t, _) = &args[0].run(vars, info).data().0 {
t.get() t.get()
} else { } else {
unreachable!() unreachable!()
@ -942,9 +984,9 @@ impl BuiltinFunction {
} }
BuiltinFunction::Sleep => { BuiltinFunction::Sleep => {
if args.len() == 1 { if args.len() == 1 {
match args[0].run(vars, info).data { match &args[0].run(vars, info).data().0 {
VDataEnum::Int(v) => std::thread::sleep(Duration::from_secs(v as _)), VDataEnum::Int(v) => std::thread::sleep(Duration::from_secs(*v as _)),
VDataEnum::Float(v) => std::thread::sleep(Duration::from_secs_f64(v)), VDataEnum::Float(v) => std::thread::sleep(Duration::from_secs_f64(*v)),
_ => unreachable!(), _ => unreachable!(),
} }
VDataEnum::Tuple(vec![]).to() VDataEnum::Tuple(vec![]).to()
@ -954,8 +996,8 @@ impl BuiltinFunction {
} }
Self::Exit => { Self::Exit => {
if let Some(s) = args.first() { if let Some(s) = args.first() {
if let VDataEnum::Int(v) = s.run(vars, info).data { if let VDataEnum::Int(v) = &s.run(vars, info).data().0 {
std::process::exit(v as _); std::process::exit(*v as _);
} else { } else {
std::process::exit(1); std::process::exit(1);
} }
@ -965,7 +1007,7 @@ impl BuiltinFunction {
} }
Self::FsList => { Self::FsList => {
if args.len() > 0 { if args.len() > 0 {
if let VDataEnum::String(path) = args[0].run(vars, info).data { if let VDataEnum::String(path) = &args[0].run(vars, info).data().0 {
if args.len() > 1 { if args.len() > 1 {
eprintln!("NOT YET IMPLEMENTED (TODO!): fs_list advanced filters") eprintln!("NOT YET IMPLEMENTED (TODO!): fs_list advanced filters")
} }
@ -1003,7 +1045,7 @@ impl BuiltinFunction {
} }
Self::FsRead => { Self::FsRead => {
if args.len() > 0 { if args.len() > 0 {
if let VDataEnum::String(path) = args[0].run(vars, info).data { if let VDataEnum::String(path) = &args[0].run(vars, info).data().0 {
match std::fs::read(path) { match std::fs::read(path) {
Ok(data) => VDataEnum::List( Ok(data) => VDataEnum::List(
VSingleType::Int.into(), VSingleType::Int.into(),
@ -1027,9 +1069,10 @@ impl BuiltinFunction {
} }
Self::FsWrite => { Self::FsWrite => {
if args.len() > 1 { if args.len() > 1 {
if let (VDataEnum::String(path), VDataEnum::List(_, data)) = if let (VDataEnum::String(path), VDataEnum::List(_, data)) = (
(args[0].run(vars, info).data, args[1].run(vars, info).data) &args[0].run(vars, info).data().0,
{ &args[1].run(vars, info).data().0,
) {
if let Some(bytes) = vdata_to_bytes(&data) { if let Some(bytes) = vdata_to_bytes(&data) {
let file_path: PathBuf = path.into(); let file_path: PathBuf = path.into();
if let Some(p) = file_path.parent() { if let Some(p) = file_path.parent() {
@ -1057,7 +1100,7 @@ impl BuiltinFunction {
} }
Self::BytesToString => { Self::BytesToString => {
if args.len() == 1 { if args.len() == 1 {
if let VDataEnum::List(_, byte_data) = args[0].run(vars, info).data { if let VDataEnum::List(_, byte_data) = &args[0].run(vars, info).data().0 {
if let Some(bytes) = vdata_to_bytes(&byte_data) { if let Some(bytes) = vdata_to_bytes(&byte_data) {
match String::from_utf8(bytes) { match String::from_utf8(bytes) {
Ok(v) => VDataEnum::String(v).to(), Ok(v) => VDataEnum::String(v).to(),
@ -1092,7 +1135,7 @@ impl BuiltinFunction {
} }
Self::StringToBytes => { Self::StringToBytes => {
if args.len() == 1 { if args.len() == 1 {
if let VDataEnum::String(s) = args[0].run(vars, info).data { if let VDataEnum::String(s) = &args[0].run(vars, info).data().0 {
VDataEnum::List( VDataEnum::List(
VSingleType::Int.into(), VSingleType::Int.into(),
s.bytes().map(|v| VDataEnum::Int(v as isize).to()).collect(), s.bytes().map(|v| VDataEnum::Int(v as isize).to()).collect(),
@ -1107,12 +1150,12 @@ impl BuiltinFunction {
} }
Self::RunCommand | Self::RunCommandGetBytes => { Self::RunCommand | Self::RunCommandGetBytes => {
if args.len() > 0 { if args.len() > 0 {
if let VDataEnum::String(s) = args[0].run(vars, info).data { if let VDataEnum::String(s) = &args[0].run(vars, info).data().0 {
let mut command = std::process::Command::new(s); let mut command = std::process::Command::new(s);
if args.len() > 1 { if args.len() > 1 {
if let VDataEnum::List(_, args) = args[1].run(vars, info).data { if let VDataEnum::List(_, args) = &args[1].run(vars, info).data().0 {
for arg in args { for arg in args {
if let VDataEnum::String(v) = arg.data { if let VDataEnum::String(v) = &arg.data().0 {
command.arg(v); command.arg(v);
} else { } else {
unreachable!("run_command second arg not [string].") unreachable!("run_command second arg not [string].")
@ -1172,19 +1215,19 @@ impl BuiltinFunction {
} }
} }
Self::Not => { Self::Not => {
if let VDataEnum::Bool(v) = args[0].run(vars, info).data { if let VDataEnum::Bool(v) = &args[0].run(vars, info).data().0 {
VDataEnum::Bool(!v).to() VDataEnum::Bool(!v).to()
} else { } else {
unreachable!() unreachable!()
} }
} }
Self::And => { Self::And => {
if let VDataEnum::Bool(a) = args[0].run(vars, info).data { if let VDataEnum::Bool(a) = &args[0].run(vars, info).data().0 {
if a == false { if *a == false {
VDataEnum::Bool(false).to() VDataEnum::Bool(false).to()
} else { } else {
if let VDataEnum::Bool(b) = args[1].run(vars, info).data { if let VDataEnum::Bool(b) = &args[1].run(vars, info).data().0 {
VDataEnum::Bool(b).to() VDataEnum::Bool(*b).to()
} else { } else {
unreachable!() unreachable!()
} }
@ -1194,12 +1237,12 @@ impl BuiltinFunction {
} }
} }
Self::Or => { Self::Or => {
if let VDataEnum::Bool(a) = args[0].run(vars, info).data { if let VDataEnum::Bool(a) = &args[0].run(vars, info).data().0 {
if a == true { if *a == true {
VDataEnum::Bool(true).to() VDataEnum::Bool(true).to()
} else { } else {
if let VDataEnum::Bool(b) = args[1].run(vars, info).data { if let VDataEnum::Bool(b) = &args[1].run(vars, info).data().0 {
VDataEnum::Bool(b).to() VDataEnum::Bool(*b).to()
} else { } else {
unreachable!() unreachable!()
} }
@ -1210,7 +1253,10 @@ impl BuiltinFunction {
} }
Self::Add => { Self::Add => {
if args.len() == 2 { if args.len() == 2 {
match (args[0].run(vars, info).data, args[1].run(vars, info).data) { match (
args[0].run(vars, info).inner(),
args[1].run(vars, info).inner(),
) {
(VDataEnum::String(mut a), VDataEnum::String(b)) => { (VDataEnum::String(mut a), VDataEnum::String(b)) => {
a.push_str(b.as_str()); a.push_str(b.as_str());
VDataEnum::String(a).to() VDataEnum::String(a).to()
@ -1231,13 +1277,16 @@ impl BuiltinFunction {
} }
Self::Sub => { Self::Sub => {
if args.len() == 2 { if args.len() == 2 {
match (args[0].run(vars, info).data, args[1].run(vars, info).data) { match (
&args[0].run(vars, info).data().0,
&args[1].run(vars, info).data().0,
) {
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a - b).to(), (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a - b).to(),
(VDataEnum::Int(a), VDataEnum::Float(b)) => { (VDataEnum::Int(a), VDataEnum::Float(b)) => {
VDataEnum::Float(a as f64 - b).to() VDataEnum::Float(*a as f64 - *b).to()
} }
(VDataEnum::Float(a), VDataEnum::Int(b)) => { (VDataEnum::Float(a), VDataEnum::Int(b)) => {
VDataEnum::Float(a - b as f64).to() VDataEnum::Float(*a - *b as f64).to()
} }
(VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Float(a - b).to(), (VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Float(a - b).to(),
_ => unreachable!("sub: not a number"), _ => unreachable!("sub: not a number"),
@ -1248,13 +1297,16 @@ impl BuiltinFunction {
} }
Self::Mul => { Self::Mul => {
if args.len() == 2 { if args.len() == 2 {
match (args[0].run(vars, info).data, args[1].run(vars, info).data) { match (
&args[0].run(vars, info).data().0,
&args[1].run(vars, info).data().0,
) {
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a * b).to(), (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a * b).to(),
(VDataEnum::Int(a), VDataEnum::Float(b)) => { (VDataEnum::Int(a), VDataEnum::Float(b)) => {
VDataEnum::Float(a as f64 * b).to() VDataEnum::Float(*a as f64 * b).to()
} }
(VDataEnum::Float(a), VDataEnum::Int(b)) => { (VDataEnum::Float(a), VDataEnum::Int(b)) => {
VDataEnum::Float(a * b as f64).to() VDataEnum::Float(a * *b as f64).to()
} }
(VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Float(a * b).to(), (VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Float(a * b).to(),
_ => unreachable!("mul: not a number"), _ => unreachable!("mul: not a number"),
@ -1265,13 +1317,16 @@ impl BuiltinFunction {
} }
Self::Div => { Self::Div => {
if args.len() == 2 { if args.len() == 2 {
match (args[0].run(vars, info).data, args[1].run(vars, info).data) { match (
&args[0].run(vars, info).data().0,
&args[1].run(vars, info).data().0,
) {
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a / b).to(), (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a / b).to(),
(VDataEnum::Int(a), VDataEnum::Float(b)) => { (VDataEnum::Int(a), VDataEnum::Float(b)) => {
VDataEnum::Float(a as f64 / b).to() VDataEnum::Float(*a as f64 / b).to()
} }
(VDataEnum::Float(a), VDataEnum::Int(b)) => { (VDataEnum::Float(a), VDataEnum::Int(b)) => {
VDataEnum::Float(a / b as f64).to() VDataEnum::Float(a / *b as f64).to()
} }
(VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Float(a / b).to(), (VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Float(a / b).to(),
_ => unreachable!("div: not a number"), _ => unreachable!("div: not a number"),
@ -1282,13 +1337,16 @@ impl BuiltinFunction {
} }
Self::Mod => { Self::Mod => {
if args.len() == 2 { if args.len() == 2 {
match (args[0].run(vars, info).data, args[1].run(vars, info).data) { match (
&args[0].run(vars, info).data().0,
&args[1].run(vars, info).data().0,
) {
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a % b).to(), (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a % b).to(),
(VDataEnum::Int(a), VDataEnum::Float(b)) => { (VDataEnum::Int(a), VDataEnum::Float(b)) => {
VDataEnum::Float(a as f64 % b).to() VDataEnum::Float(*a as f64 % b).to()
} }
(VDataEnum::Float(a), VDataEnum::Int(b)) => { (VDataEnum::Float(a), VDataEnum::Int(b)) => {
VDataEnum::Float(a % b as f64).to() VDataEnum::Float(a % *b as f64).to()
} }
(VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Float(a % b).to(), (VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Float(a % b).to(),
_ => unreachable!("mod: not a number"), _ => unreachable!("mod: not a number"),
@ -1299,23 +1357,26 @@ impl BuiltinFunction {
} }
Self::Pow => { Self::Pow => {
if args.len() == 2 { if args.len() == 2 {
match (args[0].run(vars, info).data, args[1].run(vars, info).data) { match (
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(if b == 0 { &args[0].run(vars, info).data().0,
&args[1].run(vars, info).data().0,
) {
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(if *b == 0 {
1 1
} else if b > 0 { } else if *b > 0 {
a.pow(b as _) (*a).pow(*b as _)
} else { } else {
0 0
}) })
.to(), .to(),
(VDataEnum::Int(a), VDataEnum::Float(b)) => { (VDataEnum::Int(a), VDataEnum::Float(b)) => {
VDataEnum::Float((a as f64).powf(b)).to() VDataEnum::Float((*a as f64).powf(*b)).to()
} }
(VDataEnum::Float(a), VDataEnum::Int(b)) => { (VDataEnum::Float(a), VDataEnum::Int(b)) => {
VDataEnum::Float(a.powi(b as _)).to() VDataEnum::Float((*a).powi(*b as _)).to()
} }
(VDataEnum::Float(a), VDataEnum::Float(b)) => { (VDataEnum::Float(a), VDataEnum::Float(b)) => {
VDataEnum::Float(a.powf(b)).to() VDataEnum::Float((*a).powf(*b)).to()
} }
_ => unreachable!("pow: not a number"), _ => unreachable!("pow: not a number"),
} }
@ -1332,15 +1393,18 @@ impl BuiltinFunction {
} }
Self::Gt => { Self::Gt => {
if args.len() == 2 { if args.len() == 2 {
match (args[0].run(vars, info).data, args[1].run(vars, info).data) { match (
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a > b).to(), &args[0].run(vars, info).data().0,
&args[1].run(vars, info).data().0,
) {
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(*a > *b).to(),
(VDataEnum::Int(a), VDataEnum::Float(b)) => { (VDataEnum::Int(a), VDataEnum::Float(b)) => {
VDataEnum::Bool(a as f64 > b).to() VDataEnum::Bool(*a as f64 > *b).to()
} }
(VDataEnum::Float(a), VDataEnum::Int(b)) => { (VDataEnum::Float(a), VDataEnum::Int(b)) => {
VDataEnum::Bool(a > b as f64).to() VDataEnum::Bool(*a > *b as f64).to()
} }
(VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Bool(a > b).to(), (VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Bool(*a > *b).to(),
_ => unreachable!("gt: not a number"), _ => unreachable!("gt: not a number"),
} }
} else { } else {
@ -1349,15 +1413,18 @@ impl BuiltinFunction {
} }
Self::Lt => { Self::Lt => {
if args.len() == 2 { if args.len() == 2 {
match (args[0].run(vars, info).data, args[1].run(vars, info).data) { match (
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a < b).to(), &args[0].run(vars, info).data().0,
&args[1].run(vars, info).data().0,
) {
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(*a < *b).to(),
(VDataEnum::Int(a), VDataEnum::Float(b)) => { (VDataEnum::Int(a), VDataEnum::Float(b)) => {
VDataEnum::Bool((a as f64) < b).to() VDataEnum::Bool((*a as f64) < *b).to()
} }
(VDataEnum::Float(a), VDataEnum::Int(b)) => { (VDataEnum::Float(a), VDataEnum::Int(b)) => {
VDataEnum::Bool(a < b as f64).to() VDataEnum::Bool(*a < *b as f64).to()
} }
(VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Bool(a < b).to(), (VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Bool(*a < *b).to(),
_ => unreachable!("lt: not a number"), _ => unreachable!("lt: not a number"),
} }
} else { } else {
@ -1366,15 +1433,20 @@ impl BuiltinFunction {
} }
Self::Gtoe => { Self::Gtoe => {
if args.len() == 2 { if args.len() == 2 {
match (args[0].run(vars, info).data, args[1].run(vars, info).data) { match (
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a >= b).to(), &args[0].run(vars, info).data().0,
&args[1].run(vars, info).data().0,
) {
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(*a >= *b).to(),
(VDataEnum::Int(a), VDataEnum::Float(b)) => { (VDataEnum::Int(a), VDataEnum::Float(b)) => {
VDataEnum::Bool(a as f64 >= b).to() VDataEnum::Bool(*a as f64 >= *b).to()
} }
(VDataEnum::Float(a), VDataEnum::Int(b)) => { (VDataEnum::Float(a), VDataEnum::Int(b)) => {
VDataEnum::Bool(a >= b as f64).to() VDataEnum::Bool(*a >= *b as f64).to()
}
(VDataEnum::Float(a), VDataEnum::Float(b)) => {
VDataEnum::Bool(*a >= *b).to()
} }
(VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Bool(a >= b).to(),
_ => unreachable!("gtoe: not a number"), _ => unreachable!("gtoe: not a number"),
} }
} else { } else {
@ -1383,15 +1455,20 @@ impl BuiltinFunction {
} }
Self::Ltoe => { Self::Ltoe => {
if args.len() == 2 { if args.len() == 2 {
match (args[0].run(vars, info).data, args[1].run(vars, info).data) { match (
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a <= b).to(), &args[0].run(vars, info).data().0,
&args[1].run(vars, info).data().0,
) {
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(*a <= *b).to(),
(VDataEnum::Int(a), VDataEnum::Float(b)) => { (VDataEnum::Int(a), VDataEnum::Float(b)) => {
VDataEnum::Bool(a as f64 <= b).to() VDataEnum::Bool(*a as f64 <= *b).to()
} }
(VDataEnum::Float(a), VDataEnum::Int(b)) => { (VDataEnum::Float(a), VDataEnum::Int(b)) => {
VDataEnum::Bool(a <= b as f64).to() VDataEnum::Bool(*a <= *b as f64).to()
}
(VDataEnum::Float(a), VDataEnum::Float(b)) => {
VDataEnum::Bool(*a <= *b).to()
} }
(VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Bool(a <= b).to(),
_ => unreachable!("ltoe: not a number"), _ => unreachable!("ltoe: not a number"),
} }
} else { } else {
@ -1400,16 +1477,19 @@ impl BuiltinFunction {
} }
Self::Min => { Self::Min => {
if args.len() == 2 { if args.len() == 2 {
match (args[0].run(vars, info).data, args[1].run(vars, info).data) { match (
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a.min(b)).to(), &args[0].run(vars, info).data().0,
&args[1].run(vars, info).data().0,
) {
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int((*a).min(*b)).to(),
(VDataEnum::Int(a), VDataEnum::Float(b)) => { (VDataEnum::Int(a), VDataEnum::Float(b)) => {
VDataEnum::Float((a as f64).min(b)).to() VDataEnum::Float((*a as f64).min(*b)).to()
} }
(VDataEnum::Float(a), VDataEnum::Int(b)) => { (VDataEnum::Float(a), VDataEnum::Int(b)) => {
VDataEnum::Float(a.min(b as f64)).to() VDataEnum::Float((*a).min(*b as f64)).to()
} }
(VDataEnum::Float(a), VDataEnum::Float(b)) => { (VDataEnum::Float(a), VDataEnum::Float(b)) => {
VDataEnum::Float(a.min(b)).to() VDataEnum::Float((*a).min(*b)).to()
} }
_ => unreachable!("min: not a number"), _ => unreachable!("min: not a number"),
} }
@ -1419,16 +1499,19 @@ impl BuiltinFunction {
} }
Self::Max => { Self::Max => {
if args.len() == 2 { if args.len() == 2 {
match (args[0].run(vars, info).data, args[1].run(vars, info).data) { match (
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a.max(b)).to(), &args[0].run(vars, info).data().0,
&args[1].run(vars, info).data().0,
) {
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int((*a).max(*b)).to(),
(VDataEnum::Int(a), VDataEnum::Float(b)) => { (VDataEnum::Int(a), VDataEnum::Float(b)) => {
VDataEnum::Float((a as f64).max(b)).to() VDataEnum::Float((*a as f64).max(*b)).to()
} }
(VDataEnum::Float(a), VDataEnum::Int(b)) => { (VDataEnum::Float(a), VDataEnum::Int(b)) => {
VDataEnum::Float(a.max(b as f64)).to() VDataEnum::Float((*a).max(*b as f64)).to()
} }
(VDataEnum::Float(a), VDataEnum::Float(b)) => { (VDataEnum::Float(a), VDataEnum::Float(b)) => {
VDataEnum::Float(a.max(b)).to() VDataEnum::Float((*a).max(*b)).to()
} }
_ => unreachable!("max: not a number"), _ => unreachable!("max: not a number"),
} }
@ -1438,8 +1521,9 @@ impl BuiltinFunction {
} }
Self::Push => { Self::Push => {
if args.len() == 2 { if args.len() == 2 {
if let VDataEnum::Reference(v) = args[0].run(vars, info).data { // Since this is a reference, it is safe to assume that make_mut() would do nothing.
if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { if let VDataEnum::Reference(v) = &args[0].run(vars, info).data().0 {
if let VDataEnum::List(_, v) = &mut v.data().0 {
v.push(args[1].run(vars, info)); v.push(args[1].run(vars, info));
} }
VDataEnum::Tuple(vec![]).to() VDataEnum::Tuple(vec![]).to()
@ -1452,11 +1536,13 @@ impl BuiltinFunction {
} }
Self::Insert => { Self::Insert => {
if args.len() == 3 { if args.len() == 3 {
if let (VDataEnum::Reference(v), VDataEnum::Int(i)) = // this being a reference means we wont need to call make_mut() later, so a .as_ref() borrow is enough.
(args[0].run(vars, info).data, args[2].run(vars, info).data) if let (VDataEnum::Reference(v), VDataEnum::Int(i)) = (
{ &args[0].run(vars, info).data().0,
if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { &args[2].run(vars, info).data().0,
v.insert(i as _, args[1].run(vars, info)); ) {
if let VDataEnum::List(_, v) = &mut v.data().0 {
v.insert(*i as _, args[1].run(vars, info));
} }
VDataEnum::Tuple(vec![]).to() VDataEnum::Tuple(vec![]).to()
} else { } else {
@ -1468,8 +1554,9 @@ impl BuiltinFunction {
} }
Self::Pop => { Self::Pop => {
if args.len() == 1 { if args.len() == 1 {
if let VDataEnum::Reference(v) = args[0].run(vars, info).data { // this being a reference means we wont need to call make_mut() later, so a .as_ref() borrow is enough.
if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { if let VDataEnum::Reference(v) = &args[0].run(vars, info).data().0 {
if let VDataEnum::List(_, v) = &mut v.data.lock().unwrap().0 {
if let Some(v) = v.pop() { if let Some(v) = v.pop() {
VDataEnum::Tuple(vec![v]) VDataEnum::Tuple(vec![v])
} else { } else {
@ -1488,12 +1575,14 @@ impl BuiltinFunction {
} }
Self::Remove => { Self::Remove => {
if args.len() == 2 { if args.len() == 2 {
if let (VDataEnum::Reference(v), VDataEnum::Int(i)) = // this being a reference means we wont need to call make_mut() later, so a .as_ref() borrow is enough.
(args[0].run(vars, info).data, args[1].run(vars, info).data) if let (VDataEnum::Reference(v), VDataEnum::Int(i)) = (
{ &args[0].run(vars, info).data().0,
if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { &args[1].run(vars, info).data().0,
if v.len() > i as _ && i >= 0 { ) {
let v = v.remove(i as _); if let VDataEnum::List(_, v) = &mut v.data.lock().unwrap().0 {
if *i >= 0 && v.len() > *i as _ {
let v = v.remove(*i as _);
VDataEnum::Tuple(vec![v]).to() VDataEnum::Tuple(vec![v]).to()
} else { } else {
VDataEnum::Tuple(vec![]).to() VDataEnum::Tuple(vec![]).to()
@ -1510,21 +1599,22 @@ impl BuiltinFunction {
} }
Self::Get => { Self::Get => {
if args.len() == 2 { if args.len() == 2 {
if let (container, VDataEnum::Int(i)) = if let (container, VDataEnum::Int(i)) = (
(args[0].run(vars, info).data, args[1].run(vars, info).data) &args[0].run(vars, info).data().0,
{ &args[1].run(vars, info).data().0,
if i >= 0 { ) {
if *i >= 0 {
match match container { match match container {
VDataEnum::Reference(v) => match &v.lock().unwrap().data { VDataEnum::Reference(v) => match &v.data().0 {
VDataEnum::List(_, v) | VDataEnum::Tuple(v) => { VDataEnum::List(_, v) | VDataEnum::Tuple(v) => {
v.get(i as usize).map(|v| v.clone()) v.get(*i as usize).map(|v| v.clone())
} }
_ => unreachable!( _ => unreachable!(
"get: reference to something other than list/tuple" "get: reference to something other than list/tuple"
), ),
}, },
VDataEnum::List(_, v) | VDataEnum::Tuple(v) => { VDataEnum::List(_, v) | VDataEnum::Tuple(v) => {
v.get(i as usize).map(|v| v.clone()) v.get(*i as usize).map(|v| v.clone())
} }
_ => unreachable!("get: not a reference/list/tuple"), _ => unreachable!("get: not a reference/list/tuple"),
} { } {
@ -1541,9 +1631,40 @@ impl BuiltinFunction {
unreachable!("get: not 2 args") unreachable!("get: not 2 args")
} }
} }
Self::GetRef => {
if args.len() == 2 {
if let (VDataEnum::Reference(container), VDataEnum::Int(i)) = (
&args[0].run(vars, info).data().0,
&args[1].run(vars, info).data().0,
) {
if *i >= 0 {
// we can get mutably because this is the content of a reference
match match &mut container.data().0 {
VDataEnum::List(_, v) | VDataEnum::Tuple(v) => {
if let Some(v) = v.get_mut(*i as usize) {
Some(VDataEnum::Reference(v.clone_mut()).to())
} else {
None
}
}
_ => unreachable!("get: not a reference/list/tuple"),
} {
Some(v) => VDataEnum::Tuple(vec![v]).to(),
None => VDataEnum::Tuple(vec![]).to(),
}
} else {
VDataEnum::Tuple(vec![]).to()
}
} else {
unreachable!("get_ref: not a reference and index")
}
} else {
unreachable!("get: not 2 args")
}
}
Self::Len => { Self::Len => {
if args.len() == 1 { if args.len() == 1 {
VDataEnum::Int(match args[0].run(vars, info).data { VDataEnum::Int(match &args[0].run(vars, info).data().0 {
VDataEnum::String(v) => v.len(), VDataEnum::String(v) => v.len(),
VDataEnum::Tuple(v) => v.len(), VDataEnum::Tuple(v) => v.len(),
VDataEnum::List(_, v) => v.len(), VDataEnum::List(_, v) => v.len(),
@ -1556,8 +1677,8 @@ impl BuiltinFunction {
} }
Self::Contains => { Self::Contains => {
if args.len() == 2 { if args.len() == 2 {
if let VDataEnum::String(a1) = args[0].run(vars, info).data { if let VDataEnum::String(a1) = &args[0].run(vars, info).data().0 {
if let VDataEnum::String(a2) = args[1].run(vars, info).data { if let VDataEnum::String(a2) = &args[1].run(vars, info).data().0 {
VDataEnum::Bool(a1.contains(a2.as_str())).to() VDataEnum::Bool(a1.contains(a2.as_str())).to()
} else { } else {
unreachable!() unreachable!()
@ -1571,8 +1692,8 @@ impl BuiltinFunction {
} }
Self::StartsWith => { Self::StartsWith => {
if args.len() == 2 { if args.len() == 2 {
if let VDataEnum::String(a1) = args[0].run(vars, info).data { if let VDataEnum::String(a1) = &args[0].run(vars, info).data().0 {
if let VDataEnum::String(a2) = args[1].run(vars, info).data { if let VDataEnum::String(a2) = &args[1].run(vars, info).data().0 {
VDataEnum::Bool(a1.starts_with(a2.as_str())).to() VDataEnum::Bool(a1.starts_with(a2.as_str())).to()
} else { } else {
unreachable!() unreachable!()
@ -1586,8 +1707,8 @@ impl BuiltinFunction {
} }
Self::EndsWith => { Self::EndsWith => {
if args.len() == 2 { if args.len() == 2 {
if let VDataEnum::String(a1) = args[0].run(vars, info).data { if let VDataEnum::String(a1) = &args[0].run(vars, info).data().0 {
if let VDataEnum::String(a2) = args[1].run(vars, info).data { if let VDataEnum::String(a2) = &args[1].run(vars, info).data().0 {
VDataEnum::Bool(a1.ends_with(a2.as_str())).to() VDataEnum::Bool(a1.ends_with(a2.as_str())).to()
} else { } else {
unreachable!() unreachable!()
@ -1622,41 +1743,38 @@ impl BuiltinFunction {
VDataEnum::Tuple(vec![]).to() VDataEnum::Tuple(vec![]).to()
} }
} }
match (find_in.data, pat.data) { let o = match (&find_in.data().0, &pat.data().0) {
(VDataEnum::String(a), VDataEnum::String(b)) => find(&a, &b), (VDataEnum::String(a), VDataEnum::String(b)) => find(a, b),
(VDataEnum::String(a), VDataEnum::Reference(b)) => { (VDataEnum::String(a), VDataEnum::Reference(b)) => match &b.data().0 {
match &b.lock().unwrap().data { VDataEnum::String(b) => find(a, b),
VDataEnum::String(b) => find(&a, b),
_ => unreachable!(), _ => unreachable!(),
} },
} (VDataEnum::Reference(a), VDataEnum::String(b)) => match &a.data().0 {
(VDataEnum::Reference(a), VDataEnum::String(b)) => { VDataEnum::String(a) => find(a, b),
match &a.lock().unwrap().data {
VDataEnum::String(a) => find(a, &b),
_ => unreachable!(), _ => unreachable!(),
} },
}
(VDataEnum::Reference(a), VDataEnum::Reference(b)) => { (VDataEnum::Reference(a), VDataEnum::Reference(b)) => {
if Arc::ptr_eq(&a, &b) { if a.ptr_eq(b) {
// point to the same string // point to the same string
// (this is required because a.lock() would cause b.lock() to wait indefinitely if you pass a two references to the same string here) // (this is required because a.lock() would cause b.lock() to wait indefinitely if you pass a two references to the same string here)
VDataEnum::Int(0).to() VDataEnum::Int(0).to()
} else { } else {
match (&a.lock().unwrap().data, &b.lock().unwrap().data) { match (&a.data().0, &b.data().0) {
(VDataEnum::String(a), VDataEnum::String(b)) => find(a, b), (VDataEnum::String(a), VDataEnum::String(b)) => find(a, b),
_ => unreachable!(), _ => unreachable!(),
} }
} }
} }
_ => unreachable!(), _ => unreachable!(),
} };
o
} else { } else {
unreachable!() unreachable!()
} }
} }
Self::Trim => { Self::Trim => {
if args.len() == 1 { if args.len() == 1 {
if let VDataEnum::String(a) = args[0].run(vars, info).data { if let VDataEnum::String(a) = &args[0].run(vars, info).data().0 {
VDataEnum::String(a.trim().to_string()).to() VDataEnum::String(a.trim().to_string()).to()
} else { } else {
unreachable!() unreachable!()
@ -1667,18 +1785,18 @@ impl BuiltinFunction {
} }
Self::Substring => { Self::Substring => {
if args.len() >= 2 { if args.len() >= 2 {
if let VDataEnum::String(a) = args[0].run(vars, info).data { if let VDataEnum::String(a) = &args[0].run(vars, info).data().0 {
if args.len() > 3 { if args.len() > 3 {
unreachable!() unreachable!()
} }
let left = if let VDataEnum::Int(left) = args[1].run(vars, info).data { let left = if let VDataEnum::Int(left) = &args[1].run(vars, info).data().0 {
left *left
} else { } else {
unreachable!() unreachable!()
}; };
let len = if args.len() == 3 { let len = if args.len() == 3 {
if let VDataEnum::Int(len) = args[2].run(vars, info).data { if let VDataEnum::Int(len) = &args[2].run(vars, info).data().0 {
Some(len) Some(*len)
} else { } else {
unreachable!() unreachable!()
} }
@ -1714,20 +1832,21 @@ impl BuiltinFunction {
} }
Self::Replace => { Self::Replace => {
if let (VDataEnum::String(a), VDataEnum::String(b), VDataEnum::String(c)) = ( if let (VDataEnum::String(a), VDataEnum::String(b), VDataEnum::String(c)) = (
args[0].run(vars, info).data, &args[0].run(vars, info).data().0,
args[1].run(vars, info).data, &args[1].run(vars, info).data().0,
args[2].run(vars, info).data, &args[2].run(vars, info).data().0,
) { ) {
VDataEnum::String(a.replace(&b, &c)).to() VDataEnum::String(a.replace(b, c)).to()
} else { } else {
unreachable!() unreachable!()
} }
} }
Self::Regex => { Self::Regex => {
if args.len() == 2 { if args.len() == 2 {
if let (VDataEnum::String(a), VDataEnum::String(regex)) = if let (VDataEnum::String(a), VDataEnum::String(regex)) = (
(args[0].run(vars, info).data, args[1].run(vars, info).data) &args[0].run(vars, info).data().0,
{ &args[1].run(vars, info).data().0,
) {
match regex::Regex::new(regex.as_str()) { match regex::Regex::new(regex.as_str()) {
Ok(regex) => VDataEnum::List( Ok(regex) => VDataEnum::List(
VSingleType::String.to(), VSingleType::String.to(),
@ -1757,9 +1876,9 @@ impl BuiltinFunction {
fn vdata_to_bytes(vd: &Vec<VData>) -> Option<Vec<u8>> { fn vdata_to_bytes(vd: &Vec<VData>) -> Option<Vec<u8>> {
let mut bytes = Vec::with_capacity(vd.len()); let mut bytes = Vec::with_capacity(vd.len());
for b in vd { for b in vd {
if let VDataEnum::Int(b) = b.data { if let VDataEnum::Int(b) = &b.data().0 {
bytes.push(if 0 <= b && b <= u8::MAX as isize { bytes.push(if 0 <= *b && *b <= u8::MAX as isize {
b as u8 *b as u8
} else if b.is_negative() { } else if b.is_negative() {
0 0
} else { } else {

View File

@ -8,17 +8,12 @@ use super::{
val_type::{VSingleType, VType}, val_type::{VSingleType, VType},
}; };
type Am<T> = Arc<Mutex<T>>;
fn am<T>(i: T) -> Am<T> {
Arc::new(Mutex::new(i))
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct RBlock { pub struct RBlock {
pub statements: Vec<RStatement>, pub statements: Vec<RStatement>,
} }
impl RBlock { impl RBlock {
pub fn run(&self, vars: &Vec<Am<VData>>, info: &GSInfo) -> VData { pub fn run(&self, vars: &mut Vec<VData>, info: &GSInfo) -> VData {
let mut last = None; let mut last = None;
for statement in &self.statements { for statement in &self.statements {
last = Some(statement.run(vars, info)); last = Some(statement.run(vars, info));
@ -48,7 +43,7 @@ pub struct RFunction {
pub block: RBlock, pub block: RBlock,
} }
impl RFunction { impl RFunction {
pub fn run(&self, vars: &Vec<Am<VData>>, info: &GSInfo) -> VData { pub fn run(&self, vars: &mut Vec<VData>, info: &GSInfo) -> VData {
self.block.run(vars, info) self.block.run(vars, info)
} }
pub fn out(&self, input_types: &Vec<VSingleType>) -> VType { pub fn out(&self, input_types: &Vec<VSingleType>) -> VType {
@ -91,19 +86,22 @@ pub struct RStatement {
pub force_output_type: Option<VType>, pub force_output_type: Option<VType>,
} }
impl RStatement { impl RStatement {
pub fn run(&self, vars: &Vec<Am<VData>>, info: &GSInfo) -> VData { pub fn run(&self, vars: &mut Vec<VData>, info: &GSInfo) -> VData {
let out = self.statement.run(vars, info); let out = self.statement.run(vars, info);
if let Some((v, derefs)) = self.output_to { if let Some((v, derefs)) = self.output_to {
let mut val = vars[v].clone(); let mut val = dereference_n(&mut vars[v], derefs);
for _ in 0..derefs { fn dereference_n(d: &mut VData, n: usize) -> VData {
let v = if let VDataEnum::Reference(v) = &val.lock().unwrap().data { if n > 0 {
v.clone() if let VDataEnum::Reference(v) = &mut d.data.lock().unwrap().0 {
dereference_n(v, n - 1)
} else { } else {
unreachable!("dereferencing something that isn't a reference in assignment") unreachable!("dereferencing something that isn't a reference in assignment")
};
val = v;
} }
*val.lock().unwrap() = out; } else {
d.clone_mut()
}
}
val.assign(out.inner());
VDataEnum::Tuple(vec![]).to() VDataEnum::Tuple(vec![]).to()
} else { } else {
out out
@ -142,7 +140,7 @@ pub enum RStatementEnum {
EnumVariant(usize, RStatement), EnumVariant(usize, RStatement),
} }
impl RStatementEnum { impl RStatementEnum {
pub fn run(&self, vars: &Vec<Am<VData>>, info: &GSInfo) -> VData { pub fn run(&self, vars: &mut Vec<VData>, info: &GSInfo) -> VData {
match self { match self {
Self::Value(v) => v.clone(), Self::Value(v) => v.clone(),
Self::Tuple(v) => { Self::Tuple(v) => {
@ -164,14 +162,16 @@ impl RStatementEnum {
} }
Self::Variable(v, _, is_ref) => { Self::Variable(v, _, is_ref) => {
if *is_ref { if *is_ref {
VDataEnum::Reference(vars[*v].clone()).to() // shared mutability (clone_mut)
VDataEnum::Reference(vars[*v].clone_mut()).to()
} else { } else {
vars[*v].lock().unwrap().clone() // Copy on Write (clone)
vars[*v].clone()
} }
} }
Self::FunctionCall(func, args) => { Self::FunctionCall(func, args) => {
for (i, input) in func.inputs.iter().enumerate() { for (i, input) in func.inputs.iter().enumerate() {
*vars[*input].lock().unwrap() = args[i].run(vars, info); vars[*input] = args[i].run(vars, info);
} }
func.run(vars, info) func.run(vars, info)
} }
@ -180,8 +180,8 @@ impl RStatementEnum {
.run_fn(*fnid, args.iter().map(|arg| arg.run(vars, info)).collect()), .run_fn(*fnid, args.iter().map(|arg| arg.run(vars, info)).collect()),
Self::Block(b) => b.run(vars, info), Self::Block(b) => b.run(vars, info),
Self::If(c, t, e) => { Self::If(c, t, e) => {
if let VDataEnum::Bool(v) = c.run(vars, info).data { if let VDataEnum::Bool(v) = &c.run(vars, info).data().0 {
if v { if *v {
t.run(vars, info) t.run(vars, info)
} else { } else {
if let Some(e) = e { if let Some(e) = e {
@ -196,7 +196,7 @@ impl RStatementEnum {
} }
Self::Loop(c) => loop { Self::Loop(c) => loop {
// loops will break if the value matches. // loops will break if the value matches.
if let Some(break_val) = c.run(vars, info).data.matches() { if let Some(break_val) = c.run(vars, info).inner().matches() {
break break_val; break break_val;
} }
}, },
@ -204,17 +204,17 @@ impl RStatementEnum {
// matching values also break with value from a for loop. // matching values also break with value from a for loop.
let c = c.run(vars, info); let c = c.run(vars, info);
let mut vars = vars.clone(); let mut vars = vars.clone();
let in_loop = |vars: &mut Vec<Arc<Mutex<VData>>>, c| { let in_loop = |vars: &mut Vec<VData>, c| {
vars[*v] = Arc::new(Mutex::new(c)); vars[*v] = c;
b.run(&vars, info) b.run(vars, info)
}; };
let mut oval = VDataEnum::Tuple(vec![]).to(); let mut oval = VDataEnum::Tuple(vec![]).to();
match c.data { match &c.data().0 {
VDataEnum::Int(v) => { VDataEnum::Int(v) => {
for i in 0..v { for i in 0..*v {
if let Some(v) = if let Some(v) =
in_loop(&mut vars, VDataEnum::Int(i).to()).data.matches() in_loop(&mut vars, VDataEnum::Int(i).to()).inner().matches()
{ {
oval = v; oval = v;
break; break;
@ -225,7 +225,7 @@ impl RStatementEnum {
for ch in v.chars() { for ch in v.chars() {
if let Some(v) = if let Some(v) =
in_loop(&mut vars, VDataEnum::String(ch.to_string()).to()) in_loop(&mut vars, VDataEnum::String(ch.to_string()).to())
.data .inner()
.matches() .matches()
{ {
oval = v; oval = v;
@ -235,15 +235,15 @@ impl RStatementEnum {
} }
VDataEnum::Tuple(v) | VDataEnum::List(_, v) => { VDataEnum::Tuple(v) | VDataEnum::List(_, v) => {
for v in v { for v in v {
if let Some(v) = in_loop(&mut vars, v).data.matches() { if let Some(v) = in_loop(&mut vars, v.clone()).inner().matches() {
oval = v; oval = v;
break; break;
} }
} }
} }
VDataEnum::Function(f) => loop { VDataEnum::Function(f) => loop {
if let Some(v) = f.run(&vars, info).data.matches() { if let Some(v) = f.run(&mut vars, info).inner().matches() {
if let Some(v) = in_loop(&mut vars, v).data.matches() { if let Some(v) = in_loop(&mut vars, v).inner().matches() {
oval = v; oval = v;
break; break;
} }
@ -270,10 +270,10 @@ impl RStatementEnum {
Self::Match(match_on, cases) => 'm: { Self::Match(match_on, cases) => 'm: {
for (case_condition, case_action) in cases { for (case_condition, case_action) in cases {
// [t] => Some(t), t => Some(t), [] | false => None // [t] => Some(t), t => Some(t), [] | false => None
if let Some(v) = case_condition.run(vars, info).data.matches() { if let Some(v) = case_condition.run(vars, info).inner().matches() {
let og = { std::mem::replace(&mut *vars[*match_on].lock().unwrap(), v) }; let og = { std::mem::replace(&mut vars[*match_on], v) };
let res = case_action.run(vars, info); let res = case_action.run(vars, info);
*vars[*match_on].lock().unwrap() = og; vars[*match_on] = og;
break 'm res; break 'm res;
} }
} }
@ -377,17 +377,19 @@ impl RScript {
} }
pub fn run(&self, args: Vec<String>) -> VData { pub fn run(&self, args: Vec<String>) -> VData {
let mut vars = Vec::with_capacity(self.info.vars); let mut vars = Vec::with_capacity(self.info.vars);
vars.push(am(VDataEnum::List( vars.push(
VDataEnum::List(
VSingleType::String.into(), VSingleType::String.into(),
args.into_iter() args.into_iter()
.map(|v| VDataEnum::String(v).to()) .map(|v| VDataEnum::String(v).to())
.collect(), .collect(),
) )
.to())); .to(),
);
for _i in 1..self.info.vars { for _i in 1..self.info.vars {
vars.push(am(VDataEnum::Tuple(vec![]).to())); vars.push(VDataEnum::Tuple(vec![]).to());
} }
self.main.run(&vars, &self.info) self.main.run(&mut vars, &self.info)
} }
pub fn info(&self) -> &GSInfo { pub fn info(&self) -> &GSInfo {
&self.info &self.info

View File

@ -5,16 +5,101 @@ use std::{
use super::{ use super::{
code_runnable::RFunction, code_runnable::RFunction,
global_info::{GlobalScriptInfo, GSInfo}, global_info::{GSInfo, GlobalScriptInfo},
val_type::{VSingleType, VType}, val_type::{VSingleType, VType},
}; };
#[derive(Clone, Debug, PartialEq)]
pub struct VData { pub struct VData {
pub data: VDataEnum, /// (_, mutable) - if false, behave as CopyOnWrite.
pub data: Arc<Mutex<(VDataEnum, bool)>>,
}
impl VData {
/// if self is mutable, assigns the new value to the mutex.
/// if self is immutable, creates a new mutex and sets self to mutable.
pub fn assign(&mut self, new_val: VDataEnum) {
{
let mut d = self.data.lock().unwrap();
if d.1 {
d.0 = new_val;
return;
}
}
*self = new_val.to();
}
pub fn inner_replace(&mut self, new_val: VDataEnum) -> VDataEnum {
{
let mut d = self.data.lock().unwrap();
if d.1 {
return std::mem::replace(&mut d.0, new_val);
}
}
let o = self.data().0.clone();
*self = new_val.to();
o
}
/// returns the contained VDataEnum. May or may not clone.
pub fn inner(self) -> VDataEnum {
self.data().0.clone()
}
/// ensures self is mutable, then returns a new instance of VData that is also mutable and uses the same Arc<Mutex<_>>.
pub fn clone_mut(&mut self) -> Self {
// if not mutable, copy and set to mutable.
self.make_mut();
// now, both self and the returned value are set to mutable and share the same mutex.
self.clone_mut_assume()
}
/// like clone_mut, but assumes self is already mutable, and therefor does not need to mutate self
/// as the Arc<Mutex<_>> will stay the same.
pub fn clone_mut_assume(&self) -> Self {
Self {
data: Arc::clone(&self.data),
}
}
pub fn ptr_eq(&self, rhs: &Self) -> bool {
Arc::ptr_eq(&self.data, &rhs.data)
}
/// makes self mutable. might clone.
pub fn make_mut(&mut self) -> &mut Self {
{
let mut s = self.data.lock().unwrap();
if !s.1 {
*s = (s.0.clone(), true);
}
}
self
}
pub fn data(&self) -> std::sync::MutexGuard<(VDataEnum, bool)> {
self.data.lock().unwrap()
}
}
impl Clone for VData {
fn clone(&self) -> Self {
let mut d = self.data.lock().unwrap();
// set to immutable, locking the data as-is.
d.1 = false;
// then return the same arc (-> avoid cloning)
Self {
data: Arc::clone(&self.data),
}
}
}
impl Debug for VData {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let d = self.data.lock().unwrap();
if d.1 {
write!(f, "(!mutable!):{:?}", d.0)
} else {
write!(f, "(immutable):{:?}", d.0)
}
}
}
impl PartialEq for VData {
fn eq(&self, other: &Self) -> bool {
self.data().0 == other.data().0
}
} }
#[derive(Clone, Debug)] #[derive(Debug)]
pub enum VDataEnum { pub enum VDataEnum {
Bool(bool), Bool(bool),
Int(isize), Int(isize),
@ -24,15 +109,34 @@ pub enum VDataEnum {
List(VType, Vec<VData>), List(VType, Vec<VData>),
Function(RFunction), Function(RFunction),
Thread(thread::VDataThread, VType), Thread(thread::VDataThread, VType),
Reference(Arc<Mutex<VData>>), Reference(VData),
EnumVariant(usize, Box<VData>), EnumVariant(usize, Box<VData>),
} }
impl Clone for VDataEnum {
fn clone(&self) -> Self {
match self {
// exception: don't clone the value AND don't use CoW,
// because we want to share the same Arc<Mutex<_>>.
Self::Reference(r) => Self::Reference(r.clone_mut_assume()),
// default impls
Self::Bool(b) => Self::Bool(*b),
Self::Int(i) => Self::Int(*i),
Self::Float(f) => Self::Float(*f),
Self::String(s) => Self::String(s.clone()),
Self::Tuple(v) => Self::Tuple(v.clone()),
Self::List(t, v) => Self::List(t.clone(), v.clone()),
Self::Function(f) => Self::Function(f.clone()),
Self::Thread(th, ty) => Self::Thread(th.clone(), ty.clone()),
Self::EnumVariant(v, d) => Self::EnumVariant(v.clone(), d.clone()),
}
}
}
impl PartialEq for VDataEnum { impl PartialEq for VDataEnum {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
match (self, other) { match (self, other) {
(Self::Reference(a), Self::Reference(b)) => *a.lock().unwrap() == *b.lock().unwrap(), (Self::Reference(a), Self::Reference(b)) => a == b,
(Self::Reference(a), b) => a.lock().unwrap().data == *b, (Self::Reference(a), b) => &a.data().0 == b,
(a, Self::Reference(b)) => *a == b.lock().unwrap().data, (a, Self::Reference(b)) => a == &b.data().0,
(Self::Bool(a), Self::Bool(b)) => *a == *b, (Self::Bool(a), Self::Bool(b)) => *a == *b,
(Self::Int(a), Self::Int(b)) => *a == *b, (Self::Int(a), Self::Int(b)) => *a == *b,
(Self::Float(a), Self::Float(b)) => *a == *b, (Self::Float(a), Self::Float(b)) => *a == *b,
@ -48,7 +152,7 @@ impl PartialEq for VDataEnum {
impl VData { impl VData {
pub fn safe_to_share(&self) -> bool { pub fn safe_to_share(&self) -> bool {
self.data.safe_to_share() self.data().0.safe_to_share()
} }
pub fn out(&self) -> VType { pub fn out(&self) -> VType {
VType { VType {
@ -56,7 +160,7 @@ impl VData {
} }
} }
pub fn out_single(&self) -> VSingleType { pub fn out_single(&self) -> VSingleType {
match &self.data { match &self.data().0 {
VDataEnum::Bool(..) => VSingleType::Bool, VDataEnum::Bool(..) => VSingleType::Bool,
VDataEnum::Int(..) => VSingleType::Int, VDataEnum::Int(..) => VSingleType::Int,
VDataEnum::Float(..) => VSingleType::Float, VDataEnum::Float(..) => VSingleType::Float,
@ -65,21 +169,23 @@ impl VData {
VDataEnum::List(t, _) => VSingleType::List(t.clone()), VDataEnum::List(t, _) => VSingleType::List(t.clone()),
VDataEnum::Function(f) => VSingleType::Function(f.input_output_map.clone()), VDataEnum::Function(f) => VSingleType::Function(f.input_output_map.clone()),
VDataEnum::Thread(_, o) => VSingleType::Thread(o.clone()), VDataEnum::Thread(_, o) => VSingleType::Thread(o.clone()),
VDataEnum::Reference(r) => r.lock().unwrap().out_single(), VDataEnum::Reference(r) => VSingleType::Reference(Box::new(r.out_single())),
VDataEnum::EnumVariant(e, v) => VSingleType::EnumVariant(*e, v.out()), VDataEnum::EnumVariant(e, v) => VSingleType::EnumVariant(*e, v.out()),
} }
} }
pub fn get(&self, i: usize) -> Option<Self> { pub fn get(&self, i: usize) -> Option<Self> {
self.data.get(i) self.data().0.get(i)
} }
pub fn noenum(self) -> Self { pub fn noenum(self) -> Self {
self.data.noenum() self.inner().noenum()
} }
} }
impl VDataEnum { impl VDataEnum {
pub fn to(self) -> VData { pub fn to(self) -> VData {
VData { data: self } VData {
data: Arc::new(Mutex::new((self, true))),
}
} }
} }
@ -113,7 +219,7 @@ impl VDataEnum {
None => None, None => None,
}, },
Self::Tuple(v) | Self::List(_, v) => v.get(i).cloned(), Self::Tuple(v) | Self::List(_, v) => v.get(i).cloned(),
Self::Reference(r) => r.lock().unwrap().get(i), Self::Reference(r) => r.get(i),
Self::EnumVariant(_, v) => v.get(i), Self::EnumVariant(_, v) => v.get(i),
} }
} }
@ -286,13 +392,17 @@ impl VDataEnum {
Self::Thread(..) => write!(f, "[TODO] THREAD"), Self::Thread(..) => write!(f, "[TODO] THREAD"),
Self::Reference(inner) => { Self::Reference(inner) => {
write!(f, "&")?; write!(f, "&")?;
inner.lock().unwrap().fmtgs(f, info) inner.fmtgs(f, info)
} }
Self::EnumVariant(variant, inner) => { Self::EnumVariant(variant, inner) => {
if let Some(name) = if let Some(info) = info { if let Some(name) = if let Some(info) = info {
info.enum_variants info.enum_variants.iter().find_map(|(name, id)| {
.iter() if id == variant {
.find_map(|(name, id)| if id == variant { Some(name) } else { None }) Some(name)
} else {
None
}
})
} else { } else {
None None
} { } {
@ -313,7 +423,7 @@ impl Display for VDataEnum {
impl VData { impl VData {
pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GlobalScriptInfo>) -> fmt::Result { pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GlobalScriptInfo>) -> fmt::Result {
self.data.fmtgs(f, info) self.data().0.fmtgs(f, info)
} }
} }
impl Display for VData { impl Display for VData {

View File

@ -14,7 +14,7 @@ pub fn run(tutor: &mut Tutor) {
", ",
)); ));
loop { loop {
match tutor.let_user_make_change().run(vec![]).data { match &tutor.let_user_make_change().run(vec![]).data().0 {
VDataEnum::Bool(true) => break, VDataEnum::Bool(true) => break,
other => { other => {
tutor.set_status(format!(" - Returned {} instead of true.", other)); tutor.set_status(format!(" - Returned {} instead of true.", other));

View File

@ -35,7 +35,7 @@ mul()
", ",
)); ));
loop { loop {
match tutor.let_user_make_change().run(vec![]).data { match &tutor.let_user_make_change().run(vec![]).data().0 {
VDataEnum::Int(160) => break, VDataEnum::Int(160) => break,
other => { other => {
tutor.set_status(format!(" - Returned {other} instead of 160")); tutor.set_status(format!(" - Returned {other} instead of 160"));

View File

@ -27,7 +27,7 @@ fn compute_sum(a int b int) {
", ",
)); ));
loop { loop {
match tutor.let_user_make_change().run(vec![]).data { match &tutor.let_user_make_change().run(vec![]).data().0 {
VDataEnum::Int(15) => break, VDataEnum::Int(15) => break,
other => { other => {
tutor.set_status(format!(" - Returned {} instead of 15.", other)); tutor.set_status(format!(" - Returned {} instead of 15.", other));

View File

@ -54,7 +54,7 @@ switch! words_in_string {}
true true
")); "));
loop { loop {
match tutor.let_user_make_change().run(vec![]).data { match &tutor.let_user_make_change().run(vec![]).data().0 {
VDataEnum::Tuple(v) if v.is_empty() => { VDataEnum::Tuple(v) if v.is_empty() => {
tutor.set_status(format!(" - Returned an empty tuple.")); tutor.set_status(format!(" - Returned an empty tuple."));
tutor.update(None); tutor.update(None);

View File

@ -24,7 +24,7 @@ pub fn run(tutor: &mut Tutor) {
// return any enum to return to the menu. // return any enum to return to the menu.
")); "));
loop { loop {
match tutor.let_user_make_change().run(vec![]).data { match &tutor.let_user_make_change().run(vec![]).data().0 {
VDataEnum::EnumVariant(..) => break, VDataEnum::EnumVariant(..) => break,
other => { other => {
tutor.set_status(format!(" - Returned {other} instead of an enum.")); tutor.set_status(format!(" - Returned {other} instead of an enum."));

View File

@ -18,9 +18,9 @@ five_less = sub(my_first_variable 5) // 10
", ",
)); ));
loop { loop {
match tutor.let_user_make_change().run(vec![]).data { match &tutor.let_user_make_change().run(vec![]).data().0 {
VDataEnum::String(name) if !name.is_empty() => { VDataEnum::String(name) if !name.is_empty() => {
tutor.i_name = Some(name); tutor.i_name = Some(name.to_owned());
break; break;
} }
VDataEnum::String(_) => { VDataEnum::String(_) => {

View File

@ -44,7 +44,7 @@ switch! first {
list.get(8) list.get(8)
")); "));
loop { loop {
match tutor.let_user_make_change().run(vec![]).data { match &tutor.let_user_make_change().run(vec![]).data().0 {
VDataEnum::Tuple(v) if !v.is_empty() => { VDataEnum::Tuple(v) if !v.is_empty() => {
break; break;
} }

View File

@ -24,9 +24,9 @@ go_to()
", ",
)); ));
loop { loop {
match tutor.let_user_make_change().run(vec![]).data { match &tutor.let_user_make_change().run(vec![]).data().0 {
VDataEnum::Int(pos) if pos != 0 => { VDataEnum::Int(pos) if *pos != 0 => {
tutor.current_pos = (pos.max(0) as usize).min(MAX_POS); tutor.current_pos = ((*pos).max(0) as usize).min(MAX_POS);
match tutor.current_pos { match tutor.current_pos {
0 => continue, 0 => continue,
1 => super::base_comments::run(&mut tutor), 1 => super::base_comments::run(&mut tutor),

View File

@ -45,7 +45,7 @@ false
i_name: None, i_name: None,
}; };
loop { loop {
if let VDataEnum::Bool(true) = tutor.let_user_make_change().run(vec![]).data { if let VDataEnum::Bool(true) = &tutor.let_user_make_change().run(vec![]).data().0 {
break; break;
} }
} }

View File

@ -15,7 +15,7 @@ fn run_all() {
let mut file = File::new(fs::read_to_string(file.path()).unwrap(), file.path()); let mut file = File::new(fs::read_to_string(file.path()).unwrap(), file.path());
// has to return true, otherwise the test will fail // has to return true, otherwise the test will fail
assert!(matches!( assert!(matches!(
parse::parse(&mut file).unwrap().run(vec![]).data, parse::parse(&mut file).unwrap().run(vec![]).data().0,
VDataEnum::Bool(true) VDataEnum::Bool(true)
)); ));
} }

View File

@ -57,8 +57,11 @@ fn main() {
.unwrap() .unwrap()
.1; .1;
my_lib.callbacks.run_function.consuming = Some(Box::new(move |msg| { my_lib.callbacks.run_function.consuming = Some(Box::new(move |msg| {
if let VDataEnum::String(url) = &msg.msg.args[0].data { let url = if let VDataEnum::String(url) = &msg.msg.args[0].data().0 {
let url = url.clone(); url.clone()
} else {
unreachable!()
};
std::thread::spawn(move || { std::thread::spawn(move || {
let r = match reqwest::blocking::get(url) { let r = match reqwest::blocking::get(url) {
Ok(response) => match response.text() { Ok(response) => match response.text() {
@ -89,9 +92,6 @@ fn main() {
}; };
msg.respond(r) msg.respond(r)
}); });
} else {
unreachable!()
}
})); }));
// because we handle all callbacks, this never returns Err(unhandeled message). // because we handle all callbacks, this never returns Err(unhandeled message).
// it returns Ok(()) if mers exits (i/o error in stdin/stdout), so we also exit if that happens. // it returns Ok(()) if mers exits (i/o error in stdin/stdout), so we also exit if that happens.