diff --git a/iterators.mers b/iterators.mers new file mode 100644 index 0000000..3472508 --- /dev/null +++ b/iterators.mers @@ -0,0 +1,21 @@ +fn map_string_to_int(list [string ...] func fn((string int))) { + // return a function that can be used as an iterator + () { + // this function will be called by the for loop + // whenever it wants to retrieve the next item in the collection. + // get the element + elem = &list.remove(0) + switch! elem { + // if the element was present, run the map function to convert it from a string to an int and return the result + [string] { + [func.run(elem.0)] + } + // if there are no more elements, return something that doesn't match. + [] [] + } + } +} + +for v ["1" "2" "5" "-10" ...].map_string_to_int((s string) s.parse_int().assume1("list contained strings that can't be parsed to an int!")) { + debug(v) +} diff --git a/mers/src/parse/parse.rs b/mers/src/parse/parse.rs index 1c996b1..54f1f5d 100755 --- a/mers/src/parse/parse.rs +++ b/mers/src/parse/parse.rs @@ -910,7 +910,76 @@ fn parse_single_type_adv( match file.next() { Some('(') => { break 'parse_single_type if name.as_str() == "fn" { - todo!("fn types"); + let mut fn_types = vec![]; + loop { + file.skip_whitespaces(); + match file.next() { + Some('(') => { + let mut args = vec![]; + loop { + let (t, fn_args_closed) = + parse_type_adv(file, true)?; + args.push(t); + if fn_args_closed { + 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())); + } + } + Some(')') => break, + Some(other) => { + eprintln!("Found char '{other}' in fn type when ')' or '(' was expected (will be treated as ')'). format is fn((input11 input12 output1) (input21 input22 output2))"); + break; + } + None => { + return Err(ParseError { + err: ParseErrors::FoundEofInType, + location: err_start_of_single_type, + location_end: Some(*file.get_pos()), + context: vec![], + }) + } + } + } + if in_fn_args { + if let Some(')') = file.peek() { + _ = file.next(); + closed_bracket_in_fn_args = true; + } + } + VSingleType::Function(fn_types) } else { VSingleType::EnumVariantS(name, { let po = parse_type_adv(file, true)?; diff --git a/mers/src/script/block.rs b/mers/src/script/block.rs index 7df5026..87d6cff 100755 --- a/mers/src/script/block.rs +++ b/mers/src/script/block.rs @@ -116,13 +116,14 @@ pub mod to_runnable { InvalidType { expected: VType, found: VType, - problematic: Vec, + problematic: VType, }, CaseForceButTypeNotCovered(VType), MatchConditionInvalidReturn(VType), NotIndexableFixed(VType, usize), WrongInputsForBuiltinFunction(BuiltinFunction, String, Vec), WrongArgsForLibFunction(String, Vec), + ForLoopContainerHasNoInnerTypes, } impl Debug for ToRunnableError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -144,7 +145,7 @@ pub mod to_runnable { found, problematic, } => { - write!(f, "Invalid type: Expected {expected:?} but found {found:?}, which includes {problematic:?}, which is not covered.") + write!(f, "Invalid type: Expected {expected} but found {found}, which includes {problematic}, which is not covered.") } Self::CaseForceButTypeNotCovered(v) => write!(f, "Switch! statement, but not all types covered. Types to cover: {v}"), Self::MatchConditionInvalidReturn(v) => write!(f, "match statement condition returned {v}, which is not necessarily a tuple of size 0 to 1."), @@ -163,6 +164,9 @@ pub mod to_runnable { } write!(f, ".") } + Self::ForLoopContainerHasNoInnerTypes => { + write!(f, "For loop: container had no inner types, cannot iterate.") + } } } } @@ -385,7 +389,7 @@ pub mod to_runnable { return Err(ToRunnableError::InvalidType { expected: func.input_types[i].clone(), found: rarg, - problematic: out, + problematic: VType { types: out }, }); } } @@ -446,7 +450,7 @@ pub mod to_runnable { return Err(ToRunnableError::InvalidType { expected: VSingleType::Bool.into(), found: condition.out(), - problematic: out, + problematic: VType { types: out }, }); } }, @@ -462,9 +466,13 @@ pub mod to_runnable { SStatementEnum::For(v, c, b) => { let mut linfo = linfo.clone(); let container = statement(&c, ginfo, &mut linfo)?; + let inner = container.out().inner_types(); + if inner.types.is_empty() { + return Err(ToRunnableError::ForLoopContainerHasNoInnerTypes); + } linfo .vars - .insert(v.clone(), (ginfo.vars, container.out().inner_types())); + .insert(v.clone(), (ginfo.vars, inner)); let for_loop_var = ginfo.vars; ginfo.vars += 1; let block = statement(&b, ginfo, &mut linfo)?; @@ -832,7 +840,7 @@ impl RStatementEnum { // matching values also break with value from a for loop. let c = c.run(vars, libs); let mut vars = vars.clone(); - let mut in_loop = |c| { + let in_loop = |vars: &mut Vec>>, c| { vars[*v] = Arc::new(Mutex::new(c)); b.run(&vars, libs) }; @@ -841,7 +849,9 @@ impl RStatementEnum { match c.data { VDataEnum::Int(v) => { for i in 0..v { - if let Some(v) = in_loop(VDataEnum::Int(i).to()).data.matches() { + if let Some(v) = + in_loop(&mut vars, VDataEnum::Int(i).to()).data.matches() + { oval = v; break; } @@ -849,9 +859,10 @@ impl RStatementEnum { } VDataEnum::String(v) => { for ch in v.chars() { - if let Some(v) = in_loop(VDataEnum::String(ch.to_string()).to()) - .data - .matches() + if let Some(v) = + in_loop(&mut vars, VDataEnum::String(ch.to_string()).to()) + .data + .matches() { oval = v; break; @@ -860,12 +871,22 @@ impl RStatementEnum { } VDataEnum::Tuple(v) | VDataEnum::List(_, v) => { for v in v { - if let Some(v) = in_loop(v).data.matches() { + if let Some(v) = in_loop(&mut vars, v).data.matches() { oval = v; break; } } } + VDataEnum::Function(f) => loop { + if let Some(v) = f.run(&vars, libs).data.matches() { + if let Some(v) = in_loop(&mut vars, v).data.matches() { + oval = v; + break; + } + } else { + break; + } + }, _ => unreachable!(), } oval @@ -1063,7 +1084,17 @@ impl Display for VSingleType { Ok(()) } Self::List(t) => write!(f, "[{t} ...]"), - Self::Function(_) => write!(f, "FUNCTION"), + Self::Function(t) => { + write!(f, "fn(")?; + for (t, o) in t { + for t in t { + write!(f, "{t} ")?; + } + write!(f, "{o}")?; + } + write!(f, ")")?; + Ok(()) + } Self::Thread(_) => write!(f, "THREAD"), Self::Reference(r) => write!(f, "&{r}"), Self::EnumVariant(v, t) => write!(f, "{v}({t})"), diff --git a/mers/src/script/val_type.rs b/mers/src/script/val_type.rs index 16c209e..deecd88 100755 --- a/mers/src/script/val_type.rs +++ b/mers/src/script/val_type.rs @@ -174,6 +174,21 @@ impl VSingleType { Self::List(v) => v.types.clone(), // NOTE: to make ints work in for loops Self::Int => vec![Self::Int], + // for iterators in for loops: the match of the function's returned value make up the inner type + Self::Function(f) => { + // function that takes no inputs + if let Some(out) = f.iter().find_map(|(args, out)| { + if args.is_empty() { + Some(out.clone()) + } else { + None + } + }) { + out.types + } else { + vec![] + } + } _ => vec![], } } diff --git a/mersrandr.mers b/mersrandr.mers new file mode 100644 index 0000000..aaaf205 --- /dev/null +++ b/mersrandr.mers @@ -0,0 +1,74 @@ +lib mers_libs/gui + +// GUI for xrandr, because arandr doesn't let me change the framerates. +// [ WIP ] + +base = gui_init() + +set_title("MersRandr") + +randr_output = run_command("xrandr") + +switch! randr_output { + Err(string) { + println(randr_output.noenum()) + } + [[]/int string string] { + lines = randr_output.1.regex(".*") + screen_name = "" + screen_resolutions = [["" ["" ...]] ...] + screens = [ + [ + screen_name + screen_resolutions + ] + ...] + &screen_resolutions.pop() + &screens.pop() + for line lines { + if line.starts_with("Screen ") { + // ignore + } else if line.starts_with(" ") { + // split at spaces (and ignore +*) + refresh_rates = line.regex("[^ +\\*]+").assume_no_enum() + resolution = &refresh_rates.remove(0).assume1() + print("{0} ::".format(resolution)) + for rate refresh_rates { + print(" \"{0}\"".format(rate)) + } + &screen_resolutions.push([resolution refresh_rates]) + println("") + } else { + index = line.index_of(" ") + switch! index { + int { + if not(screen_name.len().eq(0)) { + &screens.push([screen_name screen_resolutions]) + } + screen_resolutions = [ ...] + screen_name = line.substring(0 index) + println("> \"{0}\"".format(screen_name)) + } + [] {} + } + } + } + if not(screen_name.len().eq(0)) { + &screens.push([screen_name screen_resolutions]) + } + for screen screens { + println(screen.0) + gui_screen = base.gui_add(Row: []) + gui_name = gui_screen.gui_add(Text: screen.0) + for res screen.1 { + gui_resolution = gui_screen.gui_add(Button: res.0) + print(" ") + println(res.0) + for rate res.1 { + print(" ") + println(rate) + } + } + } + } +}