diff --git a/examples/destructuring_assignment.mers b/examples/destructuring_assignment.mers new file mode 100644 index 0000000..cc4ab50 --- /dev/null +++ b/examples/destructuring_assignment.mers @@ -0,0 +1,12 @@ +x := "https:\//www.google.com" + +x.debug() + +[a, [b, c]] := [1, ["str", 12.5]] + +println("---") +println(a.to_string() + " " + b + " " + c.to_string()) +println("~~~") + +// switch! run_command("curl", [x, ...]) { +// } diff --git a/mers/src/lang/code_runnable.rs b/mers/src/lang/code_runnable.rs index b848711..81394c5 100755 --- a/mers/src/lang/code_runnable.rs +++ b/mers/src/lang/code_runnable.rs @@ -1,4 +1,7 @@ -use std::sync::{Arc, Mutex}; +use std::{ + eprintln, + sync::{Arc, Mutex}, +}; use super::{ builtins::BuiltinFunction, @@ -106,33 +109,45 @@ pub struct RStatement { pub force_output_type: Option, } impl RStatement { + fn assign_to(assign_from: VData, mut assign_to: VData, info: &GSInfo) { + eprintln!("Assigning: '{assign_from}'."); + assign_to.operate_on_data_mut(|assign_to| match assign_to { + VDataEnum::Tuple(v) | VDataEnum::List(_, v) => { + for (i, v) in v.iter().enumerate() { + Self::assign_to( + assign_from.get(i).expect( + "tried to assign to tuple, but value didn't return Some(_) on get()", + ), + v.clone_data(), + info, + ) + } + } + VDataEnum::Reference(r) => r.assign(assign_from), + o => todo!("ERR: Cannot assign to {o}."), + }) + } pub fn run(&self, info: &GSInfo) -> VData { let out = self.statement.run(info); if let Some((v, derefs, is_init)) = &self.output_to { 'init: { - // assigns a new VData to the variable's Arc>, so that threads which have captured the variable at some point - // won't be updated with its new value (is_init is set to true for initializations, such as in a loop - this can happen multiple times, but each should be its own variable with the same name) - if *is_init && *derefs == 0 { - if let RStatementEnum::Variable(var, _, _) = v.statement.as_ref() { - let mut varl = var.lock().unwrap(); - #[cfg(debug_assertions)] - let varname = varl.1.clone(); - *varl = out; - #[cfg(debug_assertions)] - { - varl.1 = varname; - } - break 'init; + // // assigns a new VData to the variable's Arc>, so that threads which have captured the variable at some point + // // won't be updated with its new value (is_init is set to true for initializations, such as in a loop - this can happen multiple times, but each should be its own variable with the same name) + // if *is_init && *derefs == 0 { + // Self::assign_to(out, v.run(info), info); + // break 'init; + // } + let mut val = v.run(info); + if !*is_init { + for _ in 0..(*derefs + 1) { + val = match val.deref() { + Some(v) => v, + None => unreachable!("can't dereference..."), + }; } } - let mut val = v.run(info); - for _ in 0..(*derefs + 1) { - val = match val.deref() { - Some(v) => v, - None => unreachable!("can't dereference..."), - }; - } - val.assign(out); + Self::assign_to(out, val, info); + // val.assign(out); } VDataEnum::Tuple(vec![]).to() } else { diff --git a/mers/src/lang/to_runnable.rs b/mers/src/lang/to_runnable.rs index b16a28e..2dadbe4 100755 --- a/mers/src/lang/to_runnable.rs +++ b/mers/src/lang/to_runnable.rs @@ -1,3 +1,4 @@ +use core::panic; use std::{ collections::HashMap, fmt::{Debug, Display}, @@ -347,21 +348,39 @@ fn statement( ginfo: &mut GlobalScriptInfo, linfo: &mut LInfo, ) -> Result { - statement_adv(s, ginfo, linfo, None) + statement_adv(s, ginfo, linfo, &mut None) } fn statement_adv( s: &SStatement, ginfo: &mut GlobalScriptInfo, linfo: &mut LInfo, // if Some((t, is_init)), the statement creates by this function is the left side of an assignment, meaning it can create variables. t is the type that will be assigned to it. - to_be_assigned_to: Option<(VType, &mut bool)>, + to_be_assigned_to: &mut Option<(VType, &mut bool)>, ) -> Result { + // eprintln!("TR : {}", s); + // if let Some(t) = &to_be_assigned_to { + // eprintln!(" --> {}", t.0); + // } let mut state = match &*s.statement { SStatementEnum::Value(v) => RStatementEnum::Value(v.clone()), SStatementEnum::Tuple(v) | SStatementEnum::List(v) => { let mut w = Vec::with_capacity(v.len()); - for v in v { - w.push(statement(v, ginfo, linfo)?); + let mut prev = None; + for (i, v) in v.iter().enumerate() { + if let Some(t) = to_be_assigned_to { + let out_t = if let Some(p) = &prev { p } else { &t.0 }; + let inner_t = if let Some(v) = out_t.get_always(i, ginfo) { v } else { + panic!("cannot assign: cannot get_always({i}) on type {}.", out_t); + }; + let p = std::mem::replace(&mut t.0, inner_t); + if prev.is_none() { + prev = Some(p); + } + }; + w.push(statement_adv(v, ginfo, linfo, to_be_assigned_to)?); + } + if let (Some(t), Some(prev)) = (to_be_assigned_to, prev) { + t.0 = prev; } if let SStatementEnum::List(_) = &*s.statement { RStatementEnum::List(w) @@ -376,7 +395,7 @@ fn statement_adv( if is_init_force || (existing_var.is_none() && ginfo.to_runnable_automatic_initialization) { // if to_be_assigned_to is some (-> this is on the left side of an assignment), create a new variable. else, return an error. if let Some((t, is_init)) = to_be_assigned_to { - *is_init = true; + **is_init = true; #[cfg(not(debug_assertions))] let var = VData::new_placeholder(); #[cfg(debug_assertions)] @@ -385,7 +404,7 @@ fn statement_adv( linfo.vars.insert(v.to_owned(), (Arc::clone(&var_arc), t.clone())); RStatementEnum::Variable( var_arc, - t, + t.clone(), true, ) } else { @@ -686,41 +705,46 @@ fn statement_adv( opt, ginfo, linfo, - if *derefs == 0 { + &mut if *derefs == 0 { Some((state.out(ginfo), &mut is_init)) } else { None }, )?; - let mut opt_type = optr.out(ginfo); - for _ in 0..*derefs { - if let Some(deref_type) = optr.out(ginfo).dereference() { - opt_type = deref_type; + if !is_init { + let mut opt_type = optr.out(ginfo); + for _ in 0..*derefs { + if let Some(deref_type) = optr.out(ginfo).dereference() { + opt_type = deref_type; + } else { + return Err(ToRunnableError::CannotDereferenceTypeNTimes( + optr.out(ginfo), + *derefs, + opt_type, + )); + } + } + let opt_type_assign = match opt_type.dereference() { + Some(v) => v, + None => { + return Err(ToRunnableError::CannotDereferenceTypeNTimes( + optr.out(ginfo), + derefs + 1, + opt_type, + )) + } + }; + if state.out(ginfo).fits_in(&opt_type_assign, ginfo).is_empty() { + state.output_to = Some((Box::new(optr), *derefs, is_init)); } else { - return Err(ToRunnableError::CannotDereferenceTypeNTimes( - optr.out(ginfo), - *derefs, - opt_type, + return Err(ToRunnableError::CannotAssignTo( + state.out(ginfo), + opt_type_assign, )); } - } - let opt_type_assign = match opt_type.dereference() { - Some(v) => v, - None => { - return Err(ToRunnableError::CannotDereferenceTypeNTimes( - optr.out(ginfo), - derefs + 1, - opt_type, - )) - } - }; - if state.out(ginfo).fits_in(&opt_type_assign, ginfo).is_empty() { - state.output_to = Some((Box::new(optr), *derefs, is_init)); } else { - return Err(ToRunnableError::CannotAssignTo( - state.out(ginfo), - opt_type_assign, - )); + // TODO! ?? + state.output_to = Some((Box::new(optr), *derefs, is_init)); } // // if let Some((var_id, var_out)) = linfo.vars.get(opt) { diff --git a/mers/src/lang/val_type.rs b/mers/src/lang/val_type.rs index 9dbc7c5..6171cc9 100755 --- a/mers/src/lang/val_type.rs +++ b/mers/src/lang/val_type.rs @@ -34,7 +34,7 @@ pub enum VSingleType { } impl VSingleType { - // None => Cannot get, Some(t) => getting can return t or nothing + /// None => Cannot get, Some(t) => getting can return t or nothing pub fn get(&self, i: usize, gsinfo: &GlobalScriptInfo) -> Option { match self { Self::Bool | Self::Int | Self::Float | Self::Function(..) | Self::Thread(..) => None, @@ -63,7 +63,7 @@ impl VSingleType { } } } - // None => might not always return t, Some(t) => can only return t + /// None => might not always return t, Some(t) => can only return t pub fn get_always(&self, i: usize, info: &GlobalScriptInfo) -> Option { match self { Self::Bool