diff --git a/gui.mers b/gui.mers index 9efdf91..8f0244e 100755 --- a/gui.mers +++ b/gui.mers @@ -14,7 +14,7 @@ second_text = column.gui_add(Text: "press the button above to remove this text!" while { for event gui_updates() { switch! event { - ButtonPressed([int]) { + ButtonPressed([int ...]) { e = event.noenum() match e { &e.eq(&button) println("First button pressed") diff --git a/mers/src/parse/parse.rs b/mers/src/parse/parse.rs index 7f44738..1c996b1 100755 --- a/mers/src/parse/parse.rs +++ b/mers/src/parse/parse.rs @@ -152,6 +152,9 @@ pub fn parse_step_libs_load( let cmd_path = cmd.get_program().to_string_lossy().into_owned(); match libs::Lib::launch(cmd, &mut ginfo.enum_variants) { Ok(lib) => { + for (i, (func, _, _)) in lib.registered_fns.iter().enumerate() { + ginfo.lib_fns.insert(func.clone(), (libs.len(), i)); + } libs.push(lib); } Err(e) => return Err(UnableToLoadLibrary(cmd_path, e)), diff --git a/mers/src/script/builtins.rs b/mers/src/script/builtins.rs index 909ac11..124be6e 100755 --- a/mers/src/script/builtins.rs +++ b/mers/src/script/builtins.rs @@ -50,6 +50,10 @@ pub enum BuiltinFunction { // OS RunCommand, RunCommandGetBytes, + // Bool + Not, + And, + Or, // Math Add, Sub, @@ -75,6 +79,7 @@ pub enum BuiltinFunction { Contains, StartsWith, EndsWith, + IndexOf, Trim, Substring, Regex, @@ -108,6 +113,9 @@ impl BuiltinFunction { "string_to_bytes" => Self::StringToBytes, "run_command" => Self::RunCommand, "run_command_get_bytes" => Self::RunCommandGetBytes, + "not" => Self::Not, + "and" => Self::And, + "or" => Self::Or, "add" => Self::Add, "sub" => Self::Sub, "mul" => Self::Mul, @@ -130,6 +138,7 @@ impl BuiltinFunction { "contains" => Self::Contains, "starts_with" => Self::StartsWith, "ends_with" => Self::EndsWith, + "index_of" => Self::IndexOf, "trim" => Self::Trim, "substring" => Self::Substring, "regex" => Self::Regex, @@ -309,6 +318,13 @@ impl BuiltinFunction { || (input[0].fits_in(&st).is_empty() && input[1].fits_in(&st).is_empty()) } } + Self::Not => input.len() == 1 && input[0].fits_in(&VSingleType::Bool.to()).is_empty(), + Self::And | Self::Or => { + input.len() == 2 + && input + .iter() + .all(|v| v.fits_in(&VSingleType::Bool.to()).is_empty()) + } Self::Sub | Self::Mul | Self::Div @@ -421,12 +437,26 @@ impl BuiltinFunction { false } } + // two strings Self::Contains | Self::StartsWith | Self::EndsWith | Self::Regex => { input.len() == 2 && input .iter() .all(|v| v.fits_in(&VSingleType::String.to()).is_empty()) } + // two strings or &strings + Self::IndexOf => { + input.len() == 2 + && input.iter().all(|v| { + v.fits_in(&VType { + types: vec![ + VSingleType::String, + VSingleType::Reference(Box::new(VSingleType::String)), + ], + }) + .is_empty() + }) + } Self::Trim => { input.len() == 1 && input[0].fits_in(&VSingleType::String.to()).is_empty() } @@ -591,6 +621,7 @@ impl BuiltinFunction { ]), ], }, + Self::Not | Self::And | Self::Or => VSingleType::Bool.to(), Self::Add | Self::Sub | Self::Mul @@ -641,6 +672,9 @@ impl BuiltinFunction { Self::Push | Self::Insert => VSingleType::Tuple(vec![]).into(), Self::Len => VSingleType::Int.into(), Self::Contains | Self::StartsWith | Self::EndsWith => VSingleType::Bool.into(), + Self::IndexOf => VType { + types: vec![VSingleType::Tuple(vec![]), VSingleType::Int], + }, Self::Trim => VSingleType::String.into(), Self::Substring => VSingleType::String.into(), Self::Regex => VType { @@ -1067,6 +1101,43 @@ impl BuiltinFunction { unreachable!("run_command not 1 arg") } } + Self::Not => { + if let VDataEnum::Bool(v) = args[0].run(vars, libs).data { + VDataEnum::Bool(!v).to() + } else { + unreachable!() + } + } + Self::And => { + if let VDataEnum::Bool(a) = args[0].run(vars, libs).data { + if a == false { + VDataEnum::Bool(false).to() + } else { + if let VDataEnum::Bool(b) = args[1].run(vars, libs).data { + VDataEnum::Bool(b).to() + } else { + unreachable!() + } + } + } else { + unreachable!() + } + } + Self::Or => { + if let VDataEnum::Bool(a) = args[0].run(vars, libs).data { + if a == true { + VDataEnum::Bool(true).to() + } else { + if let VDataEnum::Bool(b) = args[1].run(vars, libs).data { + VDataEnum::Bool(b).to() + } else { + unreachable!() + } + } + } else { + unreachable!() + } + } Self::Add => { if args.len() == 2 { match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { @@ -1458,6 +1529,61 @@ impl BuiltinFunction { unreachable!() } } + Self::IndexOf => { + if args.len() == 2 { + let find_in = args[0].run(vars, libs); + let pat = args[1].run(vars, libs); + fn find(find_in: &String, pat: &String) -> VData { + if let Some(found_byte_index) = find_in.find(pat) { + if let Some(char_index) = find_in.char_indices().enumerate().find_map( + |(char_index, (byte_index, _char))| { + if byte_index == found_byte_index { + Some(char_index) + } else { + None + } + }, + ) { + VDataEnum::Int(char_index as _).to() + } else { + VDataEnum::Tuple(vec![]).to() + } + } else { + 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!(), + } + } + (VDataEnum::Reference(a), VDataEnum::Reference(b)) => { + if Arc::ptr_eq(&a, &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) { + (VDataEnum::String(a), VDataEnum::String(b)) => find(a, b), + _ => unreachable!(), + } + } + } + _ => unreachable!(), + } + } else { + unreachable!() + } + } Self::Trim => { if args.len() == 1 { if let VDataEnum::String(a) = args[0].run(vars, libs).data { diff --git a/musicdb_remote.mers b/musicdb_remote.mers index 52e6034..7472cac 100755 --- a/musicdb_remote.mers +++ b/musicdb_remote.mers @@ -55,7 +55,7 @@ fn get_db_contents() { for entry entries { lines = entry.regex(".*") switch! lines { - [string] { + [string ...] { index_line = &lines.get(0).assume1() index = index_line.substring(1 index_line.len().sub(1)).parse_int().assume1() title = &lines.get(1).assume1().substring(1) @@ -168,7 +168,7 @@ thread(() { while { for event gui_updates() { switch! event { - ButtonPressed([int]) { + ButtonPressed([int ...]) { e = event.noenum() println("Pressed button {0}".format(e.to_string())) match e {