diff --git a/examples/08_Type_Annotations.mers b/examples/08_Type_Annotations.mers index 7658c3d..de5b233 100644 --- a/examples/08_Type_Annotations.mers +++ b/examples/08_Type_Annotations.mers @@ -46,16 +46,20 @@ double := x -> { // Define custom types: // `[[MyType] TypeDefinition]` -[[City] (Int, Int, Int)] +[[City] { + population: Int + pos_x: Int + pos_y: Int +}] get_coords := city -> [(Int, Int)] { // only works with values of type City - (_, x, y) := [City] city + { population: _, pos_x: x, pos_y: y } := [City] city // return the coords (x, y) } -test_city := (56000, 127, -12) +test_city := { population: 56000, pos_x: 127, pos_y: -12 } ("Coords: ", test_city.get_coords).concat.println diff --git a/mers_lib/src/data/defs.rs b/mers_lib/src/data/defs.rs index ba61a6f..2af73f0 100755 --- a/mers_lib/src/data/defs.rs +++ b/mers_lib/src/data/defs.rs @@ -19,6 +19,24 @@ pub fn assign(from: &Data, target: &Data) { for (from, target) in from.0.iter().zip(target.0.iter()) { assign(from, target); } + } else if let (Some(from), Some(target)) = ( + from.get() + .as_any() + .downcast_ref::(), + target + .get() + .as_any() + .downcast_ref::(), + ) { + for (field, target) in target.0.iter() { + for (name, from) in from.0.iter() { + // TODO: do string comparison at compile-time instead! + if field == name { + assign(from, target); + break; + } + } + } } else { unreachable!("invalid assignment") } diff --git a/mers_lib/src/data/mod.rs b/mers_lib/src/data/mod.rs index e3ed59b..6914254 100755 --- a/mers_lib/src/data/mod.rs +++ b/mers_lib/src/data/mod.rs @@ -8,6 +8,7 @@ pub mod bool; pub mod float; pub mod function; pub mod int; +pub mod object; pub mod reference; pub mod string; pub mod tuple; diff --git a/mers_lib/src/data/object.rs b/mers_lib/src/data/object.rs new file mode 100644 index 0000000..f1f5027 --- /dev/null +++ b/mers_lib/src/data/object.rs @@ -0,0 +1,131 @@ +use std::{fmt::Display, sync::Arc}; + +use super::{Data, MersData, MersType, Type}; + +#[derive(Debug, PartialEq, Clone)] +pub struct Object(pub Vec<(String, Data)>); +#[derive(Debug, Clone)] +pub struct ObjectT(pub Vec<(String, Type)>); + +impl MersData for Object { + fn is_eq(&self, other: &dyn MersData) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + self == other + } else { + false + } + } + fn clone(&self) -> Box { + Box::new(Clone::clone(self)) + } + fn as_type(&self) -> Type { + Type::new(ObjectT( + self.0 + .iter() + .map(|(n, v)| (n.clone(), v.get().as_type())) + .collect(), + )) + } + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn mut_any(&mut self) -> &mut dyn std::any::Any { + self + } + fn to_any(self) -> Box { + Box::new(self) + } +} + +impl MersType for ObjectT { + fn is_same_type_as(&self, other: &dyn MersType) -> bool { + other.as_any().downcast_ref::().is_some_and(|other| { + self.0.len() == other.0.len() + && self + .0 + .iter() + .zip(other.0.iter()) + .all(|((s1, t1), (s2, t2))| s1 == s2 && t1.is_same_type_as(t2)) + }) + } + fn is_included_in_single(&self, target: &dyn MersType) -> bool { + target + .as_any() + .downcast_ref::() + .is_some_and(|target| { + self.0.len() >= target.0.len() + && self + .0 + .iter() + .zip(target.0.iter()) + .all(|((s1, t1), (s2, t2))| s1 == s2 && t1.is_included_in(t2)) + }) + } + fn subtypes(&self, acc: &mut Type) { + self.gen_subtypes_recursively(acc, &mut Vec::with_capacity(self.0.len())); + } + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn mut_any(&mut self) -> &mut dyn std::any::Any { + self + } + fn to_any(self) -> Box { + Box::new(self) + } +} + +impl Display for Object { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut comma_sep = false; + write!(f, "{{")?; + for (name, val) in self.0.iter() { + if comma_sep { + write!(f, ", ")?; + } + write!(f, "{name}: {}", val.get())?; + comma_sep = true; + } + write!(f, "}}")?; + Ok(()) + } +} +impl Display for ObjectT { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut comma_sep = false; + write!(f, "{{")?; + for (name, t) in self.0.iter() { + if comma_sep { + write!(f, ", ")?; + } + write!(f, "{name}: {t}")?; + comma_sep = true; + } + write!(f, "}}")?; + Ok(()) + } +} + +impl ObjectT { + pub fn gen_subtypes_recursively( + &self, + acc: &mut Type, + types: &mut Vec<(String, Arc)>, + ) { + if types.len() >= self.0.len() { + let nt = Self( + types + .iter() + .map(|(s, v)| (s.clone(), Type::newm(vec![Arc::clone(v)]))) + .collect(), + ); + acc.add(Arc::new(nt)); + } else { + for t in self.0[types.len()].1.subtypes_type().types { + types.push((self.0[types.len()].0.clone(), t)); + self.gen_subtypes_recursively(acc, types); + types.pop(); + } + } + } +} diff --git a/mers_lib/src/parsing/mod.rs b/mers_lib/src/parsing/mod.rs index 3754345..9e68d27 100755 --- a/mers_lib/src/parsing/mod.rs +++ b/mers_lib/src/parsing/mod.rs @@ -180,15 +180,24 @@ impl Source { self.i += ch.len_utf8(); Some(ch) } - fn word_splitter(ch: char) -> bool { + fn word_splitter_no_colon(ch: char) -> bool { ch.is_whitespace() || ".,;[](){}/<".contains(ch) } + fn word_splitter(ch: char) -> bool { + Self::word_splitter_no_colon(ch) || ch == ':' + } pub fn peek_word(&self) -> &str { self.src[self.i..] .split(Self::word_splitter) .next() .unwrap_or("") } + pub fn peek_word_allow_colon(&self) -> &str { + self.src[self.i..] + .split(Self::word_splitter_no_colon) + .next() + .unwrap_or("") + } pub fn next_word(&mut self) -> &str { self.end_sections(); let word = self.src[self.i..] @@ -198,6 +207,15 @@ impl Source { self.i += word.len(); word } + pub fn next_word_allow_colon(&mut self) -> &str { + self.end_sections(); + let word = self.src[self.i..] + .split(Self::word_splitter_no_colon) + .next() + .unwrap_or(""); + self.i += word.len(); + word + } pub fn peek_line(&self) -> &str { self.src[self.i..].lines().next().unwrap_or("") } diff --git a/mers_lib/src/parsing/statements.rs b/mers_lib/src/parsing/statements.rs index 5a86e3f..a52797d 100755 --- a/mers_lib/src/parsing/statements.rs +++ b/mers_lib/src/parsing/statements.rs @@ -35,15 +35,15 @@ pub fn parse( ); } src.skip_whitespace(); - if src.peek_word() == ":=" { - src.next_word(); + if src.peek_word_allow_colon() == ":=" { + src.next_word_allow_colon(); // [[name] := statement] let statement = match parse(src, srca) { Ok(Some(v)) => v, Ok(None) => { return Err(CheckError::new() .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) - .msg(format!("EOF after `[...]` type annotation"))) + .msg(format!("EOF after `[[...] := ...]` type definition"))) } Err(e) => return Err(e), }; @@ -113,10 +113,10 @@ pub fn parse( }; let mut pos_after_first = src.get_pos(); src.skip_whitespace(); - match src.peek_word() { + match src.peek_word_allow_colon() { ":=" => { let pos_in_src = src.get_pos(); - src.next_word(); + src.next_word_allow_colon(); let source = parse(src, srca)?.ok_or_else(|| { CheckError::new() .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) @@ -130,7 +130,7 @@ pub fn parse( } "=" => { let pos_in_src = src.get_pos(); - src.next_word(); + src.next_word_allow_colon(); let source = parse(src, srca)?.ok_or_else(|| { CheckError::new() .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) @@ -144,7 +144,7 @@ pub fn parse( } "->" => { let pos_in_src = src.get_pos(); - src.next_word(); + src.next_word_allow_colon(); let run = match parse(src, srca) { Ok(Some(v)) => v, Ok(None) => { @@ -303,8 +303,50 @@ pub fn parse_no_chain( } } Some('{') => { + // try: is this an object? let pos_in_src = src.get_pos(); src.next_char(); + let pos_in_src_after_bracket = src.get_pos(); + { + let mut elems = vec![]; + loop { + src.skip_whitespace(); + if src.peek_char() == Some('}') { + src.next_char(); + return Ok(Some(Box::new(program::parsed::object::Object { + pos_in_src: (pos_in_src, src.get_pos(), srca).into(), + elems, + }))); + } + let name = src.next_word().to_owned(); + src.skip_whitespace(); + match src.next_char() { + Some(':') => elems.push(( + name, + match parse(src, srca) { + Ok(Some(v)) => v, + Ok(None) => { + return Err(CheckError::new() + .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) + .msg(format!("EOF after `:` in object"))) + } + Err(e) => { + return Err(CheckError::new() + .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) + .msg(format!("Error in statement after `:` in object")) + .err(e)) + } + }, + )), + _ => { + // not an object (or invalid syntax) + src.set_pos(pos_in_src_after_bracket); + break; + } + } + } + } + // if not an object let statements = parse_multiple(src, srca, "}")?; return Ok(Some(Box::new(program::parsed::block::Block { pos_in_src: (pos_in_src, src.get_pos(), srca).into(), diff --git a/mers_lib/src/parsing/types.rs b/mers_lib/src/parsing/types.rs index 67264fc..ad7a4c2 100755 --- a/mers_lib/src/parsing/types.rs +++ b/mers_lib/src/parsing/types.rs @@ -12,6 +12,7 @@ use super::Source; pub enum ParsedType { Reference(Vec), Tuple(Vec>), + Object(Vec<(String, Vec)>), Type(String), TypeWithInfo(String, String), } @@ -54,6 +55,7 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc) -> Result { src.next_char(); @@ -82,6 +84,42 @@ 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![]; + src.skip_whitespace(); + if let Some('}') = src.peek_char() { + // empty object, don't even start the loop + } else { + loop { + src.skip_whitespace(); + let field = src.next_word().to_owned(); + src.skip_whitespace(); + if src.next_char() != Some(':') { + return Err(CheckError::new() + .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) + .msg(format!("Expected colon ':' in object type"))); + } + src.skip_whitespace(); + inner.push((field, parse_type(src, srca)?)); + src.skip_whitespace(); + match src.peek_char() { + Some('}') => { + src.next_char(); + break; + } + Some(',') => { + src.next_char(); + } + _ => (), + } + } + } + ParsedType::Object(inner) + } Some(_) => { let t = src.next_word().to_owned(); src.skip_whitespace(); @@ -131,6 +169,13 @@ pub fn type_from_parsed( .map(|v| type_from_parsed(v, info)) .collect::>()?, )), + ParsedType::Object(o) => Arc::new(data::object::ObjectT( + o.iter() + .map(|(s, v)| -> Result<_, CheckError> { + Ok((s.clone(), type_from_parsed(v, info)?)) + }) + .collect::>()?, + )), ParsedType::Type(name) => match info .scopes .iter() diff --git a/mers_lib/src/program/parsed/mod.rs b/mers_lib/src/program/parsed/mod.rs index 34da79a..d8a839b 100755 --- a/mers_lib/src/program/parsed/mod.rs +++ b/mers_lib/src/program/parsed/mod.rs @@ -24,6 +24,8 @@ pub mod include_mers; #[cfg(feature = "parse")] pub mod init_to; #[cfg(feature = "parse")] +pub mod object; +#[cfg(feature = "parse")] pub mod tuple; #[cfg(feature = "parse")] pub mod value; diff --git a/mers_lib/src/program/parsed/object.rs b/mers_lib/src/program/parsed/object.rs new file mode 100644 index 0000000..8e4d005 --- /dev/null +++ b/mers_lib/src/program/parsed/object.rs @@ -0,0 +1,35 @@ +use crate::{ + errors::{CheckError, SourceRange}, + info, + program::{self}, +}; + +use super::{CompInfo, MersStatement}; + +#[derive(Debug)] +pub struct Object { + pub pos_in_src: SourceRange, + pub elems: Vec<(String, Box)>, +} +impl MersStatement for Object { + fn has_scope(&self) -> bool { + false + } + fn compile_custom( + &self, + info: &mut info::Info, + comp: CompInfo, + ) -> Result, CheckError> { + Ok(Box::new(program::run::object::Object { + pos_in_src: self.pos_in_src.clone(), + elems: self + .elems + .iter() + .map(|(n, v)| -> Result<_, CheckError> { Ok((n.clone(), v.compile(info, comp)?)) }) + .collect::, _>>()?, + })) + } + fn source_range(&self) -> SourceRange { + self.pos_in_src.clone() + } +} diff --git a/mers_lib/src/program/run/mod.rs b/mers_lib/src/program/run/mod.rs index fa0405f..69b73b8 100755 --- a/mers_lib/src/program/run/mod.rs +++ b/mers_lib/src/program/run/mod.rs @@ -25,6 +25,8 @@ pub mod function; #[cfg(feature = "run")] pub mod r#if; #[cfg(feature = "run")] +pub mod object; +#[cfg(feature = "run")] pub mod tuple; #[cfg(feature = "run")] pub mod value; diff --git a/mers_lib/src/program/run/object.rs b/mers_lib/src/program/run/object.rs new file mode 100644 index 0000000..ca98a7c --- /dev/null +++ b/mers_lib/src/program/run/object.rs @@ -0,0 +1,112 @@ +use std::{collections::VecDeque, sync::Arc}; + +use colored::Colorize; + +use crate::{ + data::{self, object::ObjectT, Data, Type}, + errors::{error_colors, CheckError, SourceRange}, +}; + +use super::MersStatement; + +#[derive(Debug)] +pub struct Object { + pub pos_in_src: SourceRange, + pub elems: Vec<(String, Box)>, +} +impl MersStatement for Object { + fn check_custom( + &self, + info: &mut super::CheckInfo, + init_to: Option<&Type>, + ) -> Result { + let mut assign_types = if let Some(init_to) = init_to { + let print_is_part_of = init_to.types.len() > 1; + Some( + self.elems + .iter() + .map(|(field, _)| -> Result<_, CheckError> { + let mut acc = Type::empty(); + for t in init_to.types.iter() { + if let Some(t) = t.as_any().downcast_ref::() { + let mut found = false; + for (name, assign_to) in t.0.iter() { + if name == field { + acc.add(Arc::new(assign_to.clone())); + found = true; + break; + } + } + if !found { + return Err(format!( + "can't init an {} with type {}{} - field {field} not found", + "object".color(error_colors::InitTo), + t.to_string().color(error_colors::InitFrom), + if print_is_part_of { + format!( + ", which is part of {}", + init_to.to_string().color(error_colors::InitFrom) + ) + } else { + format!("") + } + ).into()); + } + } else { + return Err(format!( + "can't init an {} with type {}{} - only objects can be assigned to objects", + "object".color(error_colors::InitTo), + t.to_string().color(error_colors::InitFrom), + if print_is_part_of { + format!( + ", which is part of {}", + init_to.to_string().color(error_colors::InitFrom) + ) + } else { + format!("") + } + ).into()); + } + } + Ok(acc) + }) + .collect::, CheckError>>()?, + ) + } else { + None + }; + Ok(Type::new(data::object::ObjectT( + self.elems + .iter() + .map(|(n, v)| -> Result<_, CheckError> { + Ok(( + n.clone(), + v.check( + info, + if let Some(it) = &mut assign_types { + Some(it.pop_front().unwrap()) + } else { + None + } + .as_ref(), + )?, + )) + }) + .collect::>()?, + ))) + } + fn run_custom(&self, info: &mut super::Info) -> crate::data::Data { + Data::new(data::object::Object( + self.elems + .iter() + .map(|(n, s)| (n.clone(), s.run(info))) + .collect(), + )) + } + fn has_scope(&self) -> bool { + false + } + fn source_range(&self) -> SourceRange { + self.pos_in_src.clone() + } +}