From 6d6853cc9f561de9953f47ed445efcc14f819c42 Mon Sep 17 00:00:00 2001 From: Mark <> Date: Thu, 15 Feb 2024 10:55:13 +0100 Subject: [PATCH] add function type annotation (Input1 -> Output1, Input2 -> Output2) --- mers_lib/src/data/function.rs | 74 ++++++++++++++++--- mers_lib/src/errors/mod.rs | 1 + mers_lib/src/parsing/types.rs | 62 ++++++++++++++-- mers_lib/src/program/configs/with_base.rs | 6 +- mers_lib/src/program/configs/with_iters.rs | 10 +-- .../program/configs/with_multithreading.rs | 2 +- mers_lib/src/program/run/chain.rs | 2 +- 7 files changed, 129 insertions(+), 28 deletions(-) diff --git a/mers_lib/src/data/function.rs b/mers_lib/src/data/function.rs index 6ce9c84..13f4aa7 100755 --- a/mers_lib/src/data/function.rs +++ b/mers_lib/src/data/function.rs @@ -50,12 +50,12 @@ impl Function { pub fn get_as_type(&self) -> FunctionT { let out = Arc::clone(&self.out); let info = Arc::clone(&self.info_check); - FunctionT(Arc::new(move |a| { + FunctionT(Ok(Arc::new(move |a| { let lock = info.lock().unwrap(); let mut info = lock.clone(); drop(lock); out(a, &mut info) - })) + }))) } } @@ -87,11 +87,22 @@ impl MersData for Function { } #[derive(Clone)] -pub struct FunctionT(pub Arc Result + Send + Sync>); +pub struct FunctionT( + pub Result Result + Send + Sync>, Vec<(Type, Type)>>, +); +impl FunctionT { + /// get output type + pub fn o(&self, i: &Type) -> Result { + match &self.0 { + Ok(f) => f(i), + Err(v) => v.iter().find(|(a, _)| i.is_included_in(a)).map(|(_, o)| o.clone()).ok_or_else(|| format!("This function, which was defined with an explicit type, cannot be called with an argument of type {i}.").into()), + } + } +} impl MersType for FunctionT { fn iterable(&self) -> Option { // if this function can be called with an empty tuple and returns `()` or `(T)`, it can act as an iterator with type `T`. - if let Ok(t) = self.0(&Type::empty_tuple()) { + if let Ok(t) = self.o(&Type::empty_tuple()) { let mut out = Type::empty(); for t in &t.types { if let Some(t) = t.as_any().downcast_ref::() { @@ -109,11 +120,38 @@ impl MersType for FunctionT { None } } - fn is_same_type_as(&self, _other: &dyn MersType) -> bool { - false + fn is_same_type_as(&self, other: &dyn MersType) -> bool { + if let Err(s) = &self.0 { + if let Some(other) = other.as_any().downcast_ref::() { + if let Err(o) = &other.0 { + s.iter().all(|(si, so)| { + o.iter() + .any(|(oi, oo)| si.is_same_type_as(oi) && so.is_same_type_as(oo)) + }) && o.iter().all(|(oi, oo)| { + s.iter() + .any(|(si, so)| oi.is_same_type_as(si) && oo.is_same_type_as(so)) + }) + } else { + false + } + } else { + false + } + } else { + false + } } - fn is_included_in_single(&self, _target: &dyn MersType) -> bool { - false + fn is_included_in_single(&self, target: &dyn MersType) -> bool { + if let Some(target) = target.as_any().downcast_ref::() { + if let Err(s) = &target.0 { + s.iter() + .all(|(i, o)| self.o(i).is_ok_and(|r| r.is_included_in(o))) + } else { + false + } + } else { + false + } } fn subtypes(&self, acc: &mut Type) { acc.add(Arc::new(self.clone())); @@ -147,11 +185,23 @@ impl Display for Function { } impl Display for FunctionT { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match (self.0)(&Type::empty_tuple()) { - Ok(t) => write!(f, "Function /* () -> {t} */"), - Err(_) => { - write!(f, "Function",) + match &self.0 { + Err(e) => { + write!(f, "(")?; + for (index, (i, o)) in e.iter().enumerate() { + if index > 0 { + write!(f, ", ")?; + } + write!(f, "{i} -> {o}")?; + } + write!(f, ")") } + Ok(_) => match self.o(&Type::empty_tuple()) { + Ok(t) => write!(f, "(() -> {t}, ...)"), + Err(_) => { + write!(f, "(... -> ...)",) + } + }, } } } diff --git a/mers_lib/src/errors/mod.rs b/mers_lib/src/errors/mod.rs index c360319..67f5963 100644 --- a/mers_lib/src/errors/mod.rs +++ b/mers_lib/src/errors/mod.rs @@ -78,6 +78,7 @@ pub mod error_colors { pub const AsTypeTypeAnnotation: Color = InitTo; pub const BadCharInTupleType: Color = Color::Red; + pub const BadCharInFunctionType: Color = Color::Red; pub const BadTypeFromParsed: Color = Color::Blue; pub const TypeAnnotationNoClosingBracket: Color = Color::Blue; } diff --git a/mers_lib/src/parsing/types.rs b/mers_lib/src/parsing/types.rs index a6b3c70..97880ae 100755 --- a/mers_lib/src/parsing/types.rs +++ b/mers_lib/src/parsing/types.rs @@ -13,8 +13,8 @@ pub enum ParsedType { Reference(Vec), Tuple(Vec>), Object(Vec<(String, Vec)>), + Function(Vec<(Vec, Vec)>), Type(String), - Function(Vec<(Self, Self)>), TypeWithInfo(String, String), } @@ -44,19 +44,20 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc) -> Result { let pos_in_src = src.get_pos(); src.next_char(); src.section_begin("parse tuple's inner types".to_string()); - let mut inner = vec![]; + let mut inner_t = vec![]; + let mut inner_f = vec![]; src.skip_whitespace(); if let Some(')') = src.peek_char() { src.next_char(); // empty tuple, don't even start the loop } else { loop { - inner.push(parse_type(src, srca)?); + let t = parse_type(src, srca)?; src.skip_whitespace(); match src.peek_char() { Some(')') => { @@ -64,7 +65,48 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc) -> Result { - src.next_char(); + if inner_f.is_empty() { + inner_t.push(t); + src.next_char(); + } else { + let pos1 = src.get_pos(); + src.next_char(); + return Err(CheckError::new().src(vec![ + ((pos_in_src, src.get_pos(), srca).into(), None), + ( + (pos1, src.get_pos(), srca).into(), + Some(error_colors::BadCharInFunctionType), + ), + ]).msg(format!("Unexpected character in function type, expected arrow `->` but found `,`.")) + .msg(format!("If you wanted this to be a tuple type instead, you may have used `Input -> Output` instead of `(Input -> Output)` for a function type somewhere."))); + } + } + Some('-') if src.peek_word() == "->" => { + if inner_t.is_empty() { + src.next_word(); + inner_f.push((t, parse_type(src, srca)?)); + let pos2 = src.get_pos(); + src.skip_whitespace(); + match src.next_char() { + Some(',') => (), + Some(')') => break, + _ => return Err(CheckError::new().src(vec![ + ((pos_in_src, src.get_pos(), srca).into(), None), + ((pos2, src.get_pos(), srca).into(), Some(error_colors::BadCharInFunctionType)), + ]).msg(format!("Expected comma `,` after `In -> Out` part of function type"))) + } + } else { + let pos1 = src.get_pos(); + src.next_word(); + return Err(CheckError::new().src(vec![ + ((pos_in_src, src.get_pos(), srca).into(), None), + ( + (pos1, src.get_pos(), srca).into(), + Some(error_colors::BadCharInTupleType), + ), + ]).msg(format!("Unexpected character in tuple type, expected comma `,` but found arrow `->`.")) + .msg(format!("If you wanted to write a function type, use `(Input -> Output)` instead of `Input -> Output`."))); + } } _ => { let ppos = src.get_pos(); @@ -84,7 +126,11 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc) -> Result { @@ -179,6 +225,10 @@ pub fn type_from_parsed( }) .collect::>()?, )), + ParsedType::Function(v) => Arc::new(data::function::FunctionT(Err(v + .iter() + .map(|(i, o)| Ok((type_from_parsed(i, info)?, type_from_parsed(o, info)?))) + .collect::>()?))), ParsedType::Type(name) => match info .scopes .iter() diff --git a/mers_lib/src/program/configs/with_base.rs b/mers_lib/src/program/configs/with_base.rs index 417a193..20ad7b3 100755 --- a/mers_lib/src/program/configs/with_base.rs +++ b/mers_lib/src/program/configs/with_base.rs @@ -36,7 +36,7 @@ impl Config { let func = &t.0[1]; for func_t in func.types.iter() { if let Some(f) = func_t.as_any().downcast_ref::() { - match (f.0)(&arg) { + match f.o(&arg) { Ok(out) => { if !out.is_included_in(&arg) { return Err(format!("Function returns a value of type {out}, which isn't included in the type of the reference, {arg}.").into()); @@ -150,7 +150,7 @@ impl Config { } else { [v].into_iter().collect() } } else { [v].into_iter().collect() }) { if let Some(t) = t.as_any().downcast_ref::() { - for t in (t.0)(&Type::empty_tuple())?.types { + for t in t.o(&Type::empty_tuple())?.types { if let Some(t) = t.as_any().downcast_ref::() { if t.0.len() > 1 { return Err(format!("called loop with funcion that might return a tuple of length > 1").into()); @@ -293,7 +293,7 @@ fn get_try(allow_unused_functions: bool) -> Data { if !skip_checks { func_errors.push(( fvi, - match ft.0(&arg_type) { + match ft.o(&arg_type) { Err(e) => { func_fallible = true; if let Some(errs) = diff --git a/mers_lib/src/program/configs/with_iters.rs b/mers_lib/src/program/configs/with_iters.rs index 3f8a931..7f84e54 100755 --- a/mers_lib/src/program/configs/with_iters.rs +++ b/mers_lib/src/program/configs/with_iters.rs @@ -53,7 +53,7 @@ impl Config { .collect::>>(), ) { for f in f { - let _ret = f.0(&iter)?; + let _ret = f.o(&iter)?; // if !ret.is_zero_tuple() { // return Err(format!("for_each function must return (), not {ret}").into()); // } @@ -325,9 +325,9 @@ impl MersData for Iter { impl IterT { pub fn new(iter: ItersT, data: Type) -> Result { let t = match &iter { - ItersT::Map(f) => (f.0)(&data)?, + ItersT::Map(f) => f.o(&data)?, ItersT::Filter(f) => { - if (f.0)(&data)?.is_included_in(&data::bool::BoolT) { + if f.o(&data)?.is_included_in(&data::bool::BoolT) { data.clone() } else { return Err(format!( @@ -337,7 +337,7 @@ impl IterT { } } ItersT::FilterMap(f) => { - if let Some(v) = (f.0)(&data)?.one_tuple_possible_content() { + if let Some(v) = f.o(&data)?.one_tuple_possible_content() { v } else { return Err( @@ -346,7 +346,7 @@ impl IterT { } } ItersT::MapWhile(f) => { - if let Some(t) = (f.0)(&data)?.one_tuple_possible_content() { + if let Some(t) = f.o(&data)?.one_tuple_possible_content() { t } else { return Err( diff --git a/mers_lib/src/program/configs/with_multithreading.rs b/mers_lib/src/program/configs/with_multithreading.rs index 29403c1..a22d1f7 100755 --- a/mers_lib/src/program/configs/with_multithreading.rs +++ b/mers_lib/src/program/configs/with_multithreading.rs @@ -36,7 +36,7 @@ impl Config { let mut out = Type::empty(); for t in a.types.iter() { if let Some(f) = t.as_any().downcast_ref::() { - match (f.0)(&Type::empty_tuple()) { + match f.o(&Type::empty_tuple()) { Ok(t) => out.add(Arc::new(t)), Err(e) => return Err(CheckError::new().msg(format!("Can't call thread on a function which can't be called on an empty tuple: ")).err(e)) } diff --git a/mers_lib/src/program/run/chain.rs b/mers_lib/src/program/run/chain.rs index ac9a619..3f095ca 100755 --- a/mers_lib/src/program/run/chain.rs +++ b/mers_lib/src/program/run/chain.rs @@ -39,7 +39,7 @@ impl MersStatement for Chain { .as_any() .downcast_ref::() { - match (func.0)(&arg) { + match func.o(&arg) { Ok(t) => o.add(Arc::new(t)), Err(e) => { return Err(if let Some(_) = &self.as_part_of_include {