diff --git a/mers/Cargo.lock b/mers/Cargo.lock index e6e861a..556c2e6 100755 --- a/mers/Cargo.lock +++ b/mers/Cargo.lock @@ -647,7 +647,7 @@ dependencies = [ [[package]] name = "mers" -version = "0.1.0" +version = "0.2.0" dependencies = [ "colorize", "edit", diff --git a/mers/src/lang/builtins.rs b/mers/src/lang/builtins.rs index 8f4f944..dad7316 100755 --- a/mers/src/lang/builtins.rs +++ b/mers/src/lang/builtins.rs @@ -561,14 +561,26 @@ impl BuiltinFunction { let mut out = VType { types: vec![] }; for func in &funcs.types { if let VSingleType::Function(io) = func { - for (i, o) in io { - if i.iter() - .zip(input.iter().skip(1)) - .all(|(i, input)| input.contains(i, info)) + let mut empty = true; + let fn_out = io.iter().fold(VType::empty(), |t, (fn_in, fn_out)| { + if fn_in.len() == (input.len() - 1) + && fn_in + .iter() + .zip(input.iter().skip(1)) + .all(|(fn_in, arg)| arg.fits_in(fn_in, info).is_empty()) { - out = out | o; + empty = false; + t | fn_out.clone() + } else { + t } + }); + if empty { + unreachable!( + "fn args are incorrect for builtin run() or thread()!" + ); } + out = out | fn_out; } else { unreachable!("run called, first arg not a function") } @@ -961,7 +973,7 @@ impl BuiltinFunction { } for (i, var) in f.inputs.iter().enumerate() { let val = args[i + 1].run(info).clone_data(); - *var.lock().unwrap() = val; + var.lock().unwrap().0 = val; } f.run(info) } else { @@ -977,9 +989,14 @@ impl BuiltinFunction { for (i, var) in f.inputs.iter().enumerate() { let val = args[i + 1].run(info).clone_data(); run_input_types.push(val.out_single()); - *var.lock().unwrap() = val; + var.lock().unwrap().0 = val; } - let out_type = f.out(&run_input_types, &info); + let out_type = f + .out( + &run_input_types.iter().map(|v| v.clone().into()).collect(), + &info, + ) + .unwrap(); let info = Arc::clone(info); let f = Arc::clone(f); VDataEnum::Thread( diff --git a/mers/src/lang/code_runnable.rs b/mers/src/lang/code_runnable.rs index babfbd6..5500e0c 100755 --- a/mers/src/lang/code_runnable.rs +++ b/mers/src/lang/code_runnable.rs @@ -1,5 +1,5 @@ use std::{ - eprintln, + assert_eq, eprintln, ops::Deref, sync::{Arc, Mutex}, }; @@ -17,7 +17,7 @@ pub enum RStatementEnum { Value(VData), Tuple(Vec), List(Vec), - Variable(Arc>, VType, bool), + Variable(Arc>, bool), FunctionCall(Arc, Vec), BuiltinFunctionCall(BuiltinFunction, Vec), LibFunctionCall(usize, usize, Vec, VType), @@ -60,48 +60,51 @@ impl RBlock { #[derive(Clone, Debug)] pub struct RFunction { - pub inputs: Vec>>, + pub inputs: Vec>>, pub input_types: Vec, - pub input_output_map: Vec<(Vec, VType)>, pub statement: RStatement, + pub out_map: Vec<(Vec, VType)>, +} +impl PartialEq for RFunction { + fn eq(&self, other: &Self) -> bool { + false + } +} +impl Eq for RFunction { + fn assert_receiver_is_total_eq(&self) {} } impl RFunction { pub fn run(&self, info: &GSInfo) -> VData { self.statement.run(info) } - pub fn out(&self, input_types: &Vec, info: &GlobalScriptInfo) -> VType { - self.input_output_map + pub fn out(&self, input_types: &Vec, info: &GlobalScriptInfo) -> Option { + // NOTE: This can ONLY use self.out_map, because it's used by the VSingleType.fits_in method. + let mut empty = true; + let out = self + .out_map .iter() - .find_map(|v| { - if v.0 == *input_types { - Some(v.1.clone()) + .fold(VType::empty(), |t, (fn_in, fn_out)| { + if fn_in.len() == (input_types.len()) + && fn_in + .iter() + .zip(input_types.iter()) + .all(|(fn_in, arg)| arg.fits_in(fn_in, info).is_empty()) + { + empty = false; + t | fn_out.clone() } else { - None + t } - }) - .unwrap_or_else(|| self.statement.out(info)) - } - pub fn out_vt(&self, input_types: &Vec, info: &GlobalScriptInfo) -> VType { - let mut out = VType { types: vec![] }; - for (itype, otype) in self.input_output_map.iter() { - if itype - .iter() - .zip(input_types.iter()) - .all(|(expected, got)| got.contains(expected, info)) - { - out = out | otype; - } - } - if out.types.is_empty() { - // this can happen if we used the `any` type in our function signature, - // so in that case we just return the most broad type possible. - self.statement.out(info) + }); + if empty { + None } else { - out + Some(out) } } - pub fn out_all(&self, info: &GlobalScriptInfo) -> VType { - self.statement.out(info) + pub fn out_all(&self, _info: &GlobalScriptInfo) -> VType { + // self.statement.out(info) + self.out_map.iter().fold(VType::empty(), |t, (_, v)| t | v) } pub fn in_types(&self) -> &Vec { &self.input_types @@ -179,16 +182,16 @@ impl RStatementEnum { } VDataEnum::List(out, w).to() } - Self::Variable(v, _, is_ref) => { + Self::Variable(v, is_ref) => { if *is_ref { - VDataEnum::Reference(v.lock().unwrap().clone_mut()).to() + VDataEnum::Reference(v.lock().unwrap().0.clone_mut()).to() } else { - v.lock().unwrap().clone_data() + v.lock().unwrap().0.clone_data() } } Self::FunctionCall(func, args) => { for (i, input) in func.inputs.iter().enumerate() { - input.lock().unwrap().assign(args[i].run(info)); + input.lock().unwrap().0.assign(args[i].run(info)); } func.run(info) } @@ -333,22 +336,25 @@ impl RStatementEnum { types }) .into(), - Self::Variable(_, t, is_ref) => { + Self::Variable(t, is_ref) => { if *is_ref { VType { types: t + .lock() + .unwrap() + .1 .types .iter() .map(|t| VSingleType::Reference(Box::new(t.clone()))) .collect(), } } else { - t.clone() + t.lock().unwrap().1.clone() } } - Self::FunctionCall(f, args) => { - f.out_vt(&args.iter().map(|v| v.out(info)).collect(), info) - } + Self::FunctionCall(f, args) => f + .out(&args.iter().map(|v| v.out(info)).collect(), info) + .expect("invalid args for function -> can't determine output type"), Self::LibFunctionCall(.., out) => out.clone(), Self::Block(b) => b.out(info), Self::If(_, a, b) => { diff --git a/mers/src/lang/to_runnable.rs b/mers/src/lang/to_runnable.rs index cf4063e..d0f7d73 100755 --- a/mers/src/lang/to_runnable.rs +++ b/mers/src/lang/to_runnable.rs @@ -4,6 +4,7 @@ use std::{ eprintln, fmt::{Debug, Display}, sync::{Arc, Mutex}, + time::Duration, }; use crate::{ @@ -198,7 +199,7 @@ impl FormatGs for ToRunnableError { // Local, used to keep local variables separated #[derive(Clone)] struct LInfo { - vars: HashMap>, VType)>, + vars: HashMap>>, fns: HashMap>>, } @@ -206,17 +207,6 @@ pub fn to_runnable( s: SFunction, mut ginfo: GlobalScriptInfo, ) -> Result { - if s.inputs.len() != 1 || s.inputs[0].0 != "args" { - return Err((ToRunnableError::MainWrongInput, ginfo.to_arc())); - } - assert_eq!( - s.inputs[0].1, - VType { - types: vec![VSingleType::List(VType { - types: vec![VSingleType::String], - })], - } - ); let func = match function( &s, &mut ginfo, @@ -235,44 +225,6 @@ pub fn to_runnable( } } -// go over every possible known-type input for the given function, returning all possible RFunctions. -fn get_all_functions( - s: &SFunction, - ginfo: &mut GlobalScriptInfo, - linfo: &mut LInfo, - input_vars: &Vec>>, - inputs: &mut Vec, - out: &mut Vec<(Vec, VType)>, -) -> Result<(), ToRunnableError> { - if s.inputs.len() > inputs.len() { - let input_here = &s.inputs[inputs.len()].1; - for t in &input_here.types { - let mut t = t.clone(); - stype(&mut t, ginfo)?; - inputs.push(t); - get_all_functions(s, ginfo, linfo, input_vars, inputs, out)?; - inputs.pop(); - } - Ok(()) - } else { - // set the types - for (varid, vartype) in s.inputs.iter().zip(inputs.iter()) { - linfo.vars.get_mut(&varid.0).unwrap().1 = { - let mut vartype = vartype.clone(); - stype(&mut vartype, ginfo)?; - vartype.to() - } - } - // the statement is parsed multiple times (this is why we get duplicates in stderr): - // - n times for the function args to generate the input-output map - // - 1 more time here, where the function args aren't single types - out.push(( - inputs.clone(), - statement(&s.statement, ginfo, &mut linfo.clone())?.out(ginfo), - )); - Ok(()) - } -} fn function( s: &SFunction, ginfo: &mut GlobalScriptInfo, @@ -283,32 +235,74 @@ fn function( for (iname, itype) in &s.inputs { let mut itype = itype.to_owned(); stypes(&mut itype, ginfo)?; - let var = Arc::new(Mutex::new(VData::new_placeholder())); - linfo - .vars - .insert(iname.clone(), (Arc::clone(&var), itype.clone())); + let var = Arc::new(Mutex::new((VData::new_placeholder(), itype.clone()))); + linfo.vars.insert(iname.clone(), Arc::clone(&var)); input_vars.push(var); input_types.push(itype); } - let mut all_outs = vec![]; - get_all_functions( - s, - ginfo, - &mut linfo, - &input_vars, - &mut Vec::with_capacity(s.inputs.len()), - &mut all_outs, - )?; // set the types to all possible types (get_all_functions sets the types to one single type to get the return type of the block for that case) for (varid, vartype) in s.inputs.iter().zip(input_types.iter()) { - linfo.vars.get_mut(&varid.0).unwrap().1 = vartype.clone(); + linfo.vars.get(&varid.0).unwrap().lock().unwrap().1 = vartype.clone(); } - Ok(RFunction { + let mut o = RFunction { + out_map: vec![], inputs: input_vars, input_types, - input_output_map: all_outs, statement: statement(&s.statement, ginfo, &mut linfo.clone())?, - }) + }; + o.out_map = { + let mut map = vec![]; + let mut indices: Vec<_> = o.input_types.iter().map(|_| 0).collect(); + // like counting: advance first index, when we reach the end, reset to zero and advance the next index, ... + loop { + let mut current_types = Vec::with_capacity(o.input_types.len()); + let mut adv = true; + let mut was_last = o.input_types.is_empty(); + for i in 0..o.input_types.len() { + current_types.push(match o.input_types[i].types.get(indices[i]) { + Some(v) => v.clone().to(), + None => VType::empty(), + }); + if adv { + if indices[i] + 1 < o.input_types[i].types.len() { + indices[i] += 1; + adv = false; + } else { + indices[i] = 0; + // we just reset the last index back to 0 - if we don't break + // from the loop, we will just start all over again. + if i + 1 == o.input_types.len() { + was_last = true; + } + } + } + } + // let out = o.out(¤t_types, ginfo).expect("invalid args????"); + let out = { + let mut actual = Vec::with_capacity(o.inputs.len()); + // simulate these variable types + for (fn_input, c_type) in o.inputs.iter().zip(current_types.iter()) { + actual.push(std::mem::replace( + &mut fn_input.lock().unwrap().1, + c_type.clone(), + )); + } + // not get the return type if these were the actual types + let out = o.statement.out(ginfo); + // reset + for (fn_input, actual) in o.inputs.iter().zip(actual) { + std::mem::replace(&mut fn_input.lock().unwrap().1, actual); + } + // return + out + }; + map.push((current_types, out)); + if was_last { + break map; + } + } + }; + Ok(o) } fn block( @@ -364,7 +358,7 @@ pub fn stype(t: &mut VSingleType, ginfo: &mut GlobalScriptInfo) -> Result<(), To VSingleType::Function(io_map) => { for io_variant in io_map { for i in &mut io_variant.0 { - stype(i, ginfo)?; + stypes(i, ginfo)?; } stypes(&mut io_variant.1, ginfo)?; } @@ -449,24 +443,14 @@ fn statement_adv( let var = VData::new_placeholder(); #[cfg(debug_assertions)] let var = VData::new_placeholder_with_name(v.to_owned()); - let var_arc = Arc::new(Mutex::new(var)); - linfo - .vars - .insert(v.to_owned(), (Arc::clone(&var_arc), t.clone())); - RStatementEnum::Variable(var_arc, t.clone(), true) + let var_arc = Arc::new(Mutex::new((var, t.clone()))); + linfo.vars.insert(v.to_owned(), Arc::clone(&var_arc)); + RStatementEnum::Variable(var_arc, true) } else { return Err(ToRunnableError::UseOfUndefinedVariable(v.clone())); } } else if let Some(var) = existing_var { - RStatementEnum::Variable( - Arc::clone(&var.0), - { - let mut v = var.1.clone(); - stypes(&mut v, ginfo)?; - v - }, - *is_ref, - ) + RStatementEnum::Variable(Arc::clone(&var), *is_ref) } else { return Err(ToRunnableError::UseOfUndefinedVariable(v.clone())); } @@ -477,45 +461,23 @@ fn statement_adv( for arg in args.iter() { rargs.push(statement(arg, ginfo, linfo)?); } - fn check_fn_args( - args: &Vec, - inputs: &Vec<(Vec, VType)>, - ginfo: &GlobalScriptInfo, - ) -> Option { - let mut fit_any = false; - let mut out = VType::empty(); - for (inputs, output) in inputs { - if args.len() == inputs.len() - && args - .iter() - .zip(inputs.iter()) - .all(|(arg, input)| arg.fits_in(input, ginfo).is_empty()) - { - fit_any = true; - out = out | output; - } - } - if fit_any { - Some(out) - } else { - None - } - } let arg_types: Vec<_> = rargs.iter().map(|v| v.out(ginfo)).collect(); + fn check_fn_args( + arg_types: &Vec, + func: &RFunction, + ginfo: &GlobalScriptInfo, + ) -> bool { + func.inputs.len() == arg_types.len() + && func + .inputs + .iter() + .zip(arg_types.iter()) + .all(|(fn_in, arg)| arg.fits_in(&fn_in.lock().unwrap().1, ginfo).is_empty()) + } if let Some(funcs) = linfo.fns.get(v) { 'find_func: { for func in funcs.iter().rev() { - if let Some(_out) = check_fn_args( - &arg_types, - &func - .input_output_map - .iter() - .map(|v| { - (v.0.iter().map(|v| v.clone().to()).collect(), v.1.to_owned()) - }) - .collect(), - ginfo, - ) { + if check_fn_args(&arg_types, &func, ginfo) { break 'find_func RStatementEnum::FunctionCall( Arc::clone(&func), rargs, @@ -545,14 +507,27 @@ fn statement_adv( if let Some((libid, fnid)) = ginfo.lib_fns.get(v) { let lib = &ginfo.libs[*libid]; let libfn = &lib.registered_fns[*fnid]; - if let Some(fn_out) = check_fn_args(&arg_types, &libfn.1, ginfo) { - RStatementEnum::LibFunctionCall(*libid, *fnid, rargs, fn_out.clone()) - } else { + let mut empty = true; + let fn_out = libfn.1.iter().fold(VType::empty(), |t, (fn_in, fn_out)| { + if fn_in.len() == arg_types.len() + && fn_in + .iter() + .zip(arg_types.iter()) + .all(|(fn_in, arg)| arg.fits_in(fn_in, ginfo).is_empty()) + { + empty = false; + t | fn_out.clone() + } else { + t + } + }); + if empty { return Err(ToRunnableError::WrongArgsForLibFunction( v.to_owned(), arg_types, )); } + RStatementEnum::LibFunctionCall(*libid, *fnid, rargs, fn_out.clone()) } else { return Err(ToRunnableError::UseOfUndefinedFunction(v.clone())); } diff --git a/mers/src/lang/val_data.rs b/mers/src/lang/val_data.rs index da79207..0c5d79d 100755 --- a/mers/src/lang/val_data.rs +++ b/mers/src/lang/val_data.rs @@ -318,7 +318,7 @@ impl VDataEnum { Self::String(..) => VSingleType::String, Self::Tuple(v) => VSingleType::Tuple(v.iter().map(|v| v.out_single().to()).collect()), Self::List(t, _) => VSingleType::List(t.clone()), - Self::Function(f) => VSingleType::Function(f.input_output_map.clone()), + Self::Function(f) => VSingleType::Function(f.out_map.clone()), Self::Thread(_, o) => VSingleType::Thread(o.clone()), Self::Reference(r) => VSingleType::Reference(Box::new(r.out_single())), Self::EnumVariant(e, v) => VSingleType::EnumVariant(*e, v.out_single().to()), @@ -560,7 +560,7 @@ impl FormatGs for VDataEnum { write!(f, "...]") } Self::Function(func) => { - VSingleType::Function(func.input_output_map.clone()).fmtgs(f, info, form, file) + VSingleType::Function(func.out_map.clone()).fmtgs(f, info, form, file) } Self::Thread(..) => write!(f, "[TODO] THREAD"), Self::Reference(inner) => { diff --git a/mers/src/lang/val_type.rs b/mers/src/lang/val_type.rs index 4374279..035afbf 100755 --- a/mers/src/lang/val_type.rs +++ b/mers/src/lang/val_type.rs @@ -2,11 +2,14 @@ use std::{ collections::HashMap, fmt::{self, Debug, Display, Formatter}, ops::BitOr, + sync::Arc, }; use super::{ + code_runnable::{RFunction, RStatementEnum}, fmtgs::FormatGs, global_info::{self, GSInfo, GlobalScriptInfo}, + val_data::VDataEnum, }; use super::global_info::LogMsg; @@ -25,7 +28,7 @@ pub enum VSingleType { String, Tuple(Vec), List(VType), - Function(Vec<(Vec, VType)>), + Function(Vec<(Vec, VType)>), Thread(VType), Reference(Box), EnumVariant(usize, VType), @@ -467,18 +470,24 @@ impl VSingleType { (Self::Tuple(_), _) => false, (Self::List(a), Self::List(b)) => a.fits_in(b, info).is_empty(), (Self::List(_), _) => false, - (Self::Function(a), Self::Function(b)) => 'func_out: { - for a in a { - 'search: { - for b in b { - if a.1.fits_in(&b.1, info).is_empty() - && a.0.len() == b.0.len() - && a.0.iter().zip(b.0.iter()).all(|(a, b)| *a == *b) - { - break 'search; - } + (Self::Function(a), Self::Function(b)) => 'fnt: { + // since RFunction.out only uses out_map, we can create a dummy RFunction here. + let af = RFunction { + inputs: vec![], + input_types: vec![], + statement: RStatementEnum::Value(VDataEnum::Bool(false).to()).to(), + out_map: a.clone(), + }; + for (ins, out) in b { + // try everything that would be valid for b + if let Some(v) = af.out(ins, info) { + if !v.fits_in(out, info).is_empty() { + // found something that's valid for both, but a returns something that doesn't fit in what b would have returned -> a doesn't fit. + break 'fnt false; } - break 'func_out false; + } else { + // found something that's valid for b but not for a -> a doesn't fit. + break 'fnt false; } } true diff --git a/mers/src/parsing/parse.rs b/mers/src/parsing/parse.rs index 6878eb1..3fb8e17 100755 --- a/mers/src/parsing/parse.rs +++ b/mers/src/parsing/parse.rs @@ -192,7 +192,7 @@ pub fn parse_step_interpret( "args".to_string(), VSingleType::List(VSingleType::String.into()).to(), )], - SStatementEnum::Block(parse_block_advanced(file, Some(false), false, true, false)?).to(), + SStatementEnum::Block(parse_block_advanced(file, Some(false), true, true, false)?).to(), ); if ginfo.log.after_parse.log() { ginfo.log.log(LogMsg::AfterParse( @@ -855,6 +855,7 @@ pub mod implementation { file.skip_whitespaces(); // least local (evaluated last) // 0 = + // 015 && || // 020 == != // 025 > < >= <= // 050 + - @@ -991,35 +992,6 @@ pub mod implementation { ) .to() } - // 023 && || - (0..=23, Some('&')) - if matches!( - file.get_char(file.get_pos().current_char_index + 1), - Some('&') - ) => - { - file.next(); - file.next(); - SStatementEnum::FunctionCall( - "and".to_owned(), - vec![out, parse_statement_adv(file, false, 24)?], - ) - .to() - } - (0..=23, Some('|')) - if matches!( - file.get_char(file.get_pos().current_char_index + 1), - Some('|') - ) => - { - file.next(); - file.next(); - SStatementEnum::FunctionCall( - "or".to_owned(), - vec![out, parse_statement_adv(file, false, 24)?], - ) - .to() - } // 020 == != (0..=20, Some('=')) if matches!( @@ -1049,6 +1021,35 @@ pub mod implementation { ) .to() } + // 015 && || + (0..=15, Some('&')) + if matches!( + file.get_char(file.get_pos().current_char_index + 1), + Some('&') + ) => + { + file.next(); + file.next(); + SStatementEnum::FunctionCall( + "and".to_owned(), + vec![out, parse_statement_adv(file, false, 16)?], + ) + .to() + } + (0..=15, Some('|')) + if matches!( + file.get_char(file.get_pos().current_char_index + 1), + Some('|') + ) => + { + file.next(); + file.next(); + SStatementEnum::FunctionCall( + "or".to_owned(), + vec![out, parse_statement_adv(file, false, 16)?], + ) + .to() + } // 000 = := (0..=0, Some('=')) => { file.next(); @@ -1145,6 +1146,7 @@ pub mod implementation { file.next(); break; } + Some(']') => break, Some(ch) if ch.is_whitespace() => break, Some(ch) if ch == ',' => { file.next(); @@ -1233,6 +1235,7 @@ pub mod implementation { match file.next() { Some('(') => { break 'parse_single_type if name.as_str() == "fn" { + // syntax: fn((arg1 arg2 arg3) returns1 (arg1 arg2) returns2) let mut fn_types = vec![]; loop { file.skip_whitespaces(); @@ -1240,6 +1243,7 @@ pub mod implementation { Some('(') => { let mut args = vec![]; loop { + file.skip_whitespaces(); let (t, fn_args_closed) = parse_type_adv(file, true)?; args.push(t); @@ -1247,39 +1251,9 @@ pub mod implementation { break; } } - let out = if let Some(v) = args.pop() { - v - } else { - VSingleType::Tuple(vec![]).to() - }; - fn get_all_single_types( - types: &mut Vec, - ) -> Vec> - { - if types.is_empty() { - vec![] - } else if types.len() == 1 { - vec![types[0].types.clone()] - } else { - let last = types.pop().unwrap(); - let o = get_all_single_types(types); - let mut out = Vec::with_capacity( - o.len() * last.types.len(), - ); - for other in o { - for t in &last.types { - let mut vec = other.clone(); - vec.push(t.clone()); - out.push(vec); - } - } - types.push(last); - out - } - } - for t in get_all_single_types(&mut args) { - fn_types.push((t, out.clone())); - } + file.skip_whitespaces(); + let out = parse_type(file)?; + fn_types.push((args, out)); } Some(')') => break, Some(other) => { diff --git a/mers/tests/destructuring_assignment.mers b/mers/tests/destructuring_assignment.mers new file mode 100644 index 0000000..441487e --- /dev/null +++ b/mers/tests/destructuring_assignment.mers @@ -0,0 +1,3 @@ +[a, [b, c]] := [1, ["str", 12.5]] + +a == 1 && b == "str" && c == 12.5 diff --git a/mers/tests/force_output_type.mers b/mers/tests/force_output_type.mers new file mode 100644 index 0000000..7aeca48 --- /dev/null +++ b/mers/tests/force_output_type.mers @@ -0,0 +1,3 @@ +fn plus(a int, b int) -> int { a + b } + +10.plus(20) == 30 diff --git a/mers/tests/functions_double_definitions.mers b/mers/tests/functions_double_definitions.mers new file mode 100644 index 0000000..adb6f30 --- /dev/null +++ b/mers/tests/functions_double_definitions.mers @@ -0,0 +1,18 @@ +type person [string int] +fn name(p person/&person) p.0 +fn age(p person/&person) p.1 + +type village [[float float] string] +fn name(v village/&village) v.1 +fn location(v village/&village) v.0 +fn x_coordinate(v village/&village) v.0.0 +fn y_coordinate(v village/&village) v.0.1 + + + +customer := ["Max M.", 43] +home_town := [[12.3, 5.09], "Maxburg"] + +customer.name() == "Max M." +&& home_town.name() == "Maxburg" +&& home_town.location() == [12.3, 5.09] diff --git a/mers/tests/get_ref.mers b/mers/tests/get_ref.mers new file mode 100644 index 0000000..e665de6 --- /dev/null +++ b/mers/tests/get_ref.mers @@ -0,0 +1,10 @@ +list := [1 2 3 4 5 6 7 8 9 ...] + +// calling get on an &list will get a reference +&list.get(2).assume1() = 24 +// calling get on a list will get a value +should_not_be_changeable := list.get(3).assume1() +&should_not_be_changeable = 24 + +list.get(2) == [24] +&& list.get(3) != [24] diff --git a/mers/tests/macro.mers b/mers/tests/macro.mers new file mode 100644 index 0000000..9dc8ab5 --- /dev/null +++ b/mers/tests/macro.mers @@ -0,0 +1,7 @@ +val := !(mers { + "macro returned value" +}) + +val := !(mers "my_macro.mers") + +true diff --git a/mers/tests/modify_variable.mers b/mers/tests/modify_variable.mers new file mode 100644 index 0000000..7d0f042 --- /dev/null +++ b/mers/tests/modify_variable.mers @@ -0,0 +1,9 @@ +// NOTE: Might change, but this is the current state of things +x := 10 +t := thread(() { + sleep(0.25) + x +}) +&x = 20 // -> 20 20 because it modifies the original variable x +// x := 20 // -> 10 20 because it shadows the original variable x +t.await() == x diff --git a/mers/tests/my_macro.mers b/mers/tests/my_macro.mers new file mode 100644 index 0000000..f32a580 --- /dev/null +++ b/mers/tests/my_macro.mers @@ -0,0 +1 @@ +true \ No newline at end of file diff --git a/mers/tests/test_in_mers.rs b/mers/tests/test_in_mers.rs index 288b1dc..473dc45 100644 --- a/mers/tests/test_in_mers.rs +++ b/mers/tests/test_in_mers.rs @@ -14,10 +14,13 @@ fn run_all() { eprintln!("Checking {}", file_name); 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![]).inner_cloned(), - VDataEnum::Bool(true) - )); + assert!( + matches!( + parse::parse(&mut file).unwrap().run(vec![]).inner_cloned(), + VDataEnum::Bool(true) + ), + "{file_name} didn't return true!" + ); } } } diff --git a/mers/tests/thread_NOCHECK_ONLY_COMPILE.mers b/mers/tests/thread_NOCHECK_ONLY_COMPILE.mers new file mode 100644 index 0000000..9e48d1c --- /dev/null +++ b/mers/tests/thread_NOCHECK_ONLY_COMPILE.mers @@ -0,0 +1,38 @@ +// this is true by default so the example doesn't finish too quickly or too slowly depending on your hardware. +// you can set it to false and tweak the max value for a more authentic cpu-heavy workload. +fake_delay := true + +// this will be shared between the two threads to report the progress in percent (0-100%). +progress := 0 + +// an anonymous function that sums all numbers from 0 to max. +// it captures the progress variable and uses it to report its status to the main thread, which will periodically print the current progress. +// once done, it returns a string with the sum of all numbers. +calculator := (max int) { + sum := 0 + for i max { + i := i + 1 + // println("i: {0} s: {1}".format(i.to_string() sum.to_string())) + &sum = sum + i + // if fake_delay sleep(1) + &progress = i * 100 / max + } + "the sum of all numbers from 0 to {0} is {1}!".format(max.to_string() sum.to_string()) +} + +// start the thread. if fake_delay is true, calculate 1+2+3+4+5+6+7+8+9+10. if fake_delay is false, count up to some ridiculously large number. +slow_calculation_thread := calculator.thread(if fake_delay 10 else 20000000) + +// every second, print the progress. once it reaches 100%, stop +loop { + sleep(1) + println("{0}%".format(progress.to_string())) + progress == 100 // break from the loop +} + +// use await() to get the result from the thread. if the thread is still running, this will block until the thread finishes. +result := slow_calculation_thread.await() + +println("Thread finished, result: {0}".format(result)) + +true