From 3032d3c1156310389a69469da35e9a618ab884e3 Mon Sep 17 00:00:00 2001 From: mark Date: Mon, 12 Jun 2023 19:06:20 +0200 Subject: [PATCH] fixed examples, fixed bug due to VType changes --- examples/amogus.mers | 2 +- examples/gui.mers | 51 --------- examples/http.mers | 17 --- examples/iterators.mers | 4 +- examples/mersrandr.mers | 74 ------------- examples/musicdb_remote.mers | 205 ----------------------------------- examples/struct_fields.mers | 12 +- mers/Cargo.lock | 2 +- mers/Cargo.toml | 2 +- mers/src/lang/builtins.rs | 6 +- mers/src/lang/to_runnable.rs | 3 +- mers/src/lang/val_type.rs | 160 ++++++++------------------- mers/src/parsing/parse.rs | 21 ++-- 13 files changed, 76 insertions(+), 483 deletions(-) delete mode 100644 examples/gui.mers delete mode 100644 examples/http.mers delete mode 100644 examples/mersrandr.mers delete mode 100644 examples/musicdb_remote.mers diff --git a/examples/amogus.mers b/examples/amogus.mers index 26f0cb5..34b3096 100644 --- a/examples/amogus.mers +++ b/examples/amogus.mers @@ -9,7 +9,7 @@ fn random(min int max int) { } fn rnd() { r := random(5 15) - switch r { + switch! r { int r r [] [] 10 } diff --git a/examples/gui.mers b/examples/gui.mers deleted file mode 100644 index e4da470..0000000 --- a/examples/gui.mers +++ /dev/null @@ -1,51 +0,0 @@ -lib mers_libs/gui - -base = gui_init() -column = base.gui_add(Column: []) - -text = column.gui_add(Text: "Welcome to MERS GUI!") - -button = column.gui_add(Button: "This is a button.") -second_button = column.gui_add(Button: "This is a second button.") - -text_state = -2 -second_text = column.gui_add(Text: "press the button above to remove this text!") - -loop { - for event gui_updates() { - switch! event { - ButtonPressed([int ...]) { - e = event.noenum() - match e { - &e.eq(&button) println("First button pressed") - &e.eq(&second_button) { - // don't match on text_state because we need to change the original from inside the match statement - state = text_state - match state { - // the first, third, fifth, ... time the button is pressed: remove the text - text_state.mod(2).eq(0) { - if text_state.eq(-2) { - // the first time the button is pressed - text_state = 0 - set_title("keep pressing the button!") - } - second_text.gui_remove() - } - // the 2nd, 4th, 6th, ... time the button is pressed: add the text back - text_state.eq(1) second_text = column.gui_add(Text: "i'm back!") - text_state.eq(3) second_text = column.gui_add(Text: "you can't fully get rid of me!") - true { - second_text = column.gui_add(Text: "i always come back") - // restart (set text_state to 0) - text_state = -1 - } - } - text_state = text_state.add(1) - } - true println("A different button was pressed (unreachable)") - } - } - } - } - [] -} diff --git a/examples/http.mers b/examples/http.mers deleted file mode 100644 index acc4a11..0000000 --- a/examples/http.mers +++ /dev/null @@ -1,17 +0,0 @@ -lib mers_libs/http_requests - -t = thread(() { - // because this downloads for so long, the println() will appear after the other one. - http_get("https:\//raw.githubusercontent.com/dwyl/english-words/master/words.txt").assume_no_enum() - println("got words from word list!") -}) - -sleep(0.1) - -// this will finish before the thread does. -http_get("https:\//github.com/").assume_no_enum() -println("got github start page as html") - -t.await() - -http_get("not a url").debug() diff --git a/examples/iterators.mers b/examples/iterators.mers index 7d8bea5..c279057 100644 --- a/examples/iterators.mers +++ b/examples/iterators.mers @@ -1,4 +1,4 @@ -fn map_string_to_int(list [string ...] func fn((string int))) { +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 @@ -36,4 +36,4 @@ fn square_numbers() { for n square_numbers() { println(n.to_string()) n >= 100 -} +} \ No newline at end of file diff --git a/examples/mersrandr.mers b/examples/mersrandr.mers deleted file mode 100644 index aaaf205..0000000 --- a/examples/mersrandr.mers +++ /dev/null @@ -1,74 +0,0 @@ -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) - } - } - } - } -} diff --git a/examples/musicdb_remote.mers b/examples/musicdb_remote.mers deleted file mode 100644 index ec0e6d1..0000000 --- a/examples/musicdb_remote.mers +++ /dev/null @@ -1,205 +0,0 @@ -lib mers_libs/http_requests -lib mers_libs/gui - -// MusicDB is a private project of mine. -// Basically, a SBC is hooked up to some speakers. -// It has a database of my songs and can play them. -// It can be controlled through HTTP requests, such as -// p- to stop -// p+ to play -// qi to get the queue index / playback position -// l to list all the songs -// ql or qL to list the queue -// ... -// this mers program should work as a very simple gui to remotely control -// the playback of songs. It should be able to add/remove songs to/from the queue, -// to start/stop playback, and to display some information about the current song. - -// the url used to connect to the SBC and use the HTTP api -api_url = "http:\//192.168.2.103:26315/api/raw?" - -// start the GUI -base = gui_init() -set_title("Mers MusicDB Remote") - -playback_controls = base.gui_add(Row: []) -play_button = playback_controls.gui_add(Button: "Play") -stop_button = playback_controls.gui_add(Button: "Stop") - -// these functions abstract away the api interactions -// because they aren't pretty. -// feel free to skip past this section and look at the gui code instead -fn make_api_request(to_api string) { - http_get(api_url.add(to_api)) -} -fn get_queue_index() { - r = make_api_request("qi") - switch! r { - string { - r.regex("[0-9]").assume_no_enum().get(0).assume1().parse_int().assume1() - } - // return the error - Err(ErrBuildingRequest(string)/ErrGettingResponseText(string)) r - } -} -fn get_db_contents() { - // list only 7 = 1 (title) + 2 (artist) + 4 (album) - r = make_api_request("lo7,") - switch! r { - string { - entries = r.regex("#.*:\n.*\n.*\n.*").assume_no_enum() - // initialize the list with a default value - // because mers doesnt know the inner type without it. - db = [[0 "title" "artist" "album"] ...] - &db.remove(0) - for entry entries { - lines = entry.regex(".*") - switch! lines { - [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) - artist = &lines.get(2).assume1().substring(1) - album = &lines.get(3).assume1().substring(1) - &db.push([index title artist album]) - } - Err(string) { - println("invalid response from server; 3u123128731289") - exit(1) - } - } - } - db - } - Err(ErrBuildingRequest(string)/ErrGettingResponseText(string)) { - println("couldn't request db from server:") - print(" ") - println(r.to_string()) - exit(1) - } - } -} -fn get_queue() { - r = make_api_request("ql") - switch! r { - string { - queue = [0 ...] - &queue.remove(0) - for index r.regex("[0-9]+[ \n]").assume_no_enum() { - &queue.push(index.substring(0 index.len().sub(1)).parse_int().assume1()) - } - queue - } - Err(ErrBuildingRequest(string)/ErrGettingResponseText(string)) { - println("couldn't request queue from server:") - print(" ") - println(r.to_string()) - exit(1) - } - } -} -fn play() { - r = make_api_request("p+") - switch! r { - // success! but nothing to return. - string [] - // return the error - Err(ErrBuildingRequest(string)/ErrGettingResponseText(string)) r - } -} -fn stop() { - r = make_api_request("p-") - switch! r { - // success! but nothing to return. - string [] - // return the error - Err(ErrBuildingRequest(string)/ErrGettingResponseText(string)) r - } -} -fn queue_song(song_id int) { - make_api_request("q+{0}".format(song_id.to_string())) -} - -// fetch the database contents (a list of songs) -database = get_db_contents() - -// this is where all the songs will be displayed. -songs_gui = base.gui_add(Row: []) -queue_list = songs_gui.gui_add(Column: []) -library = songs_gui.gui_add(Column: []) - -limit = 0 // set to 0 to disable library length limit -for entry database { - // by on - song = library.gui_add(Row: []) - song.gui_add(Button: entry.1) - song.gui_add(Text: "by {0} on {1}".format(entry.2 entry.3)) - limit = limit.sub(1) - if limit.eq(0) [[]] else [] -} - -// regularly update the queue -thread(() { - queue_index = get_queue_index().assume_no_enum("if the server isn't reachable, it's ok to crash") - queue_list_inner = queue_list.gui_add(Column: []) - prev_artist = "" - prev_album = "" - for song_id get_queue() { - this_is_playing = if queue_index.eq(0) { queue_index = -1 true } else if queue_index.gt(0) { queue_index = queue_index.sub(1) false } else { false } - song = &database.get(song_id).assume1() - row = queue_list_inner.gui_add(Row: []) - text = if this_is_playing ">> " else "" - text = text.add(song.1) - if prev_artist.eq(song.2) { - if prev_album.eq(song.3) { - } else { - text = text.add("(on {0})".format(song.3)) - } - } else { - text = text.add("(by {0} on {1})".format(song.2 song.3)) - } - row.gui_add(Text: text) - [] - } - sleep(10) - queue_list_inner.gui_remove() -}) - -loop { - for event gui_updates() { - switch! event { - ButtonPressed([int ...]) { - e = event.noenum() - println("Pressed button {0}".format(e.to_string())) - match e { - &e.eq(&play_button) { - println("pressed play.") - play() - } - &e.eq(&stop_button) { - println("pressed stop.") - stop() - } - { - // the button is in a row that is in the library, - // so pop()ing twice gets the library path. - // THIS WILL BREAK IF THE GUI LIBRARY SWITCHES TO IDs INSTEAD OF PATHS! - last = &e.pop().assume1() - slast = &e.pop().assume1() - matches = &e.eq(&library) - &e.push(slast) - &e.push(last) - if matches e else [] - } { - // the second to last part of the path is the row within library, which is also the index in the db - song = &database.get(&e.get(e.len().sub(2)).assume1()).assume1() - println("Added song \"{0}\" to queue.".format(song.1)) - queue_song(song.0) - } - true println("A different button was pressed (unreachable)") - } - } - } - } - [] -} diff --git a/examples/struct_fields.mers b/examples/struct_fields.mers index ec43d50..6e66a38 100644 --- a/examples/struct_fields.mers +++ b/examples/struct_fields.mers @@ -3,7 +3,17 @@ type myStruct [ int, string ] -to give names to the fields, we define functions: +// to give names to the fields, we define functions: fn count(s myStruct) s.0 // to allow users to change the value, add &myStruct to the valid types for s (only through references can values be changed) fn note(s myStruct/&myStruct) s.1 + +my_struct := [12, "test"] + +my_struct.count().debug() + +my_struct.note().debug() + +&my_struct.note() = "changed" + +my_struct.note().debug() diff --git a/mers/Cargo.lock b/mers/Cargo.lock index 538f2db..00b32ba 100755 --- a/mers/Cargo.lock +++ b/mers/Cargo.lock @@ -647,7 +647,7 @@ dependencies = [ [[package]] name = "mers" -version = "0.2.1" +version = "0.2.2" dependencies = [ "colorize", "edit", diff --git a/mers/Cargo.toml b/mers/Cargo.toml index 04d858f..8bd0926 100755 --- a/mers/Cargo.toml +++ b/mers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mers" -version = "0.2.1" +version = "0.2.2" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/mers/src/lang/builtins.rs b/mers/src/lang/builtins.rs index a6dfce0..24c7c89 100755 --- a/mers/src/lang/builtins.rs +++ b/mers/src/lang/builtins.rs @@ -636,11 +636,7 @@ impl BuiltinFunction { VSingleType::Tuple(vec![]), VSingleType::Tuple(vec![v .get_any(info) - .expect("cannot use get on this type") - .dereference(info) - .expect( - "running get_any() on &[ ...] should give a reference...", - )]), + .expect("cannot use get on this type")]), ], } } else { diff --git a/mers/src/lang/to_runnable.rs b/mers/src/lang/to_runnable.rs index 8b98635..b3b9de4 100755 --- a/mers/src/lang/to_runnable.rs +++ b/mers/src/lang/to_runnable.rs @@ -557,7 +557,7 @@ fn statement_adv( SStatementEnum::For(v, c, b) => { let mut linfo = linfo.clone(); let container = statement(&c, ginfo, &mut linfo)?; - let inner = container.out(ginfo).inner_types(ginfo); + let inner = container.out(ginfo).inner_types_for_iters(ginfo); if inner.types.is_empty() { return Err(ToRunnableError::ForLoopContainerHasNoInnerTypes); } @@ -591,7 +591,6 @@ fn statement_adv( statement(case_action, ginfo, &mut linfo)?, )); } - if *force { let types_not_covered = og_type.fits_in(&covered_types, ginfo); if !types_not_covered.is_empty() { diff --git a/mers/src/lang/val_type.rs b/mers/src/lang/val_type.rs index 1ddaec6..e7f9c4e 100755 --- a/mers/src/lang/val_type.rs +++ b/mers/src/lang/val_type.rs @@ -35,70 +35,6 @@ pub enum VSingleType { CustomTypeS(String), } -impl VSingleType { - /// None => Cannot get, Some(t) => getting can return t or nothing - pub fn get(&self, i: usize, gsinfo: &GlobalScriptInfo) -> Option { - match self { - Self::Any - | Self::Bool - | Self::Int - | Self::Float - | Self::Function(..) - | Self::Thread(..) - | Self::EnumVariant(..) - | Self::EnumVariantS(..) => None, - Self::String => Some(VSingleType::String.into()), - Self::Tuple(t) => t.get(i).cloned(), - Self::List(t) => Some(t.clone()), - Self::Reference(r) => Some(r.get(i, gsinfo)?.reference()), - Self::CustomType(t) => gsinfo.custom_types[*t].get(i, gsinfo), - &Self::CustomTypeS(_) => { - unreachable!("CustomTypeS instead of CustomType, compiler bug? [get]") - } - } - } - /// 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::Any - | Self::Bool - | Self::Int - | Self::Float - | Self::String - | Self::List(_) - | Self::Function(..) - | Self::Thread(..) - | Self::EnumVariant(..) - | Self::EnumVariantS(..) => None, - Self::Tuple(t) => t.get(i).cloned(), - Self::Reference(r) => r.get_always_ref(i, info), - Self::CustomType(t) => info.custom_types[*t].get_always(i, info), - Self::CustomTypeS(_) => { - unreachable!("CustomTypeS instead of CustomType, compiler bug? [get_always]") - } - } - } - pub fn get_always_ref(&self, i: usize, info: &GlobalScriptInfo) -> Option { - match self { - Self::Any - | Self::Bool - | Self::Int - | Self::Float - | Self::String - | Self::List(_) - | Self::Function(..) - | Self::Thread(..) - | Self::EnumVariant(..) - | Self::EnumVariantS(..) => None, - Self::Tuple(t) => Some(t.get(i)?.reference()), - Self::Reference(r) => r.get_always_ref(i, info), - Self::CustomType(t) => info.custom_types[*t].get_always_ref(i, info), - Self::CustomTypeS(_) => { - unreachable!("CustomTypeS instead of CustomType, compiler bug? [get_always]") - } - } - } -} impl VType { pub fn empty() -> Self { Self { types: vec![] } @@ -117,13 +53,6 @@ impl VType { } Some(out) } - pub fn get_always_ref(&self, i: usize, info: &GlobalScriptInfo) -> Option { - let mut out = VType { types: vec![] }; - for t in &self.types { - out.add_types(t.get_always_ref(i, info)?, info); // if we can't use *get* on one type, we can't use it at all. - } - Some(out) - } /// returns Some(true) or Some(false) if all types are references or not references. If it is mixed or types is empty, returns None. pub fn is_reference(&self) -> Option { let mut noref = false; @@ -156,6 +85,27 @@ impl VType { } impl VSingleType { + /// None => Cannot get, Some(t) => getting can return t or nothing + pub fn get(&self, i: usize, gsinfo: &GlobalScriptInfo) -> Option { + match self { + Self::Any + | Self::Bool + | Self::Int + | Self::Float + | Self::Function(..) + | Self::Thread(..) + | Self::EnumVariant(..) + | Self::EnumVariantS(..) => None, + Self::String => Some(VSingleType::String.into()), + Self::Tuple(t) => t.get(i).cloned(), + Self::List(t) => Some(t.clone()), + Self::Reference(r) => Some(r.get(i, gsinfo)?.reference()), + Self::CustomType(t) => gsinfo.custom_types[*t].get(i, gsinfo), + &Self::CustomTypeS(_) => { + unreachable!("CustomTypeS instead of CustomType, compiler bug? [get]") + } + } + } pub fn get_any(&self, info: &GlobalScriptInfo) -> Option { match self { Self::Any @@ -170,33 +120,40 @@ impl VSingleType { a })), Self::List(t) => Some(t.clone()), - Self::Reference(r) => r.get_any_ref(info), + Self::Reference(r) => Some(VType { + // this is &a/&b/..., NOT &(a/b/...)! + types: r + .get_any(info)? + .types + .iter() + .map(|v| VSingleType::Reference(v.clone().to())) + .collect(), + }), Self::EnumVariant(_, t) => t.get_any(info), Self::EnumVariantS(..) => unreachable!(), Self::CustomType(t) => info.custom_types[*t].get_any(info), Self::CustomTypeS(_) => unreachable!(), } } - pub fn get_any_ref(&self, info: &GlobalScriptInfo) -> Option { + /// 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::Any | Self::Bool | Self::Int | Self::Float + | Self::String + | Self::List(_) | Self::Function(..) - | Self::Thread(..) => None, - Self::String => Some(VSingleType::String.into()), - Self::Tuple(t) => Some(t.iter().fold(VType::empty(), |mut a, b| { - a.add_types(b.reference(), info); - a - })), - Self::List(t) => Some(t.reference()), - // TODO: idk if this is right... - Self::Reference(r) => r.get_any_ref(info), - Self::EnumVariant(_, t) => t.get_any_ref(info), - Self::EnumVariantS(..) => unreachable!(), - Self::CustomType(t) => info.custom_types[*t].get_any(info), - Self::CustomTypeS(_) => unreachable!(), + | Self::Thread(..) + | Self::EnumVariant(..) + | Self::EnumVariantS(..) => None, + Self::Tuple(t) => t.get(i).cloned(), + Self::Reference(r) => Some(VSingleType::Reference(r.get_any(info)?).to()), + Self::CustomType(t) => info.custom_types[*t].get_always(i, info), + Self::CustomTypeS(_) => { + unreachable!("CustomTypeS instead of CustomType, compiler bug? [get_always]") + } } } pub fn is_reference(&self) -> bool { @@ -221,13 +178,6 @@ impl VType { } Some(out) } - pub fn get_any_ref(&self, info: &GlobalScriptInfo) -> Option { - let mut out = VType { types: vec![] }; - for t in &self.types { - out.add_types(t.get_any_ref(info)?, info); // if we can't use *get* on one type, we can't use it at all. - } - Some(out) - } } impl VType { @@ -246,10 +196,10 @@ impl VType { } no } - pub fn inner_types(&self, info: &GlobalScriptInfo) -> VType { + pub fn inner_types_for_iters(&self, info: &GlobalScriptInfo) -> VType { let mut out = VType { types: vec![] }; for t in &self.types { - out.add_types(t.inner_types(info), info); + out.add_types(t.inner_types_for_iters(info), info); } out } @@ -307,7 +257,7 @@ impl VSingleType { pub fn to(self) -> VType { VType { types: vec![self] } } - pub fn inner_types(&self, info: &GlobalScriptInfo) -> VType { + pub fn inner_types_for_iters(&self, info: &GlobalScriptInfo) -> VType { match self { Self::Tuple(v) => { let mut out = VType::empty(); @@ -324,7 +274,7 @@ impl VSingleType { // function that takes no inputs if let Some(out) = f.iter().find_map(|(args, out)| { if args.is_empty() { - Some(out.clone()) + Some(out.clone().matches(info).1) } else { None } @@ -334,21 +284,7 @@ impl VSingleType { VType::empty() } } - Self::Reference(r) => r.inner_types(info).reference(), - _ => VType::empty(), - } - } - pub fn inner_types_ref(&self, info: &GlobalScriptInfo) -> VType { - match self { - Self::Tuple(v) => { - let mut out = VType::empty(); - for it in v { - out.add_types(it.reference(), info); - } - out - } - Self::List(v) => v.reference(), - Self::Reference(r) => r.inner_types(info).reference(), + Self::Reference(r) => r.inner_types_for_iters(info).reference(), _ => VType::empty(), } } diff --git a/mers/src/parsing/parse.rs b/mers/src/parsing/parse.rs index 7da7210..b73da0b 100755 --- a/mers/src/parsing/parse.rs +++ b/mers/src/parsing/parse.rs @@ -83,8 +83,6 @@ impl FormatGs for ScriptError { } } -pub const PARSE_VERSION: u64 = 0; - pub struct Error { pub err: ScriptError, pub ginfo: GSInfo, @@ -313,7 +311,6 @@ pub enum ParseErrors { FoundEofInType, FoundEofInsteadOfType, RefTypeWithBracketsNotClosedProperly, - InvalidType(String), CannotUseFixedIndexingWithThisType(VType), CannotWrapWithThisStatement(SStatementEnum), ErrorParsingFunctionArgs(Box), @@ -354,7 +351,6 @@ impl FormatGs for ParseErrors { Self::RefTypeWithBracketsNotClosedProperly => { write!(f, "ref type with brackets &(...) wasn't closed properly.") } - Self::InvalidType(name) => write!(f, "\"{name}\" is not a type."), Self::CannotUseFixedIndexingWithThisType(t) => { write!(f, "cannot use fixed-indexing with type ")?; t.fmtgs(f, info, form, file)?; @@ -1166,12 +1162,12 @@ pub mod implementation { } Ok((VType { types }, closed_fn_args)) } - fn parse_single_type(file: &mut File) -> Result { - match parse_single_type_adv(file, false) { - Ok((v, _)) => Ok(v), - Err(e) => Err(e), - } - } + // fn parse_single_type(file: &mut File) -> Result { + // match parse_single_type_adv(file, false) { + // Ok((v, _)) => Ok(v), + // Err(e) => Err(e), + // } + // } fn parse_single_type_adv( file: &mut File, in_fn_args: bool, @@ -1277,8 +1273,11 @@ pub mod implementation { } } file.skip_whitespaces(); - let out = parse_type(file)?; + let (out, close) = parse_type_adv(file, true)?; fn_types.push((args, out)); + if close { + break; + }; } Some(')') => break, Some(other) => {