diff --git a/get_ref.mers b/get_ref.mers new file mode 100644 index 0000000..a3801ac --- /dev/null +++ b/get_ref.mers @@ -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() diff --git a/mers/src/libs/comms.rs b/mers/src/libs/comms.rs index 10165e2..efd433f 100644 --- a/mers/src/libs/comms.rs +++ b/mers/src/libs/comms.rs @@ -461,7 +461,7 @@ impl ByteData for VSingleType { } impl ByteDataA for VData { fn as_byte_data(&self, vec: &mut Vec) { - self.data.as_byte_data(vec) + self.data().0.as_byte_data(vec) } } impl ByteData for VData { @@ -469,9 +469,7 @@ impl ByteData for VData { where R: std::io::Read, { - Ok(Self { - data: ByteData::from_byte_data(data)?, - }) + Ok(VDataEnum::from_byte_data(data)?.to()) } } impl ByteDataA for VDataEnum { diff --git a/mers/src/parsing/parse.rs b/mers/src/parsing/parse.rs index ec32fa4..f5cb11f 100755 --- a/mers/src/parsing/parse.rs +++ b/mers/src/parsing/parse.rs @@ -910,8 +910,8 @@ pub mod implementation { let args = [out].into_iter().chain(args.into_iter()).collect(); SStatementEnum::FunctionCall(func, args).to() } - SStatementEnum::Value(vd) => match vd.data { - VDataEnum::Int(i) => SStatementEnum::IndexFixed(out, i as _).to(), + SStatementEnum::Value(vd) => match &vd.data().0 { + VDataEnum::Int(i) => SStatementEnum::IndexFixed(out, *i as _).to(), _ => { let mut context = vec![]; if chain_length > 0 { diff --git a/mers/src/script/builtins.rs b/mers/src/script/builtins.rs index 4753a1f..3192fc3 100755 --- a/mers/src/script/builtins.rs +++ b/mers/src/script/builtins.rs @@ -76,6 +76,7 @@ pub enum BuiltinFunction { Pop, Remove, Get, + GetRef, Len, // String Contains, @@ -138,6 +139,7 @@ impl BuiltinFunction { "pop" => Self::Pop, "remove" => Self::Remove, "get" => Self::Get, + "get_ref" => Self::GetRef, "len" => Self::Len, "contains" => Self::Contains, "starts_with" => Self::StartsWith, @@ -443,7 +445,7 @@ impl BuiltinFunction { } } // TODO! finish this - Self::Get | Self::Len => true, + Self::Get | Self::GetRef | Self::Len => true, Self::Substring => { if input.len() >= 2 && input.len() <= 3 { let (s, start) = (&input[0], &input[1]); @@ -598,6 +600,26 @@ impl BuiltinFunction { 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::FsList => VType { types: vec![ @@ -726,67 +748,87 @@ impl BuiltinFunction { }, } } - pub fn run( - &self, - args: &Vec, - vars: &Vec>>, - info: &GSInfo, - ) -> VData { + pub fn run(&self, args: &Vec, vars: &mut Vec, info: &GSInfo) -> VData { match self { - Self::Assume1 => match args[0].run(vars, info).data { - VDataEnum::Tuple(mut v) => { - if let Some(v) = v.pop() { - v - } else { - panic!( - "ASSUMPTION FAILED: assume1 :: {}", - if args.len() > 1 { - if let VDataEnum::String(v) = args[1].run(vars, info).data { - v + Self::Assume1 => { + 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() { + v + } else { + let msg = if args.len() > 1 { + let a1 = args[1].run(vars, info); + let a1 = a1.data(); + if let VDataEnum::String(v) = &a1.0 { + Some(v.to_owned()) } else { - String::new() + None } } 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 => { - let data = args[0].run(vars, info); - match data.data { - VDataEnum::EnumVariant(..) => panic!( - "ASSUMPTION FAILED: assume_no_enum :: found {} :: {}", - data.gsi(info.clone()), - if args.len() > 1 { - if let VDataEnum::String(v) = args[1].run(vars, info).data { - v + let msg = if args.len() > 1 { + if let VDataEnum::String(v) = args[1].run(vars, info).inner() { + Some(v.to_owned()) + } else { + None + } + } else { + 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 { String::new() } - } else { - String::new() - } - ), - d => d.to(), + ) + } + _ => { + drop(a0d); + a0 + } } } 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(), None => VDataEnum::Tuple(vec![]).to(), }, Self::Clone => { - if let VDataEnum::Reference(r) = args[0].run(vars, info).data { - r.lock().unwrap().clone() + if let VDataEnum::Reference(r) = &args[0].run(vars, info).data().0 { + r.clone() } else { unreachable!() } } 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); VDataEnum::Tuple(vec![]).to() } else { @@ -794,7 +836,7 @@ impl BuiltinFunction { } } 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"))] println!("{}", arg); #[cfg(feature = "nushell_plugin")] @@ -831,13 +873,13 @@ impl BuiltinFunction { VDataEnum::String(args[0].run(vars, info).gsi(info.clone()).to_string()).to() } 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() { text = text.replace( &format!("{{{i}}}"), &format!( "{}", - if let VDataEnum::String(v) = arg.run(vars, info).data { + if let VDataEnum::String(v) = &arg.run(vars, info).data().0 { v } else { unreachable!() @@ -852,7 +894,7 @@ impl BuiltinFunction { } BuiltinFunction::ParseInt => { 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() { VDataEnum::Int(s).to() } else { @@ -867,7 +909,7 @@ impl BuiltinFunction { } BuiltinFunction::ParseFloat => { 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() { VDataEnum::Float(s).to() } else { @@ -882,13 +924,13 @@ impl BuiltinFunction { } BuiltinFunction::Run => { 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 { unreachable!() } for (i, var) in f.inputs.iter().enumerate() { let val = args[i + 1].run(vars, info); - *vars[*var].lock().unwrap() = val; + vars[*var] = val; } f.run(vars, info) } else { @@ -900,7 +942,7 @@ impl BuiltinFunction { } BuiltinFunction::Thread => { 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 { unreachable!() } @@ -910,13 +952,13 @@ impl BuiltinFunction { for (i, var) in f.inputs.iter().enumerate() { let val = args[i + 1].run(vars, info); 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 libs = info.clone(); VDataEnum::Thread( VDataThreadEnum::Running(std::thread::spawn(move || { - f.run(&thread_vars, &libs) + f.run(&mut thread_vars, &libs) })) .to(), out_type, @@ -931,7 +973,7 @@ impl BuiltinFunction { } BuiltinFunction::Await => { 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() } else { unreachable!() @@ -942,9 +984,9 @@ impl BuiltinFunction { } BuiltinFunction::Sleep => { if args.len() == 1 { - match args[0].run(vars, info).data { - VDataEnum::Int(v) => std::thread::sleep(Duration::from_secs(v as _)), - VDataEnum::Float(v) => std::thread::sleep(Duration::from_secs_f64(v)), + match &args[0].run(vars, info).data().0 { + VDataEnum::Int(v) => std::thread::sleep(Duration::from_secs(*v as _)), + VDataEnum::Float(v) => std::thread::sleep(Duration::from_secs_f64(*v)), _ => unreachable!(), } VDataEnum::Tuple(vec![]).to() @@ -954,8 +996,8 @@ impl BuiltinFunction { } Self::Exit => { if let Some(s) = args.first() { - if let VDataEnum::Int(v) = s.run(vars, info).data { - std::process::exit(v as _); + if let VDataEnum::Int(v) = &s.run(vars, info).data().0 { + std::process::exit(*v as _); } else { std::process::exit(1); } @@ -965,7 +1007,7 @@ impl BuiltinFunction { } Self::FsList => { 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 { eprintln!("NOT YET IMPLEMENTED (TODO!): fs_list advanced filters") } @@ -1003,7 +1045,7 @@ impl BuiltinFunction { } Self::FsRead => { 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) { Ok(data) => VDataEnum::List( VSingleType::Int.into(), @@ -1027,9 +1069,10 @@ impl BuiltinFunction { } Self::FsWrite => { if args.len() > 1 { - if let (VDataEnum::String(path), VDataEnum::List(_, data)) = - (args[0].run(vars, info).data, args[1].run(vars, info).data) - { + if let (VDataEnum::String(path), VDataEnum::List(_, data)) = ( + &args[0].run(vars, info).data().0, + &args[1].run(vars, info).data().0, + ) { if let Some(bytes) = vdata_to_bytes(&data) { let file_path: PathBuf = path.into(); if let Some(p) = file_path.parent() { @@ -1057,7 +1100,7 @@ impl BuiltinFunction { } Self::BytesToString => { 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) { match String::from_utf8(bytes) { Ok(v) => VDataEnum::String(v).to(), @@ -1092,7 +1135,7 @@ impl BuiltinFunction { } Self::StringToBytes => { 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( VSingleType::Int.into(), s.bytes().map(|v| VDataEnum::Int(v as isize).to()).collect(), @@ -1107,12 +1150,12 @@ impl BuiltinFunction { } Self::RunCommand | Self::RunCommandGetBytes => { 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); 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 { - if let VDataEnum::String(v) = arg.data { + if let VDataEnum::String(v) = &arg.data().0 { command.arg(v); } else { unreachable!("run_command second arg not [string].") @@ -1172,19 +1215,19 @@ impl BuiltinFunction { } } 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() } else { unreachable!() } } Self::And => { - if let VDataEnum::Bool(a) = args[0].run(vars, info).data { - if a == false { + if let VDataEnum::Bool(a) = &args[0].run(vars, info).data().0 { + if *a == false { VDataEnum::Bool(false).to() } else { - if let VDataEnum::Bool(b) = args[1].run(vars, info).data { - VDataEnum::Bool(b).to() + if let VDataEnum::Bool(b) = &args[1].run(vars, info).data().0 { + VDataEnum::Bool(*b).to() } else { unreachable!() } @@ -1194,12 +1237,12 @@ impl BuiltinFunction { } } Self::Or => { - if let VDataEnum::Bool(a) = args[0].run(vars, info).data { - if a == true { + if let VDataEnum::Bool(a) = &args[0].run(vars, info).data().0 { + if *a == true { VDataEnum::Bool(true).to() } else { - if let VDataEnum::Bool(b) = args[1].run(vars, info).data { - VDataEnum::Bool(b).to() + if let VDataEnum::Bool(b) = &args[1].run(vars, info).data().0 { + VDataEnum::Bool(*b).to() } else { unreachable!() } @@ -1210,7 +1253,10 @@ impl BuiltinFunction { } Self::Add => { 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)) => { a.push_str(b.as_str()); VDataEnum::String(a).to() @@ -1231,13 +1277,16 @@ impl BuiltinFunction { } Self::Sub => { 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::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 - b as f64).to() + VDataEnum::Float(*a - *b as f64).to() } (VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Float(a - b).to(), _ => unreachable!("sub: not a number"), @@ -1248,13 +1297,16 @@ impl BuiltinFunction { } Self::Mul => { 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::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 * b as f64).to() + VDataEnum::Float(a * *b as f64).to() } (VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Float(a * b).to(), _ => unreachable!("mul: not a number"), @@ -1265,13 +1317,16 @@ impl BuiltinFunction { } Self::Div => { 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::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 / b as f64).to() + VDataEnum::Float(a / *b as f64).to() } (VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Float(a / b).to(), _ => unreachable!("div: not a number"), @@ -1282,13 +1337,16 @@ impl BuiltinFunction { } Self::Mod => { 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::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 % b as f64).to() + VDataEnum::Float(a % *b as f64).to() } (VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Float(a % b).to(), _ => unreachable!("mod: not a number"), @@ -1299,23 +1357,26 @@ impl BuiltinFunction { } Self::Pow => { if args.len() == 2 { - match (args[0].run(vars, info).data, args[1].run(vars, info).data) { - (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(if b == 0 { + match ( + &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 - } else if b > 0 { - a.pow(b as _) + } else if *b > 0 { + (*a).pow(*b as _) } else { 0 }) .to(), (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.powi(b as _)).to() + VDataEnum::Float((*a).powi(*b as _)).to() } (VDataEnum::Float(a), VDataEnum::Float(b)) => { - VDataEnum::Float(a.powf(b)).to() + VDataEnum::Float((*a).powf(*b)).to() } _ => unreachable!("pow: not a number"), } @@ -1332,15 +1393,18 @@ impl BuiltinFunction { } Self::Gt => { if args.len() == 2 { - match (args[0].run(vars, info).data, args[1].run(vars, info).data) { - (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a > b).to(), + match ( + &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::Bool(a as f64 > b).to() + VDataEnum::Bool(*a as f64 > *b).to() } (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"), } } else { @@ -1349,15 +1413,18 @@ impl BuiltinFunction { } Self::Lt => { if args.len() == 2 { - match (args[0].run(vars, info).data, args[1].run(vars, info).data) { - (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a < b).to(), + match ( + &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::Bool((a as f64) < b).to() + VDataEnum::Bool((*a as f64) < *b).to() } (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"), } } else { @@ -1366,15 +1433,20 @@ impl BuiltinFunction { } Self::Gtoe => { if args.len() == 2 { - match (args[0].run(vars, info).data, args[1].run(vars, info).data) { - (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a >= b).to(), + match ( + &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::Bool(a as f64 >= b).to() + VDataEnum::Bool(*a as f64 >= *b).to() } (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"), } } else { @@ -1383,15 +1455,20 @@ impl BuiltinFunction { } Self::Ltoe => { if args.len() == 2 { - match (args[0].run(vars, info).data, args[1].run(vars, info).data) { - (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a <= b).to(), + match ( + &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::Bool(a as f64 <= b).to() + VDataEnum::Bool(*a as f64 <= *b).to() } (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"), } } else { @@ -1400,16 +1477,19 @@ impl BuiltinFunction { } Self::Min => { if args.len() == 2 { - match (args[0].run(vars, info).data, args[1].run(vars, info).data) { - (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a.min(b)).to(), + match ( + &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::Float((a as f64).min(b)).to() + VDataEnum::Float((*a as f64).min(*b)).to() } (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.min(b)).to() + VDataEnum::Float((*a).min(*b)).to() } _ => unreachable!("min: not a number"), } @@ -1419,16 +1499,19 @@ impl BuiltinFunction { } Self::Max => { if args.len() == 2 { - match (args[0].run(vars, info).data, args[1].run(vars, info).data) { - (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a.max(b)).to(), + match ( + &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::Float((a as f64).max(b)).to() + VDataEnum::Float((*a as f64).max(*b)).to() } (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.max(b)).to() + VDataEnum::Float((*a).max(*b)).to() } _ => unreachable!("max: not a number"), } @@ -1438,8 +1521,9 @@ impl BuiltinFunction { } Self::Push => { if args.len() == 2 { - if let VDataEnum::Reference(v) = args[0].run(vars, info).data { - if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { + // Since this is a reference, it is safe to assume that make_mut() would do nothing. + 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)); } VDataEnum::Tuple(vec![]).to() @@ -1452,11 +1536,13 @@ impl BuiltinFunction { } Self::Insert => { if args.len() == 3 { - if let (VDataEnum::Reference(v), VDataEnum::Int(i)) = - (args[0].run(vars, info).data, args[2].run(vars, info).data) - { - if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { - v.insert(i as _, args[1].run(vars, info)); + // this being a reference means we wont need to call make_mut() later, so a .as_ref() borrow is enough. + if let (VDataEnum::Reference(v), VDataEnum::Int(i)) = ( + &args[0].run(vars, info).data().0, + &args[2].run(vars, info).data().0, + ) { + if let VDataEnum::List(_, v) = &mut v.data().0 { + v.insert(*i as _, args[1].run(vars, info)); } VDataEnum::Tuple(vec![]).to() } else { @@ -1468,8 +1554,9 @@ impl BuiltinFunction { } Self::Pop => { if args.len() == 1 { - if let VDataEnum::Reference(v) = args[0].run(vars, info).data { - if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { + // this being a reference means we wont need to call make_mut() later, so a .as_ref() borrow is enough. + 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() { VDataEnum::Tuple(vec![v]) } else { @@ -1488,12 +1575,14 @@ impl BuiltinFunction { } Self::Remove => { if args.len() == 2 { - if let (VDataEnum::Reference(v), VDataEnum::Int(i)) = - (args[0].run(vars, info).data, args[1].run(vars, info).data) - { - if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { - if v.len() > i as _ && i >= 0 { - let v = v.remove(i as _); + // this being a reference means we wont need to call make_mut() later, so a .as_ref() borrow is enough. + if let (VDataEnum::Reference(v), VDataEnum::Int(i)) = ( + &args[0].run(vars, info).data().0, + &args[1].run(vars, info).data().0, + ) { + 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() } else { VDataEnum::Tuple(vec![]).to() @@ -1510,21 +1599,22 @@ impl BuiltinFunction { } Self::Get => { if args.len() == 2 { - if let (container, VDataEnum::Int(i)) = - (args[0].run(vars, info).data, args[1].run(vars, info).data) - { - if i >= 0 { + if let (container, VDataEnum::Int(i)) = ( + &args[0].run(vars, info).data().0, + &args[1].run(vars, info).data().0, + ) { + if *i >= 0 { match match container { - VDataEnum::Reference(v) => match &v.lock().unwrap().data { + VDataEnum::Reference(v) => match &v.data().0 { 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: reference to something other than list/tuple" ), }, 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"), } { @@ -1541,9 +1631,40 @@ impl BuiltinFunction { 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 => { 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::Tuple(v) => v.len(), VDataEnum::List(_, v) => v.len(), @@ -1556,8 +1677,8 @@ impl BuiltinFunction { } Self::Contains => { if args.len() == 2 { - if let VDataEnum::String(a1) = args[0].run(vars, info).data { - if let VDataEnum::String(a2) = args[1].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().0 { VDataEnum::Bool(a1.contains(a2.as_str())).to() } else { unreachable!() @@ -1571,8 +1692,8 @@ impl BuiltinFunction { } Self::StartsWith => { if args.len() == 2 { - if let VDataEnum::String(a1) = args[0].run(vars, info).data { - if let VDataEnum::String(a2) = args[1].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().0 { VDataEnum::Bool(a1.starts_with(a2.as_str())).to() } else { unreachable!() @@ -1586,8 +1707,8 @@ impl BuiltinFunction { } Self::EndsWith => { if args.len() == 2 { - if let VDataEnum::String(a1) = args[0].run(vars, info).data { - if let VDataEnum::String(a2) = args[1].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().0 { VDataEnum::Bool(a1.ends_with(a2.as_str())).to() } else { unreachable!() @@ -1622,41 +1743,38 @@ impl BuiltinFunction { VDataEnum::Tuple(vec![]).to() } } - match (find_in.data, pat.data) { - (VDataEnum::String(a), VDataEnum::String(b)) => find(&a, &b), - (VDataEnum::String(a), VDataEnum::Reference(b)) => { - match &b.lock().unwrap().data { - VDataEnum::String(b) => find(&a, b), - _ => unreachable!(), - } - } - (VDataEnum::Reference(a), VDataEnum::String(b)) => { - match &a.lock().unwrap().data { - VDataEnum::String(a) => find(a, &b), - _ => unreachable!(), - } - } + let o = match (&find_in.data().0, &pat.data().0) { + (VDataEnum::String(a), VDataEnum::String(b)) => find(a, b), + (VDataEnum::String(a), VDataEnum::Reference(b)) => match &b.data().0 { + VDataEnum::String(b) => find(a, b), + _ => unreachable!(), + }, + (VDataEnum::Reference(a), VDataEnum::String(b)) => match &a.data().0 { + VDataEnum::String(a) => find(a, b), + _ => unreachable!(), + }, (VDataEnum::Reference(a), VDataEnum::Reference(b)) => { - if Arc::ptr_eq(&a, &b) { + if a.ptr_eq(b) { // 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) VDataEnum::Int(0).to() } 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), _ => unreachable!(), } } } _ => unreachable!(), - } + }; + o } else { unreachable!() } } Self::Trim => { 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() } else { unreachable!() @@ -1667,18 +1785,18 @@ impl BuiltinFunction { } Self::Substring => { 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 { unreachable!() } - let left = if let VDataEnum::Int(left) = args[1].run(vars, info).data { - left + let left = if let VDataEnum::Int(left) = &args[1].run(vars, info).data().0 { + *left } else { unreachable!() }; let len = if args.len() == 3 { - if let VDataEnum::Int(len) = args[2].run(vars, info).data { - Some(len) + if let VDataEnum::Int(len) = &args[2].run(vars, info).data().0 { + Some(*len) } else { unreachable!() } @@ -1714,20 +1832,21 @@ impl BuiltinFunction { } Self::Replace => { if let (VDataEnum::String(a), VDataEnum::String(b), VDataEnum::String(c)) = ( - args[0].run(vars, info).data, - args[1].run(vars, info).data, - args[2].run(vars, info).data, + &args[0].run(vars, info).data().0, + &args[1].run(vars, info).data().0, + &args[2].run(vars, info).data().0, ) { - VDataEnum::String(a.replace(&b, &c)).to() + VDataEnum::String(a.replace(b, c)).to() } else { unreachable!() } } Self::Regex => { if args.len() == 2 { - if let (VDataEnum::String(a), VDataEnum::String(regex)) = - (args[0].run(vars, info).data, args[1].run(vars, info).data) - { + if let (VDataEnum::String(a), VDataEnum::String(regex)) = ( + &args[0].run(vars, info).data().0, + &args[1].run(vars, info).data().0, + ) { match regex::Regex::new(regex.as_str()) { Ok(regex) => VDataEnum::List( VSingleType::String.to(), @@ -1757,9 +1876,9 @@ impl BuiltinFunction { fn vdata_to_bytes(vd: &Vec) -> Option> { let mut bytes = Vec::with_capacity(vd.len()); for b in vd { - if let VDataEnum::Int(b) = b.data { - bytes.push(if 0 <= b && b <= u8::MAX as isize { - b as u8 + if let VDataEnum::Int(b) = &b.data().0 { + bytes.push(if 0 <= *b && *b <= u8::MAX as isize { + *b as u8 } else if b.is_negative() { 0 } else { diff --git a/mers/src/script/code_runnable.rs b/mers/src/script/code_runnable.rs index f8d4d10..8a1b220 100755 --- a/mers/src/script/code_runnable.rs +++ b/mers/src/script/code_runnable.rs @@ -8,17 +8,12 @@ use super::{ val_type::{VSingleType, VType}, }; -type Am = Arc>; -fn am(i: T) -> Am { - Arc::new(Mutex::new(i)) -} - #[derive(Clone, Debug)] pub struct RBlock { pub statements: Vec, } impl RBlock { - pub fn run(&self, vars: &Vec>, info: &GSInfo) -> VData { + pub fn run(&self, vars: &mut Vec, info: &GSInfo) -> VData { let mut last = None; for statement in &self.statements { last = Some(statement.run(vars, info)); @@ -48,7 +43,7 @@ pub struct RFunction { pub block: RBlock, } impl RFunction { - pub fn run(&self, vars: &Vec>, info: &GSInfo) -> VData { + pub fn run(&self, vars: &mut Vec, info: &GSInfo) -> VData { self.block.run(vars, info) } pub fn out(&self, input_types: &Vec) -> VType { @@ -91,19 +86,22 @@ pub struct RStatement { pub force_output_type: Option, } impl RStatement { - pub fn run(&self, vars: &Vec>, info: &GSInfo) -> VData { + pub fn run(&self, vars: &mut Vec, info: &GSInfo) -> VData { let out = self.statement.run(vars, info); if let Some((v, derefs)) = self.output_to { - let mut val = vars[v].clone(); - for _ in 0..derefs { - let v = if let VDataEnum::Reference(v) = &val.lock().unwrap().data { - v.clone() + let mut val = dereference_n(&mut vars[v], derefs); + fn dereference_n(d: &mut VData, n: usize) -> VData { + if n > 0 { + if let VDataEnum::Reference(v) = &mut d.data.lock().unwrap().0 { + dereference_n(v, n - 1) + } else { + unreachable!("dereferencing something that isn't a reference in assignment") + } } else { - unreachable!("dereferencing something that isn't a reference in assignment") - }; - val = v; + d.clone_mut() + } } - *val.lock().unwrap() = out; + val.assign(out.inner()); VDataEnum::Tuple(vec![]).to() } else { out @@ -142,7 +140,7 @@ pub enum RStatementEnum { EnumVariant(usize, RStatement), } impl RStatementEnum { - pub fn run(&self, vars: &Vec>, info: &GSInfo) -> VData { + pub fn run(&self, vars: &mut Vec, info: &GSInfo) -> VData { match self { Self::Value(v) => v.clone(), Self::Tuple(v) => { @@ -164,14 +162,16 @@ impl RStatementEnum { } Self::Variable(v, _, is_ref) => { if *is_ref { - VDataEnum::Reference(vars[*v].clone()).to() + // shared mutability (clone_mut) + VDataEnum::Reference(vars[*v].clone_mut()).to() } else { - vars[*v].lock().unwrap().clone() + // Copy on Write (clone) + vars[*v].clone() } } Self::FunctionCall(func, args) => { 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) } @@ -180,8 +180,8 @@ impl RStatementEnum { .run_fn(*fnid, args.iter().map(|arg| arg.run(vars, info)).collect()), Self::Block(b) => b.run(vars, info), Self::If(c, t, e) => { - if let VDataEnum::Bool(v) = c.run(vars, info).data { - if v { + if let VDataEnum::Bool(v) = &c.run(vars, info).data().0 { + if *v { t.run(vars, info) } else { if let Some(e) = e { @@ -196,7 +196,7 @@ impl RStatementEnum { } Self::Loop(c) => loop { // 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; } }, @@ -204,17 +204,17 @@ impl RStatementEnum { // matching values also break with value from a for loop. let c = c.run(vars, info); let mut vars = vars.clone(); - let in_loop = |vars: &mut Vec>>, c| { - vars[*v] = Arc::new(Mutex::new(c)); - b.run(&vars, info) + let in_loop = |vars: &mut Vec, c| { + vars[*v] = c; + b.run(vars, info) }; let mut oval = VDataEnum::Tuple(vec![]).to(); - match c.data { + match &c.data().0 { VDataEnum::Int(v) => { - for i in 0..v { + for i in 0..*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; break; @@ -225,7 +225,7 @@ impl RStatementEnum { for ch in v.chars() { if let Some(v) = in_loop(&mut vars, VDataEnum::String(ch.to_string()).to()) - .data + .inner() .matches() { oval = v; @@ -235,15 +235,15 @@ impl RStatementEnum { } VDataEnum::Tuple(v) | VDataEnum::List(_, 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; break; } } } VDataEnum::Function(f) => loop { - if let Some(v) = f.run(&vars, info).data.matches() { - if let Some(v) = in_loop(&mut vars, v).data.matches() { + if let Some(v) = f.run(&mut vars, info).inner().matches() { + if let Some(v) = in_loop(&mut vars, v).inner().matches() { oval = v; break; } @@ -270,10 +270,10 @@ impl RStatementEnum { Self::Match(match_on, cases) => 'm: { for (case_condition, case_action) in cases { // [t] => Some(t), t => Some(t), [] | false => None - if let Some(v) = case_condition.run(vars, info).data.matches() { - let og = { std::mem::replace(&mut *vars[*match_on].lock().unwrap(), v) }; + if let Some(v) = case_condition.run(vars, info).inner().matches() { + let og = { std::mem::replace(&mut vars[*match_on], v) }; let res = case_action.run(vars, info); - *vars[*match_on].lock().unwrap() = og; + vars[*match_on] = og; break 'm res; } } @@ -377,17 +377,19 @@ impl RScript { } pub fn run(&self, args: Vec) -> VData { let mut vars = Vec::with_capacity(self.info.vars); - vars.push(am(VDataEnum::List( - VSingleType::String.into(), - args.into_iter() - .map(|v| VDataEnum::String(v).to()) - .collect(), - ) - .to())); + vars.push( + VDataEnum::List( + VSingleType::String.into(), + args.into_iter() + .map(|v| VDataEnum::String(v).to()) + .collect(), + ) + .to(), + ); 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 { &self.info diff --git a/mers/src/script/to_runnable.rs b/mers/src/script/to_runnable.rs index 7d86373..4c682fc 100755 --- a/mers/src/script/to_runnable.rs +++ b/mers/src/script/to_runnable.rs @@ -75,7 +75,7 @@ impl ToRunnableError { Self::CannotDereferenceTypeNTimes(og_type, derefs_wanted, last_valid_type) => { write!(f, "Cannot dereference type ")?; og_type.fmtgs(f, info)?; - write!(f, "{derefs_wanted} times (stopped at ")?; + write!(f, " {derefs_wanted} times (stopped at ")?; last_valid_type.fmtgs(f, info); write!(f, ")")?; Ok(()) diff --git a/mers/src/script/val_data.rs b/mers/src/script/val_data.rs index c1b06c6..78fdcb2 100755 --- a/mers/src/script/val_data.rs +++ b/mers/src/script/val_data.rs @@ -5,16 +5,101 @@ use std::{ use super::{ code_runnable::RFunction, - global_info::{GlobalScriptInfo, GSInfo}, + global_info::{GSInfo, GlobalScriptInfo}, val_type::{VSingleType, VType}, }; -#[derive(Clone, Debug, PartialEq)] pub struct VData { - pub data: VDataEnum, + /// (_, mutable) - if false, behave as CopyOnWrite. + pub data: Arc>, +} +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>. + 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> 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 { Bool(bool), Int(isize), @@ -24,15 +109,34 @@ pub enum VDataEnum { List(VType, Vec), Function(RFunction), Thread(thread::VDataThread, VType), - Reference(Arc>), + Reference(VData), EnumVariant(usize, Box), } +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>. + 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 { fn eq(&self, other: &Self) -> bool { match (self, other) { - (Self::Reference(a), Self::Reference(b)) => *a.lock().unwrap() == *b.lock().unwrap(), - (Self::Reference(a), b) => a.lock().unwrap().data == *b, - (a, Self::Reference(b)) => *a == b.lock().unwrap().data, + (Self::Reference(a), Self::Reference(b)) => a == b, + (Self::Reference(a), b) => &a.data().0 == b, + (a, Self::Reference(b)) => a == &b.data().0, (Self::Bool(a), Self::Bool(b)) => *a == *b, (Self::Int(a), Self::Int(b)) => *a == *b, (Self::Float(a), Self::Float(b)) => *a == *b, @@ -48,7 +152,7 @@ impl PartialEq for VDataEnum { impl VData { pub fn safe_to_share(&self) -> bool { - self.data.safe_to_share() + self.data().0.safe_to_share() } pub fn out(&self) -> VType { VType { @@ -56,7 +160,7 @@ impl VData { } } pub fn out_single(&self) -> VSingleType { - match &self.data { + match &self.data().0 { VDataEnum::Bool(..) => VSingleType::Bool, VDataEnum::Int(..) => VSingleType::Int, VDataEnum::Float(..) => VSingleType::Float, @@ -65,21 +169,23 @@ impl VData { VDataEnum::List(t, _) => VSingleType::List(t.clone()), VDataEnum::Function(f) => VSingleType::Function(f.input_output_map.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()), } } pub fn get(&self, i: usize) -> Option { - self.data.get(i) + self.data().0.get(i) } pub fn noenum(self) -> Self { - self.data.noenum() + self.inner().noenum() } } impl VDataEnum { pub fn to(self) -> VData { - VData { data: self } + VData { + data: Arc::new(Mutex::new((self, true))), + } } } @@ -113,7 +219,7 @@ impl VDataEnum { None => None, }, 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), } } @@ -286,13 +392,17 @@ impl VDataEnum { Self::Thread(..) => write!(f, "[TODO] THREAD"), Self::Reference(inner) => { write!(f, "&")?; - inner.lock().unwrap().fmtgs(f, info) + inner.fmtgs(f, info) } Self::EnumVariant(variant, inner) => { if let Some(name) = if let Some(info) = info { - info.enum_variants - .iter() - .find_map(|(name, id)| if id == variant { Some(name) } else { None }) + info.enum_variants.iter().find_map(|(name, id)| { + if id == variant { + Some(name) + } else { + None + } + }) } else { None } { @@ -313,7 +423,7 @@ impl Display for VDataEnum { impl VData { 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 { diff --git a/mers/src/tutor/base_comments.rs b/mers/src/tutor/base_comments.rs index 63c22b7..3304d42 100755 --- a/mers/src/tutor/base_comments.rs +++ b/mers/src/tutor/base_comments.rs @@ -14,7 +14,7 @@ pub fn run(tutor: &mut Tutor) { ", )); loop { - match tutor.let_user_make_change().run(vec![]).data { + match &tutor.let_user_make_change().run(vec![]).data().0 { VDataEnum::Bool(true) => break, other => { tutor.set_status(format!(" - Returned {} instead of true.", other)); diff --git a/mers/src/tutor/base_functions.rs b/mers/src/tutor/base_functions.rs index 0c0bcc3..ecefe85 100755 --- a/mers/src/tutor/base_functions.rs +++ b/mers/src/tutor/base_functions.rs @@ -35,7 +35,7 @@ mul() ", )); loop { - match tutor.let_user_make_change().run(vec![]).data { + match &tutor.let_user_make_change().run(vec![]).data().0 { VDataEnum::Int(160) => break, other => { tutor.set_status(format!(" - Returned {other} instead of 160")); diff --git a/mers/src/tutor/base_return.rs b/mers/src/tutor/base_return.rs index 308a24d..0b06252 100755 --- a/mers/src/tutor/base_return.rs +++ b/mers/src/tutor/base_return.rs @@ -27,7 +27,7 @@ fn compute_sum(a int b int) { ", )); loop { - match tutor.let_user_make_change().run(vec![]).data { + match &tutor.let_user_make_change().run(vec![]).data().0 { VDataEnum::Int(15) => break, other => { tutor.set_status(format!(" - Returned {} instead of 15.", other)); diff --git a/mers/src/tutor/base_types.rs b/mers/src/tutor/base_types.rs index 7692fa2..941e728 100755 --- a/mers/src/tutor/base_types.rs +++ b/mers/src/tutor/base_types.rs @@ -54,7 +54,7 @@ switch! words_in_string {} true ")); 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() => { tutor.set_status(format!(" - Returned an empty tuple.")); tutor.update(None); diff --git a/mers/src/tutor/base_values.rs b/mers/src/tutor/base_values.rs index 5c772f0..dddba19 100755 --- a/mers/src/tutor/base_values.rs +++ b/mers/src/tutor/base_values.rs @@ -24,7 +24,7 @@ pub fn run(tutor: &mut Tutor) { // return any enum to return to the menu. ")); loop { - match tutor.let_user_make_change().run(vec![]).data { + match &tutor.let_user_make_change().run(vec![]).data().0 { VDataEnum::EnumVariant(..) => break, other => { tutor.set_status(format!(" - Returned {other} instead of an enum.")); diff --git a/mers/src/tutor/base_variables.rs b/mers/src/tutor/base_variables.rs index bb55c10..a1b01e4 100755 --- a/mers/src/tutor/base_variables.rs +++ b/mers/src/tutor/base_variables.rs @@ -18,9 +18,9 @@ five_less = sub(my_first_variable 5) // 10 ", )); 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() => { - tutor.i_name = Some(name); + tutor.i_name = Some(name.to_owned()); break; } VDataEnum::String(_) => { diff --git a/mers/src/tutor/error_handling.rs b/mers/src/tutor/error_handling.rs index 8ba71f3..126abd6 100755 --- a/mers/src/tutor/error_handling.rs +++ b/mers/src/tutor/error_handling.rs @@ -44,7 +44,7 @@ switch! first { list.get(8) ")); 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() => { break; } diff --git a/mers/src/tutor/menu.rs b/mers/src/tutor/menu.rs index c3a1750..d98c4fa 100755 --- a/mers/src/tutor/menu.rs +++ b/mers/src/tutor/menu.rs @@ -24,9 +24,9 @@ go_to() ", )); loop { - match tutor.let_user_make_change().run(vec![]).data { - VDataEnum::Int(pos) if pos != 0 => { - tutor.current_pos = (pos.max(0) as usize).min(MAX_POS); + match &tutor.let_user_make_change().run(vec![]).data().0 { + VDataEnum::Int(pos) if *pos != 0 => { + tutor.current_pos = ((*pos).max(0) as usize).min(MAX_POS); match tutor.current_pos { 0 => continue, 1 => super::base_comments::run(&mut tutor), diff --git a/mers/src/tutor/mod.rs b/mers/src/tutor/mod.rs index e9d9815..af80484 100755 --- a/mers/src/tutor/mod.rs +++ b/mers/src/tutor/mod.rs @@ -45,7 +45,7 @@ false i_name: None, }; 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; } } diff --git a/mers/tests/test_in_mers.rs b/mers/tests/test_in_mers.rs index 5192de1..e4475b2 100644 --- a/mers/tests/test_in_mers.rs +++ b/mers/tests/test_in_mers.rs @@ -15,7 +15,7 @@ fn run_all() { let mut file = File::new(fs::read_to_string(file.path()).unwrap(), file.path()); // has to return true, otherwise the test will fail assert!(matches!( - parse::parse(&mut file).unwrap().run(vec![]).data, + parse::parse(&mut file).unwrap().run(vec![]).data().0, VDataEnum::Bool(true) )); } diff --git a/mers_libs/http_requests_v1/src/main.rs b/mers_libs/http_requests_v1/src/main.rs index d70c2df..4d43ebb 100755 --- a/mers_libs/http_requests_v1/src/main.rs +++ b/mers_libs/http_requests_v1/src/main.rs @@ -57,41 +57,41 @@ fn main() { .unwrap() .1; my_lib.callbacks.run_function.consuming = Some(Box::new(move |msg| { - if let VDataEnum::String(url) = &msg.msg.args[0].data { - let url = url.clone(); - std::thread::spawn(move || { - let r = match reqwest::blocking::get(url) { - Ok(response) => match response.text() { - Ok(text) => VDataEnum::String(text).to(), - Err(e) => VDataEnum::EnumVariant( - err_general, - Box::new( - VDataEnum::EnumVariant( - err_getting_response_text, - Box::new(VDataEnum::String(e.to_string()).to()), - ) - .to(), - ), - ) - .to(), - }, + let url = if let VDataEnum::String(url) = &msg.msg.args[0].data().0 { + url.clone() + } else { + unreachable!() + }; + std::thread::spawn(move || { + let r = match reqwest::blocking::get(url) { + Ok(response) => match response.text() { + Ok(text) => VDataEnum::String(text).to(), Err(e) => VDataEnum::EnumVariant( err_general, Box::new( VDataEnum::EnumVariant( - err_building_request, + err_getting_response_text, Box::new(VDataEnum::String(e.to_string()).to()), ) .to(), ), ) .to(), - }; - msg.respond(r) - }); - } else { - unreachable!() - } + }, + Err(e) => VDataEnum::EnumVariant( + err_general, + Box::new( + VDataEnum::EnumVariant( + err_building_request, + Box::new(VDataEnum::String(e.to_string()).to()), + ) + .to(), + ), + ) + .to(), + }; + msg.respond(r) + }); })); // 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.