diff --git a/README.md b/README.md index e3aa52e..410e85b 100755 --- a/README.md +++ b/README.md @@ -1,12 +1,9 @@ # mers -Mers is getting a rewrite! +Mers is a high-level programming language. +It is designed to be safe (it doesn't crash at runtime) and as simple as possible. -This means that this README isn't complete, -many things will change, -and the docs/ are for a completely different language. - -## why mers? +## what makes it special ### Simplicity @@ -15,18 +12,21 @@ But this means that many things you may be familiar with simply don't exist in m because they aren't actually needed. This includes: -**Exceptions**, because errors in mers are values just like any other. +**Exceptions**, because errors in mers are just values. A function to read a UTF-8 text file from disk could have a return type like `String/IOError/UTF8DecodeError`, which tells you exactly what errors can happen and also forces you to handle them (see later for mers' type-system). -**Loops**, because, as it turns out, a function which takes an iterable and a function can do this just fine. +**Loops**, because, as it turns out, a function which takes an iterable and a function can do this just fine: +``` +my_list := (1, 2, 3, "four", "five", 6.5).as_list +(my_list, val -> val.println).for_each +``` Javascript has `forEach`, Rust `for_each`, C# `Each`, and Java has `forEach`. At first, it seemed stupid to have a function that does the same thing a `for` or `foreach` loop can already do, but if a function can do the job, why do we even need a special language construct to do this for us? -To keep it simple, mers doesn't have any loops except for `loop`. -`loop` simply repeats until the inner expression returns `(T)`, which causes loop to return `T`. +To keep it simple, mers doesn't have any loops - just functions you can use to loop. -**Breaks** aren't necessary, since this can be achieved using iterator magic or by returning `(T)` in a `loop`. +**Breaks** aren't necessary, since this can be achieved using iterators or by returning `(T)` in a `loop`. It is also similar to `goto`, because it makes control flow less obvious, so it had to go. The same control flow obfuscation issue exists for **returns**, so these also aren't a thing. @@ -37,18 +37,41 @@ Why? Because we don't need anything else. A function with no arguments now takes an empty tuple `()`, a function with two arguments now takes a two-length tuple `(a, b)`, a function with either zero, one, or three arguments now takes a `()/(a)/(a, b, c)`, -and a function with n args takes a list, or a list as part of a tuple, or an optional list via `()/`. +and a function with n arguments takes a list, or a list as part of a tuple, or an optional list via `()/`. -### Types +### Types and Safety Mers is built around a type-system where a value could be one of multiple types. This is basically what dynamic typing allows you to do: - - x := if condition { 12 } else { "something went wrong" } - +``` +x := if condition { 12 } else { "something went wrong" } +``` This would be valid code. However, in mers, the compiler still tracks all the types in your program, and it will catch every possible crash before the program even runs: -If we tried to use `x` as an Int, the compiler would complain since it might be a string. +If we tried to use `x` as an int, the compiler would complain since it might be a string, so this does not compile: +``` +list := (1, 2, if true 3 else "not an int") +list.sum.println +``` -(note: type-checks aren't implemented since the rewrite is just barely functional, but they will be there and fully working soon) +Type-safety for functions is different from what you might expect. +You don't need to tell mers what type your function's argument has - you just use it however you want as if mers was a dynamically typed language: +``` +sum_doubled := iter -> { + one := iter.sum + (one, one).sum +} +(1, 2, 3).sum_doubled.println +``` +We could try to use the function improperly by passing a string instead of an int: +``` +(1, 2, "3").sum_doubled.println +``` +But mers will catch this and show an error, because the call to `sum` inside of `sum_doubled` would fail. + +(note: type-checks aren't implemented for all functions yet, you may need to use `--check no` to get around this and deal with runtime panics for now) + +## docs + +docs will be available some time. for now, check mers_lib/src/program/configs/* diff --git a/mers/src/cfg_globals.rs b/mers/src/cfg_globals.rs old mode 100644 new mode 100755 diff --git a/mers/src/main.rs b/mers/src/main.rs index f8ffe6e..a62d3ea 100755 --- a/mers/src/main.rs +++ b/mers/src/main.rs @@ -6,11 +6,14 @@ mod cfg_globals; #[derive(Parser)] struct Args { + #[command(subcommand)] + command: Command, /// controls availability of features when compiling/running #[arg(long, value_enum, default_value_t = Configs::Std)] config: Configs, - #[command(subcommand)] - command: Command, + /// perform checks to avoid runtime crashes + #[arg(long, default_value_t = Check::Yes)] + check: Check, } #[derive(Subcommand)] enum Command { @@ -20,6 +23,25 @@ enum Command { Exec { source: String }, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +enum Check { + No, + Yes, + Only, +} +impl Display for Check { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::No => "no", + Self::Yes => "yes", + Self::Only => "only", + } + ) + } +} +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] enum Configs { None, Base, @@ -42,20 +64,39 @@ fn main() { Configs::Base => Config::new().bundle_base(), Configs::Std => Config::new().bundle_std(), }); - let (mut info1, mut info2) = config.infos(); - match args.command { + let (mut info_parsed, mut info_run, mut info_check) = config.infos(); + let mut source = match args.command { Command::Run { file } => { let str = fs::read_to_string(file).unwrap(); - let mut src = Source::new(str); - let parsed = parse(&mut src).unwrap(); - let run = parsed.compile(&mut info1, Default::default()).unwrap(); - run.run(&mut info2); + Source::new(str) } - Command::Exec { source } => { - let mut src = Source::new(source); - let parsed = parse(&mut src).unwrap(); - let run = parsed.compile(&mut info1, Default::default()).unwrap(); - run.run(&mut info2); + Command::Exec { source } => Source::new(source), + }; + let parsed = parse(&mut source).unwrap(); + #[cfg(debug_assertions)] + dbg!(&parsed); + let run = parsed + .compile(&mut info_parsed, Default::default()) + .unwrap(); + #[cfg(debug_assertions)] + dbg!(&run); + match args.check { + Check::No => { + run.run(&mut info_run); + } + Check::Yes | Check::Only => { + let rt = match run.check(&mut info_check, None) { + Ok(v) => v, + Err(e) => { + eprintln!("check failed: {e}"); + std::process::exit(36); + } + }; + if args.check == Check::Yes { + run.run(&mut info_run); + } else { + eprintln!("return type is {}", rt) + } } } } diff --git a/mers/test.mers b/mers/test.mers index 2d6e08c..8aa1d62 100755 --- a/mers/test.mers +++ b/mers/test.mers @@ -1,27 +1 @@ -list := ( - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, -); - -total := 0 -(list item -> &total = (total, item).sum).iter -"total: ".print -total.println -"sum: ".print -list.sum.println -iter := (list item -> { item.println 12 }).map -"---".println -list := iter.as_list -list.println -list.sum.println -list.enumerate.as_list.println - -"mers cli: ".print -mers_cli.println +("a").{ (a) -> a } diff --git a/mers/test2.mers b/mers/test2.mers new file mode 100755 index 0000000..e554a66 --- /dev/null +++ b/mers/test2.mers @@ -0,0 +1,28 @@ +list := ( + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, +); + +total := 0 +t := &total +(list, item -> t = (t.deref, item).sum).iter +"total: ".print +total.println +"sum: ".print +list.sum.println +iter := (list item -> { item.println 12 }).map +"---".println +list := iter.as_list +list.println +list.sum.println +list.enumerate.as_list.println + +"mers cli: ".print +mers_cli.println diff --git a/mers/test3.mers b/mers/test3.mers new file mode 100755 index 0000000..d153572 --- /dev/null +++ b/mers/test3.mers @@ -0,0 +1,8 @@ +values := ().as_list +counter := 0 +counter_ref := &counter +{val -> { + counter_ref = (counter_ref.deref, 1).sum + counter_ref.deref.println + if (counter_ref.deref, 5).eq { (()) } else { () } +}}.loop diff --git a/mers_lib/Cargo.toml b/mers_lib/Cargo.toml index abe6218..94e18f4 100755 --- a/mers_lib/Cargo.toml +++ b/mers_lib/Cargo.toml @@ -5,6 +5,6 @@ edition = "2021" [features] -default = [] +default = ["parse"] parse = ["run"] run = [] diff --git a/mers_lib/doc.md b/mers_lib/doc.md index 64de77b..ec4381c 100755 --- a/mers_lib/doc.md +++ b/mers_lib/doc.md @@ -1,5 +1,10 @@ # mers documentation +## ISSUES + +when storing a reference, then reinitializing a variable of the same name, the reference may get the new value although +it would be expected for it to be a reference to the value before it was reinitialized. + ## parsing syntax: diff --git a/mers_lib/src/data/bool.rs b/mers_lib/src/data/bool.rs index 827b217..85a1a1c 100755 --- a/mers_lib/src/data/bool.rs +++ b/mers_lib/src/data/bool.rs @@ -1,14 +1,24 @@ use std::{any::Any, fmt::Display}; -use super::{MersData, MersType}; +use super::{MersData, MersType, Type}; #[derive(Debug, Clone)] pub struct Bool(pub bool); impl MersData for Bool { + fn is_eq(&self, other: &dyn MersData) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + other.0 == self.0 + } else { + false + } + } fn clone(&self) -> Box { Box::new(Clone::clone(self)) } + fn as_type(&self) -> super::Type { + Type::new(BoolT) + } fn as_any(&self) -> &dyn Any { self } @@ -45,3 +55,8 @@ impl Display for Bool { write!(f, "{}", self.0) } } +impl Display for BoolT { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Bool") + } +} diff --git a/mers_lib/src/data/defs.rs b/mers_lib/src/data/defs.rs index 48b1571..895a3b9 100755 --- a/mers_lib/src/data/defs.rs +++ b/mers_lib/src/data/defs.rs @@ -1,12 +1,14 @@ -use super::Data; +use crate::program::run::CheckError; + +use super::{Data, MersType, Type}; pub fn assign(from: Data, target: &Data) { - let mut target = target.get_mut(); + let target = target.get(); if let Some(r) = target - .mut_any() - .downcast_mut::() + .as_any() + .downcast_ref::() { - *r.0.get_mut() = from.get().clone(); + *r.0.lock().unwrap().get_mut() = from.get().clone(); } else { todo!("assignment to non-reference") } diff --git a/mers_lib/src/data/float.rs b/mers_lib/src/data/float.rs index 81a0e92..4a4eb9c 100755 --- a/mers_lib/src/data/float.rs +++ b/mers_lib/src/data/float.rs @@ -6,9 +6,19 @@ use super::{MersData, MersType, Type}; pub struct Float(pub f64); impl MersData for Float { + fn is_eq(&self, other: &dyn MersData) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + other.0 == self.0 + } else { + false + } + } fn clone(&self) -> Box { Box::new(Clone::clone(self)) } + fn as_type(&self) -> super::Type { + Type::new(FloatT) + } fn as_any(&self) -> &dyn Any { self } @@ -45,3 +55,8 @@ impl Display for Float { write!(f, "{}", self.0) } } +impl Display for FloatT { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Float") + } +} diff --git a/mers_lib/src/data/function.rs b/mers_lib/src/data/function.rs index 788c385..17fa54f 100755 --- a/mers_lib/src/data/function.rs +++ b/mers_lib/src/data/function.rs @@ -1,36 +1,57 @@ use std::{ any::Any, fmt::{Debug, Display}, - sync::Arc, + sync::{Arc, Mutex}, }; -use crate::program::{self, run::Info}; +use crate::program::{ + self, + run::{CheckError, CheckInfo, Info}, +}; use super::{Data, MersData, MersType, Type}; #[derive(Clone)] pub struct Function { - pub info: Info, - pub out: Arc Option>, - pub run: Arc Data>, + pub info: Arc, + pub info_check: Arc>, + pub out: Arc Result + Send + Sync>, + pub run: Arc Data + Send + Sync>, } impl Function { - pub fn with_info(&self, info: program::run::Info) -> Self { + pub fn with_info_run(&self, info: Arc) -> Self { Self { info, + info_check: Arc::clone(&self.info_check), out: Arc::clone(&self.out), run: Arc::clone(&self.run), } } + pub fn with_info_check(&self, check: CheckInfo) { + *self.info_check.lock().unwrap() = check; + } + pub fn check(&self, arg: &Type) -> Result { + (self.out)(arg, &mut self.info_check.lock().unwrap().clone()) + } pub fn run(&self, arg: Data) -> Data { - (self.run)(arg, &mut self.info.clone()) + (self.run)(arg, &mut self.info.as_ref().clone()) } } impl MersData for Function { + fn is_eq(&self, _other: &dyn MersData) -> bool { + false + } fn clone(&self) -> Box { Box::new(Clone::clone(self)) } + fn as_type(&self) -> Type { + let out = Arc::clone(&self.out); + let info = Arc::clone(&self.info_check); + Type::new(FunctionT(Arc::new(move |a| { + out(a, &mut info.lock().unwrap().clone()) + }))) + } fn as_any(&self) -> &dyn Any { self } @@ -42,7 +63,7 @@ impl MersData for Function { } } -pub struct FunctionT(Arc Option>); +pub struct FunctionT(pub Arc Result + Send + Sync>); impl MersType for FunctionT { fn is_same_type_as(&self, _other: &dyn MersType) -> bool { false @@ -77,3 +98,8 @@ impl Display for Function { write!(f, "") } } +impl Display for FunctionT { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Function") + } +} diff --git a/mers_lib/src/data/int.rs b/mers_lib/src/data/int.rs index 9f1200f..ab748d0 100755 --- a/mers_lib/src/data/int.rs +++ b/mers_lib/src/data/int.rs @@ -6,9 +6,19 @@ use super::{MersData, MersType, Type}; pub struct Int(pub isize); impl MersData for Int { + fn is_eq(&self, other: &dyn MersData) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + other.0 == self.0 + } else { + false + } + } fn clone(&self) -> Box { Box::new(Clone::clone(self)) } + fn as_type(&self) -> super::Type { + Type::new(IntT) + } fn as_any(&self) -> &dyn Any { self } @@ -45,3 +55,8 @@ impl Display for Int { write!(f, "{}", self.0) } } +impl Display for IntT { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Int") + } +} diff --git a/mers_lib/src/data/mod.rs b/mers_lib/src/data/mod.rs index 9398989..6b80568 100755 --- a/mers_lib/src/data/mod.rs +++ b/mers_lib/src/data/mod.rs @@ -1,7 +1,7 @@ use std::{ any::Any, fmt::{Debug, Display}, - sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, + sync::{atomic::AtomicUsize, Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; pub mod bool; @@ -14,10 +14,7 @@ pub mod tuple; pub mod defs; -pub trait MersData: Any + Debug + Display { - fn matches(&self) -> Option { - None - } +pub trait MersData: Any + Debug + Display + Send + Sync { fn iterable(&self) -> Option>> { None } @@ -26,18 +23,17 @@ pub trait MersData: Any + Debug + Display { fn get(&self, i: usize) -> Option { self.iterable()?.nth(i) } + /// If self and other are different types (`other.as_any().downcast_ref::().is_none()`), + /// this *must* return false. + fn is_eq(&self, other: &dyn MersData) -> bool; fn clone(&self) -> Box; + fn as_type(&self) -> Type; fn as_any(&self) -> &dyn Any; fn mut_any(&mut self) -> &mut dyn Any; fn to_any(self) -> Box; } -pub trait MersType: Any + Debug { - /// If Some((_, false)) is returned, data of this type could match. If it matches, it matches with the type. - /// If Some((_, true)) is returned, data of this type will always match with the type. - fn matches(&self) -> Option<(Type, bool)> { - None - } +pub trait MersType: Any + Debug + Display + Send + Sync { /// If Some(T), calling `iterable` on the MersData this MersType belongs to /// Should return Some(I), where I is an Iterator which only returns items of type T. fn iterable(&self) -> Option { @@ -66,18 +62,28 @@ pub trait MersType: Any + Debug { fn as_any(&self) -> &dyn Any; fn mut_any(&mut self) -> &mut dyn Any; fn to_any(self) -> Box; + fn is_reference_to(&self) -> Option<&Type> { + None + } } #[derive(Debug)] pub struct Data { + is_mut: bool, + counts: Arc<(AtomicUsize, AtomicUsize)>, pub data: Arc>>, } impl Data { pub fn new(data: T) -> Self { - Self::new_boxed(Box::new(data)) + Self::new_boxed(Box::new(data), true) } - pub fn new_boxed(data: Box) -> Self { + pub fn new_boxed(data: Box, is_mut: bool) -> Self { Self { + is_mut, + counts: Arc::new(( + AtomicUsize::new(if is_mut { 0 } else { 1 }), + AtomicUsize::new(if is_mut { 1 } else { 0 }), + )), data: Arc::new(RwLock::new(data)), } } @@ -87,27 +93,116 @@ impl Data { pub fn one_tuple(v: Self) -> Self { Self::new(tuple::Tuple(vec![v])) } + /// Returns true if self is `()`. + pub fn is_zero_tuple(&self) -> bool { + if let Some(tuple) = self + .get() + .as_any() + .downcast_ref::() + { + tuple.0.is_empty() + } else { + false + } + } + /// Returns `Some(d)` if and only if self is `(d)`. + pub fn one_tuple_content(&self) -> Option { + if let Some(data) = self + .get() + .as_any() + .downcast_ref::() + .filter(|v| v.len() == 1) + .and_then(|v| v.get(0)) + { + Some(data.clone()) + } else { + None + } + } pub fn get(&self) -> RwLockReadGuard> { + #[cfg(debug_assertions)] + eprintln!("[mers:data:cow] get"); self.data.read().unwrap() } - pub fn get_mut(&self) -> RwLockWriteGuard> { + pub fn get_mut_unchecked(&self) -> RwLockWriteGuard> { self.data.write().unwrap() } + pub fn try_get_mut(&self) -> Option>> { + if self.is_mut && self.counts.0.load(std::sync::atomic::Ordering::Relaxed) == 0 { + Some(self.get_mut_unchecked()) + } else { + None + } + } + /// like try_get_mut, but instead of returning `None` this function `get()`s the data and clones it. + /// When cloning data, this transforms `self` into a `Data` with `is_mut: true`, hence the `&mut self` parameter. + pub fn get_mut(&mut self) -> RwLockWriteGuard> { + if self.try_get_mut().is_none() { + #[cfg(debug_assertions)] + eprintln!( + "[mers:data:cow] cloning! get_mut called on {}", + if !self.is_mut { + "non-mut value" + } else { + "value with immut references" + } + ); + let val = self.get().clone(); + *self = Self::new_boxed(val, true); + } + self.get_mut_unchecked() + } + pub fn mkref(&self) -> Self { + if self.is_mut { + self.counts + .1 + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + Self { + is_mut: true, + counts: Arc::clone(&self.counts), + data: Arc::clone(&self.data), + } + } else { + #[cfg(debug_assertions)] + eprintln!("[mers:data:cow] cloning! mkref called on immutable data"); + Self::new_boxed(self.data.read().unwrap().clone(), true) + } + } } impl Clone for Data { fn clone(&self) -> Self { - // todo!("clone for data - requires CoW"); + self.counts + .0 + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); Self { + is_mut: false, + counts: Arc::clone(&self.counts), data: Arc::clone(&self.data), } } } +impl Drop for Data { + fn drop(&mut self) { + if self.is_mut { + &self.counts.1 + } else { + &self.counts.0 + } + .fetch_sub(1, std::sync::atomic::Ordering::Relaxed); + } +} + +impl PartialEq for Data { + fn eq(&self, other: &Self) -> bool { + self.get().is_eq(other.get().as_ref()) + } +} #[derive(Clone, Debug)] pub struct Type { // TODO: Maybe make sure this is always sorted by (recursive?!?) TypeId, // that way is_same_type_as can work more efficiently (cuz good code but also branch prediction) - types: Vec>, + pub types: Vec>, } impl Type { pub fn new(t: T) -> Self { @@ -118,11 +213,64 @@ impl Type { pub fn newm(types: Vec>) -> Self { Self { types } } + pub fn empty() -> Self { + Self { types: vec![] } + } pub fn empty_tuple() -> Self { Self::new(tuple::TupleT(vec![])) } - pub fn add(&mut self, new: Box) { - todo!() + /// Returns true if self is `()`. + pub fn is_zero_tuple(&self) -> bool { + let mut o = false; + for t in &self.types { + o = true; + if let Some(tuple) = t.as_any().downcast_ref::() { + if !tuple.0.is_empty() { + return false; + } + } else { + return false; + } + } + o + } + /// Returns `Some(d)` if and only if self is `(d)`. + pub fn one_tuple_content(&self) -> Option { + let mut o = Self::empty(); + for t in &self.types { + if let Some(t) = t + .as_any() + .downcast_ref::() + .filter(|v| v.0.len() == 1) + .and_then(|v| v.0.get(0)) + { + o.add(Arc::new(t.clone())); + } else { + return None; + } + } + Some(o) + } + pub fn add(&mut self, new: Arc) { + let n = new.as_any(); + if let Some(s) = n.downcast_ref::() { + for t in &s.types { + self.add(Arc::clone(t)); + } + } else { + self.types.push(new); + } + } + pub fn dereference(&self) -> Option { + let mut o = Self::empty(); + for t in &self.types { + if let Some(t) = t.is_reference_to() { + o.add(Arc::new(t.clone())); + } else { + return None; + } + } + Some(o) } } @@ -141,7 +289,7 @@ impl MersType for Type { todo!() } fn is_included_in_single(&self, target: &dyn MersType) -> bool { - todo!() + self.types.iter().all(|t| t.is_included_in_single(target)) } fn as_any(&self) -> &dyn Any { self @@ -152,4 +300,45 @@ impl MersType for Type { fn to_any(self) -> Box { Box::new(self) } + fn iterable(&self) -> Option { + let mut o = Self::empty(); + for t in self.types.iter() { + if let Some(t) = t.iterable() { + o.add(Arc::new(t)); + } else { + return None; + } + } + Some(o) + } + fn get(&self) -> Option { + let mut o = Self::empty(); + for t in self.types.iter() { + if let Some(t) = t.get() { + o.add(Arc::new(t)); + } else { + return None; + } + } + Some(o) + } +} +impl Display for Type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.types.is_empty() { + write!(f, "") + } else { + if self.types.len() > 1 { + write!(f, "{{")?; + } + write!(f, "{}", self.types[0])?; + for t in self.types.iter().skip(1) { + write!(f, "/{t}")?; + } + if self.types.len() > 1 { + write!(f, "}}")?; + } + Ok(()) + } + } } diff --git a/mers_lib/src/data/reference.rs b/mers_lib/src/data/reference.rs index b026203..41f3e90 100755 --- a/mers_lib/src/data/reference.rs +++ b/mers_lib/src/data/reference.rs @@ -1,14 +1,28 @@ -use std::{any::Any, fmt::Display, sync::Mutex}; +use std::{ + any::Any, + fmt::Display, + sync::{Arc, Mutex}, +}; use super::{Data, MersData, MersType, Type}; #[derive(Debug, Clone)] -pub struct Reference(pub Data); +pub struct Reference(pub Arc>); impl MersData for Reference { + fn is_eq(&self, other: &dyn MersData) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + *other.0.lock().unwrap() == *self.0.lock().unwrap() + } else { + false + } + } fn clone(&self) -> Box { Box::new(Clone::clone(self)) } + fn as_type(&self) -> Type { + Type::new(ReferenceT(self.0.lock().unwrap().get().as_type())) + } fn as_any(&self) -> &dyn Any { self } @@ -31,6 +45,7 @@ impl MersType for ReferenceT { } } fn is_included_in_single(&self, target: &dyn MersType) -> bool { + // &int isn't included in &(int/float), otherwise we could assign a float to it self.is_same_type_as(target) } fn as_any(&self) -> &dyn Any { @@ -42,10 +57,18 @@ impl MersType for ReferenceT { fn to_any(self) -> Box { Box::new(self) } + fn is_reference_to(&self) -> Option<&Type> { + Some(&self.0) + } } impl Display for Reference { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "&{}", self.0.get()) + write!(f, "&{}", self.0.lock().unwrap().get()) + } +} +impl Display for ReferenceT { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "&{}", self.0) } } diff --git a/mers_lib/src/data/string.rs b/mers_lib/src/data/string.rs index fb4d4bc..db39c34 100755 --- a/mers_lib/src/data/string.rs +++ b/mers_lib/src/data/string.rs @@ -6,12 +6,22 @@ use super::{MersData, MersType, Type}; pub struct String(pub std::string::String); impl MersData for String { + fn is_eq(&self, other: &dyn MersData) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + other.0 == self.0 + } else { + false + } + } fn clone(&self) -> Box { Box::new(Clone::clone(self)) } fn as_any(&self) -> &dyn Any { self } + fn as_type(&self) -> super::Type { + Type::new(StringT) + } fn mut_any(&mut self) -> &mut dyn Any { self } @@ -45,3 +55,8 @@ impl Display for String { write!(f, "{}", self.0) } } +impl Display for StringT { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "String") + } +} diff --git a/mers_lib/src/data/tuple.rs b/mers_lib/src/data/tuple.rs index 0c197a5..eb0a910 100755 --- a/mers_lib/src/data/tuple.rs +++ b/mers_lib/src/data/tuple.rs @@ -1,4 +1,4 @@ -use std::{any::Any, fmt::Display}; +use std::{any::Any, fmt::Display, sync::Arc}; use super::{Data, MersData, MersType, Type}; @@ -15,15 +15,11 @@ impl Tuple { } impl MersData for Tuple { - fn matches(&self) -> Option { - if let Some(d) = self.0.first() { - if self.0.len() == 1 { - Some(d.clone()) - } else { - None - } + fn is_eq(&self, other: &dyn MersData) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + other.0 == self.0 } else { - None + false } } fn iterable(&self) -> Option>> { @@ -32,6 +28,9 @@ impl MersData for Tuple { fn clone(&self) -> Box { Box::new(Clone::clone(self)) } + fn as_type(&self) -> Type { + Type::new(TupleT(self.0.iter().map(|v| v.get().as_type()).collect())) + } fn as_any(&self) -> &dyn Any { self } @@ -46,19 +45,12 @@ impl MersData for Tuple { #[derive(Debug)] pub struct TupleT(pub Vec); impl MersType for TupleT { - fn matches(&self) -> Option<(Type, bool)> { - if let Some(d) = self.0.first() { - if self.0.len() == 1 { - Some((d.clone(), true)) - } else { - None - } - } else { - None - } - } fn iterable(&self) -> Option { - Some(todo!("joine types")) + let mut o = Type::empty(); + for t in self.0.iter() { + o.add(Arc::new(t.clone())); + } + Some(o) } fn is_same_type_as(&self, other: &dyn MersType) -> bool { other.as_any().downcast_ref::().is_some() @@ -90,3 +82,16 @@ impl Display for Tuple { Ok(()) } } +impl Display for TupleT { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "(")?; + for (i, c) in self.0.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}", c)?; + } + write!(f, ")")?; + Ok(()) + } +} diff --git a/mers_lib/src/parsing/errors.rs b/mers_lib/src/parsing/errors.rs index e69de29..8b13789 100755 --- a/mers_lib/src/parsing/errors.rs +++ b/mers_lib/src/parsing/errors.rs @@ -0,0 +1 @@ + diff --git a/mers_lib/src/parsing/mod.rs b/mers_lib/src/parsing/mod.rs index e9860f8..b3ddb86 100755 --- a/mers_lib/src/parsing/mod.rs +++ b/mers_lib/src/parsing/mod.rs @@ -1,9 +1,6 @@ use std::sync::Arc; -use crate::{ - info::Info, - program::{self, parsed::CompInfo}, -}; +use crate::program; pub mod errors; pub mod statements; @@ -96,7 +93,7 @@ impl Source { /// Useful for debugging the parser. pub fn section_begin(&mut self, section: String) -> Arc { #[cfg(debug_assertions)] - println!("Section begin: {}", §ion); + println!("[mers:parse] Section begin: {}", §ion); let arc = Arc::new(section); self.sections.push(SectionMarker { section: Arc::clone(&arc), @@ -141,7 +138,7 @@ impl SectionMarker { if Arc::strong_count(&self.section) == 1 { self.end = Some(end); #[cfg(debug_assertions)] - println!("Section end : {}", &self.section); + println!("[mers:parse] Section end : {}", &self.section); } } } diff --git a/mers_lib/src/parsing/statements.rs b/mers_lib/src/parsing/statements.rs index e678504..90cdfca 100755 --- a/mers_lib/src/parsing/statements.rs +++ b/mers_lib/src/parsing/statements.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use super::Source; use crate::{ - data::{self, Data}, + data::Data, program::{self, parsed::MersStatement}, }; @@ -142,12 +142,6 @@ pub fn parse_no_chain( }, }) } - "loop" => { - src.section_begin("loop".to_string()); - Box::new(program::parsed::r#loop::Loop { - inner: parse(src)?.expect("err: EOF instead of inner statement after loop"), - }) - } "switch" => { src.section_begin("loop".to_string()); todo!() diff --git a/mers_lib/src/program/configs/mod.rs b/mers_lib/src/program/configs/mod.rs index dd899a6..00ebd47 100755 --- a/mers_lib/src/program/configs/mod.rs +++ b/mers_lib/src/program/configs/mod.rs @@ -1,17 +1,18 @@ -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use crate::{ - data::{self, Data, Type}, + data::{Data, Type}, info::Local, - program, }; +mod with_base; mod with_command_running; mod with_get; mod with_iters; mod with_list; mod with_math; -mod with_prints; +mod with_multithreading; +mod with_stdio; /// Usage: create an empty Config using Config::new(), use the methods to customize it, then get the Infos using Config::infos() /// bundle_* for bundles (combines multiple groups or even bundles) @@ -27,23 +28,30 @@ pub struct Config { globals: usize, info_parsed: super::parsed::Info, info_run: super::run::Info, + info_check: super::run::CheckInfo, } impl Config { /// standard utilitis used in many programs /// `bundle_base()` + /// `with_stdio()` /// `with_list()` /// `with_command_running()` + /// `with_multithreading()` pub fn bundle_std(self) -> Self { - self.with_command_running().with_list().bundle_base() + self.with_multithreading() + .with_command_running() + .with_list() + .with_stdio() + .bundle_base() } /// base utilities used in most programs - /// `with_prints()` + /// `with_base()` /// `with_math()` /// `with_get()` /// `with_iters()` pub fn bundle_base(self) -> Self { - self.with_iters().with_get().with_math().with_prints() + self.with_iters().with_get().with_math().with_base() } pub fn new() -> Self { @@ -51,12 +59,15 @@ impl Config { globals: 0, info_parsed: Default::default(), info_run: Default::default(), + info_check: Default::default(), } } pub fn add_var(mut self, name: String, val: Data) -> Self { + let t = val.get().as_type(); self.info_parsed.scopes[0].init_var(name, (0, self.globals)); - self.info_run.scopes[0].init_var(self.globals, val); + self.info_run.scopes[0].init_var(self.globals, Arc::new(Mutex::new(val))); + self.info_check.scopes[0].init_var(self.globals, t); self.globals += 1; self } @@ -65,7 +76,7 @@ impl Config { self } - pub fn infos(self) -> (super::parsed::Info, super::run::Info) { - (self.info_parsed, self.info_run) + pub fn infos(self) -> (super::parsed::Info, super::run::Info, super::run::CheckInfo) { + (self.info_parsed, self.info_run, self.info_check) } } diff --git a/mers_lib/src/program/configs/with_base.rs b/mers_lib/src/program/configs/with_base.rs new file mode 100755 index 0000000..a0eb758 --- /dev/null +++ b/mers_lib/src/program/configs/with_base.rs @@ -0,0 +1,108 @@ +use std::sync::{Arc, Mutex}; + +use crate::{ + data::{self, Data, MersType, Type}, + program::run::{CheckInfo, Info}, +}; + +use super::Config; + +impl Config { + /// `deref: fn` clones the value from a reference + /// `eq: fn` returns true if all the values are equal, otherwise false. + /// `loop: fn` runs a function until it returns (T) instead of (), then returns T. + pub fn with_base(self) -> Self { + self.add_var( + "loop".to_string(), + Data::new(data::function::Function { + info: Arc::new(Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| { + dbg!(a); + let mut o = Type::empty(); + for t in &a.types { + if let Some(t) = t.as_any().downcast_ref::() { + for t in (t.0)(&Type::empty_tuple())?.types { + if let Some(t) = t.as_any().downcast_ref::() { + if t.0.len() > 1 { + return Err(crate::program::run::CheckError(format!("called loop with funcion that might return a tuple of length > 1"))); + } else if let Some(v) = t.0.first() { + o.add(Arc::new(v.clone())) + } + } else { + return Err(crate::program::run::CheckError(format!("called loop with funcion that might return something other than a tuple"))); + } + } + } else { + return Err(crate::program::run::CheckError(format!("called loop on a non-function"))); + } + } + Ok(o) + }), + run: Arc::new(|a, _i| { + if let Some(r) = a.get().as_any().downcast_ref::() { + loop { + if let Some(r) = r.run(Data::empty_tuple()).one_tuple_content() { + break r; + } + } + } else { + unreachable!("called loop on non-function") + } + }), + }), + ) + .add_var( + "eq".to_string(), + Data::new(data::function::Function { + info: Arc::new(Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| { + for t in &a.types { + if t.iterable().is_none() { + return Err(crate::program::run::CheckError(format!("called eq on non-iterable"))) + } + } + Ok(Type::new(data::bool::BoolT)) + }), + run: Arc::new(|a, _i| { + Data::new(data::bool::Bool(if let Some(mut i) = a.get().iterable() { + if let Some(f) = i.next() { + let mut o = true; + for el in i { + if el != f { + o = false; + break; + } + } + o + } else { + false + } + } else { + false + })) + }), + }), + ) + .add_var( + "deref".to_string(), + Data::new(data::function::Function { + info: Arc::new(Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| if let Some(v) = a.dereference() { Ok(v) } else { Err(crate::program::run::CheckError(format!("cannot dereference type {a}")))}), + run: Arc::new(|a, _i| { + if let Some(r) = a + .get() + .as_any() + .downcast_ref::() + { + r.0.lock().unwrap().clone() + } else { + unreachable!("called deref on non-reference") + } + }), + }), + ) + } +} diff --git a/mers_lib/src/program/configs/with_command_running.rs b/mers_lib/src/program/configs/with_command_running.rs index 91c5ebd..574fe9b 100755 --- a/mers_lib/src/program/configs/with_command_running.rs +++ b/mers_lib/src/program/configs/with_command_running.rs @@ -1,8 +1,12 @@ -use std::{fmt::Display, process::Command, sync::Arc}; +use std::{ + fmt::Display, + process::Command, + sync::{Arc, Mutex}, +}; use crate::{ - data::{self, Data, MersData, MersType}, - program, + data::{self, Data, MersData, MersType, Type}, + program::{self, run::CheckInfo}, }; use super::Config; @@ -16,8 +20,9 @@ impl Config { self.add_var( "run_command".to_string(), Data::new(data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_a| todo!()), + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new( CheckInfo::neverused())), + out: Arc::new(|a, i| todo!()), run: Arc::new(|a, _i| { if let Some(cmd) = a.get().as_any().downcast_ref::() { if let (Some(cmd), Some(args)) = (cmd.get(0), cmd.get(1)) { @@ -67,9 +72,19 @@ pub struct RunCommandError(String); #[derive(Debug)] pub struct RunCommandErrorT; impl MersData for RunCommandError { + fn is_eq(&self, other: &dyn MersData) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + other.0 == self.0 + } else { + false + } + } fn clone(&self) -> Box { Box::new(Clone::clone(self)) } + fn as_type(&self) -> data::Type { + Type::new(RunCommandErrorT) + } fn as_any(&self) -> &dyn std::any::Any { self } @@ -102,3 +117,8 @@ impl Display for RunCommandError { write!(f, "RunCommandError: {}", self.0) } } +impl Display for RunCommandErrorT { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "RunCommandError") + } +} diff --git a/mers_lib/src/program/configs/with_get.rs b/mers_lib/src/program/configs/with_get.rs old mode 100644 new mode 100755 index a6268d7..fbc5007 --- a/mers_lib/src/program/configs/with_get.rs +++ b/mers_lib/src/program/configs/with_get.rs @@ -1,8 +1,8 @@ -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use crate::{ - data::{self, Data}, - program, + data::{self, Data, MersType}, + program::{self, run::CheckInfo}, }; use super::Config; @@ -13,8 +13,17 @@ impl Config { self.add_var( "get".to_string(), Data::new(data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_a| todo!()), + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| { + if let Some(v) = a.get() { + Ok(v) + } else { + Err(program::run::CheckError(format!( + "called get on non-gettable type {a}" + ))) + } + }), run: Arc::new(|a, _i| { let a = a.get(); if let (Some(v), Some(i)) = (a.get(0), a.get(1)) { diff --git a/mers_lib/src/program/configs/with_iters.rs b/mers_lib/src/program/configs/with_iters.rs index e5467fa..6ed6d98 100755 --- a/mers_lib/src/program/configs/with_iters.rs +++ b/mers_lib/src/program/configs/with_iters.rs @@ -1,8 +1,14 @@ -use std::{fmt::Display, sync::Arc}; +use std::{ + fmt::Display, + sync::{Arc, Mutex}, +}; use crate::{ - data::{self, Data, MersData}, - program, + data::{self, Data, MersData, MersType, Type}, + program::{ + self, + run::{CheckError, CheckInfo}, + }, }; use super::Config; @@ -12,14 +18,51 @@ impl Config { /// `iter: fn` executes a function once for each element of the iterable /// `map: fn` maps each value in the iterable to a new one by applying a transformation function /// `filter: fn` filters the iterable by removing all elements where the filter function doesn't return true - /// `filter_map: fn` combines filter and map via matching + /// `filter_map: fn` combines filter and map /// `enumerate: fn` transforms an iterator over T into one over (Int, T), where Int is the index of the element pub fn with_iters(self) -> Self { self.add_var( - "iter".to_string(), + "for_each".to_string(), Data::new(data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_a| todo!()), + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| { + for a in &a.types { + if let Some(tuple) = a.as_any().downcast_ref::() { + if let (Some(v), Some(f)) = (tuple.0.get(0), tuple.0.get(1)) { + if let (Some(iter), Some(f)) = ( + v.iterable(), + f.types + .iter() + .map(|t| { + t.as_any().downcast_ref::() + }) + .collect::>>(), + ) { + for f in f { + let ret = f.0(&iter)?; + if !ret.is_zero_tuple() { + return Err(CheckError(format!( + "for_each function must return (), not {ret}" + ))); + } + } + } else { + return Err(CheckError(format!( + "for_each called on tuple not containing iterable and function" + ))); + } + } else { + return Err(CheckError(format!( + "for_each called on tuple with len < 2" + ))); + } + } else { + return Err(CheckError(format!("for_each called on non-tuple"))); + } + } + Ok(Type::empty_tuple()) + }), run: Arc::new(|a, _i| { if let Some(tuple) = a.get().as_any().downcast_ref::() { if let (Some(v), Some(f)) = (tuple.get(0), tuple.get(1)) { @@ -33,14 +76,14 @@ impl Config { Data::empty_tuple() } else { unreachable!( - "iter called on tuple not containing iterable and function" + "for_each called on tuple not containing iterable and function" ) } } else { - unreachable!("iter called on tuple with len < 2") + unreachable!("for_each called on tuple with len < 2") } } else { - unreachable!("iter called on non-tuple") + unreachable!("for_each called on non-tuple") } }), }), @@ -48,8 +91,9 @@ impl Config { .add_var( "map".to_string(), Data::new(data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_a| todo!()), + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| todo!()), run: Arc::new(|a, _i| { if let Some(tuple) = a.get().as_any().downcast_ref::() { if let (Some(v), Some(f)) = (tuple.get(0), tuple.get(1)) { @@ -72,8 +116,9 @@ impl Config { .add_var( "filter".to_string(), Data::new(data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_a| todo!()), + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| todo!()), run: Arc::new(|a, _i| { if let Some(tuple) = a.get().as_any().downcast_ref::() { if let (Some(v), Some(f)) = (tuple.get(0), tuple.get(1)) { @@ -96,8 +141,9 @@ impl Config { .add_var( "filter_map".to_string(), Data::new(data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_a| todo!()), + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| todo!()), run: Arc::new(|a, _i| { if let Some(tuple) = a.get().as_any().downcast_ref::() { if let (Some(v), Some(f)) = (tuple.get(0), tuple.get(1)) { @@ -120,8 +166,9 @@ impl Config { .add_var( "enumerate".to_string(), Data::new(data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_a| todo!()), + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| todo!()), run: Arc::new(|a, _i| Data::new(Iter(Iters::Enumerate, a.clone()))), }), ) @@ -140,6 +187,9 @@ pub struct Iter(Iters, Data); #[derive(Clone, Debug)] pub struct IterT(Iters); impl MersData for Iter { + fn is_eq(&self, _other: &dyn MersData) -> bool { + false + } fn iterable(&self) -> Option>> { Some(match &self.0 { Iters::Map(f) => { @@ -162,7 +212,7 @@ impl MersData for Iter { self.1 .get() .iterable()? - .filter_map(move |v| f.run(v).get().matches()), + .filter_map(move |v| f.run(v).one_tuple_content()), ) } Iters::Enumerate => Box::new(self.1.get().iterable()?.enumerate().map(|(i, v)| { @@ -171,12 +221,36 @@ impl MersData for Iter { v, ])) })), - _ => todo!(), }) } fn clone(&self) -> Box { Box::new(Clone::clone(self)) } + fn as_type(&self) -> data::Type { + Type::new(IterT(self.0.clone())) + } + 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 IterT { + fn is_same_type_as(&self, other: &dyn MersType) -> bool { + false + } + fn is_included_in_single(&self, target: &dyn MersType) -> bool { + if let Some(target) = target.as_any().downcast_ref::() { + // TODO: ? + false + } else { + false + } + } fn as_any(&self) -> &dyn std::any::Any { self } @@ -192,3 +266,8 @@ impl Display for Iter { write!(f, "") } } +impl Display for IterT { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "") + } +} diff --git a/mers_lib/src/program/configs/with_list.rs b/mers_lib/src/program/configs/with_list.rs index 1c8c954..5e10fc7 100755 --- a/mers_lib/src/program/configs/with_list.rs +++ b/mers_lib/src/program/configs/with_list.rs @@ -1,8 +1,11 @@ -use std::{fmt::Display, sync::Arc}; +use std::{ + fmt::Display, + sync::{Arc, Mutex}, +}; use crate::{ data::{self, Data, MersData, MersType, Type}, - program, + program::{self, run::CheckInfo}, }; use super::Config; @@ -17,8 +20,17 @@ impl Config { .add_var( "as_list".to_string(), Data::new(data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_a| todo!()), + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| { + if let Some(v) = a.iterable() { + Ok(Type::new(ListT(v))) + } else { + Err(program::run::CheckError(format!( + "cannot iterate over type {a}" + ))) + } + }), run: Arc::new(|a, _i| { if let Some(i) = a.get().iterable() { Data::new(List(i.collect())) @@ -36,12 +48,26 @@ pub struct List(Vec); #[derive(Debug)] pub struct ListT(Type); impl MersData for List { + fn is_eq(&self, other: &dyn MersData) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + other.0 == self.0 + } else { + false + } + } fn iterable(&self) -> Option>> { Some(Box::new(self.0.clone().into_iter())) } fn clone(&self) -> Box { Box::new(Clone::clone(self)) } + fn as_type(&self) -> Type { + let mut t = Type::empty(); + for el in &self.0 { + t.add(Arc::new(el.get().as_type())); + } + Type::new(ListT(t)) + } fn as_any(&self) -> &dyn std::any::Any { self } @@ -88,3 +114,9 @@ impl Display for List { Ok(()) } } +impl Display for ListT { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[{}]", self.0)?; + Ok(()) + } +} diff --git a/mers_lib/src/program/configs/with_math.rs b/mers_lib/src/program/configs/with_math.rs old mode 100644 new mode 100755 index 22ef591..b66054d --- a/mers_lib/src/program/configs/with_math.rs +++ b/mers_lib/src/program/configs/with_math.rs @@ -1,8 +1,11 @@ -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use crate::{ - data::{self, Data}, - program, + data::{self, Data, Type}, + program::{ + self, + run::{CheckError, CheckInfo}, + }, }; use super::Config; @@ -13,8 +16,38 @@ impl Config { self.add_var( "sum".to_string(), Data::new(data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_a| todo!()), + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| { + let mut ints = false; + let mut floats = false; + for a in &a.types { + if let Some(i) = a.iterable() { + if i.types + .iter() + .all(|t| t.as_any().downcast_ref::().is_some()) + { + ints = true; + } else if i.types.iter().all(|t| { + t.as_any().downcast_ref::().is_some() + || t.as_any().downcast_ref::().is_some() + }) { + floats = true; + } else { + return Err(CheckError(format!("cannot get sum of iterator over type {i} because it contains types that aren't int/float"))) + } + } else { + return Err(CheckError(format!( + "cannot get sum of non-iterable type {a}" + ))); + } + } + Ok(match (ints, floats) { + (_, true) => Type::new(data::float::FloatT), + (true, false) => Type::new(data::int::IntT), + (false, false) => Type::empty(), + }) + }), run: Arc::new(|a, _i| { if let Some(i) = a.get().iterable() { let mut sumi = 0; diff --git a/mers_lib/src/program/configs/with_multithreading.rs b/mers_lib/src/program/configs/with_multithreading.rs new file mode 100755 index 0000000..cd0b223 --- /dev/null +++ b/mers_lib/src/program/configs/with_multithreading.rs @@ -0,0 +1,156 @@ +use std::{ + fmt::{Debug, Display}, + sync::{Arc, Mutex}, + thread::JoinHandle, +}; + +use crate::{ + data::{self, Data, MersData, MersType, Type}, + program::{self, run::CheckInfo}, +}; + +use super::Config; + +impl Config { + /// `get: fn` is used to retrieve elements from collections + /// `thread: fn` turns `(func, arg)` into a `Thread`, which will run the function with the argument. + /// `thread_get_result: fn` returns `()` while the thread is running and `(result)` otherwise. + pub fn with_multithreading(self) -> Self { + self.add_type( + "Thread".to_string(), + Type::new(ThreadT(Type::empty_tuple())), + ) + .add_var( + "thread".to_string(), + Data::new(data::function::Function { + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| todo!()), + run: Arc::new(|a, _i| { + let a = a.get(); + if let (Some(f), Some(arg)) = ( + a.get(0).and_then(|v| { + v.get() + .as_any() + .downcast_ref::() + .cloned() + }), + a.get(1), + ) { + Data::new(Thread(Arc::new(Mutex::new(Ok(std::thread::spawn( + move || f.run(arg), + )))))) + } else { + unreachable!("thread called, but arg wasn't a (function, _)"); + } + }), + }), + ) + .add_var( + "thread_get_result".to_string(), + Data::new(data::function::Function { + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| todo!()), + run: Arc::new(|a, _i| { + let a = a.get(); + if let Some(t) = a + .get(0) + .and_then(|v| v.get().as_any().downcast_ref::().cloned()) + { + let mut t = t.0.lock().unwrap(); + if t.as_ref().is_ok_and(|t| t.is_finished()) { + unsafe { + // extract the JoinHandle from the Result by replacing it with uninitialized memory. + #[allow(invalid_value)] + let thread = std::mem::replace( + &mut *t, + std::mem::MaybeUninit::uninit().assume_init(), + ) + .unwrap(); + // forget about t and its uninitialized memory while replacing it with the new value + std::mem::forget(std::mem::replace( + &mut *t, + Err(thread.join().unwrap()), + )); + } + } + match &*t { + Ok(_) => Data::empty_tuple(), + Err(v) => Data::one_tuple(v.clone()), + } + } else { + unreachable!("thread_get_result called, but arg wasn't a Thread"); + } + }), + }), + ) + } +} + +#[derive(Clone)] +pub struct Thread(Arc, Data>>>); +#[derive(Debug)] +pub struct ThreadT(Type); + +impl MersData for Thread { + fn is_eq(&self, _other: &dyn MersData) -> bool { + false + } + fn clone(&self) -> Box { + Box::new(Clone::clone(self)) + } + fn as_type(&self) -> Type { + unreachable!("can't get type from Thread value! (can't construct Thread with syntax, so this should be fine?)") + } + 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 ThreadT { + fn is_same_type_as(&self, other: &dyn MersType) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + self.0.is_same_type_as(&other.0) + } else { + false + } + } + fn is_included_in_single(&self, target: &dyn MersType) -> bool { + if let Some(target) = target.as_any().downcast_ref::() { + self.0.is_included_in_single(&target.0) + } else { + false + } + } + 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 Debug for Thread { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "") + } +} +impl Display for Thread { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "") + } +} +impl Display for ThreadT { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "") + } +} diff --git a/mers_lib/src/program/configs/with_prints.rs b/mers_lib/src/program/configs/with_prints.rs deleted file mode 100644 index f227c14..0000000 --- a/mers_lib/src/program/configs/with_prints.rs +++ /dev/null @@ -1,73 +0,0 @@ -use std::sync::Arc; - -use crate::{ - data::{self, Data}, - program, -}; - -use super::Config; - -impl Config { - /// `println: fn` prints to stdout and adds a newline to the end - /// `print: fn` prints to stdout - /// `eprintln: fn` prints to stderr and adds a newline to the end - /// `eprint: fn` prints to stderr - /// `debug: fn` debug-prints any value - pub fn with_prints(self) -> Self { - self.add_var( - "debug".to_string(), - Data::new(data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_a| todo!()), - run: Arc::new(|a, _i| { - eprintln!("{:#?}", a.get()); - Data::empty_tuple() - }), - }), - ) - .add_var( - "eprint".to_string(), - Data::new(data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_a| todo!()), - run: Arc::new(|a, _i| { - eprint!("{}", a.get()); - Data::empty_tuple() - }), - }), - ) - .add_var( - "eprintln".to_string(), - Data::new(data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_a| todo!()), - run: Arc::new(|a, _i| { - eprintln!("{}", a.get()); - Data::empty_tuple() - }), - }), - ) - .add_var( - "print".to_string(), - Data::new(data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_a| todo!()), - run: Arc::new(|a, _i| { - print!("{}", a.get()); - Data::empty_tuple() - }), - }), - ) - .add_var( - "println".to_string(), - Data::new(data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_a| todo!()), - run: Arc::new(|a, _i| { - println!("{}", a.get()); - Data::empty_tuple() - }), - }), - ) - } -} diff --git a/mers_lib/src/program/configs/with_stdio.rs b/mers_lib/src/program/configs/with_stdio.rs new file mode 100755 index 0000000..738d484 --- /dev/null +++ b/mers_lib/src/program/configs/with_stdio.rs @@ -0,0 +1,92 @@ +use std::sync::{Arc, Mutex}; + +use crate::{ + data::{self, Data, Type}, + program::{self, run::CheckInfo}, +}; + +use super::Config; + +impl Config { + /// `println: fn` prints to stdout and adds a newline to the end + /// `print: fn` prints to stdout + /// `eprintln: fn` prints to stderr and adds a newline to the end + /// `eprint: fn` prints to stderr + /// `debug: fn` debug-prints any value + /// `read_line: fn` reads a line from stdin and returns it + pub fn with_stdio(self) -> Self { + self.add_var( + "read_line".to_string(), + Data::new(data::function::Function { + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| Ok(Type::new(data::string::StringT))), + run: Arc::new(|_a, _i| { + let mut line = String::new(); + _ = std::io::stdin().read_line(&mut line); + Data::new(data::string::String(line)) + }), + }), + ) + .add_var( + "debug".to_string(), + Data::new(data::function::Function { + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| Ok(Type::empty_tuple())), + run: Arc::new(|a, _i| { + eprintln!("{:#?}", a.get()); + Data::empty_tuple() + }), + }), + ) + .add_var( + "eprint".to_string(), + Data::new(data::function::Function { + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| Ok(Type::empty_tuple())), + run: Arc::new(|a, _i| { + eprint!("{}", a.get()); + Data::empty_tuple() + }), + }), + ) + .add_var( + "eprintln".to_string(), + Data::new(data::function::Function { + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| Ok(Type::empty_tuple())), + run: Arc::new(|a, _i| { + eprintln!("{}", a.get()); + Data::empty_tuple() + }), + }), + ) + .add_var( + "print".to_string(), + Data::new(data::function::Function { + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| Ok(Type::empty_tuple())), + run: Arc::new(|a, _i| { + print!("{}", a.get()); + Data::empty_tuple() + }), + }), + ) + .add_var( + "println".to_string(), + Data::new(data::function::Function { + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(|a, i| Ok(Type::empty_tuple())), + run: Arc::new(|a, _i| { + println!("{}", a.get()); + Data::empty_tuple() + }), + }), + ) + } +} diff --git a/mers_lib/src/program/parsed/assign_to.rs b/mers_lib/src/program/parsed/assign_to.rs index 17d27d7..ed2285b 100755 --- a/mers_lib/src/program/parsed/assign_to.rs +++ b/mers_lib/src/program/parsed/assign_to.rs @@ -1,4 +1,4 @@ -use crate::{info::Local, program}; +use crate::program; use super::{CompInfo, MersStatement}; @@ -15,9 +15,10 @@ impl MersStatement for AssignTo { fn compile_custom( &self, info: &mut crate::info::Info, - mut comp: CompInfo, + comp: CompInfo, ) -> Result, String> { Ok(Box::new(program::run::assign_to::AssignTo { + is_init: false, target: self.target.compile(info, comp)?, source: self.source.compile(info, comp)?, })) diff --git a/mers_lib/src/program/parsed/function.rs b/mers_lib/src/program/parsed/function.rs index 4cb60bf..841b15c 100755 --- a/mers_lib/src/program/parsed/function.rs +++ b/mers_lib/src/program/parsed/function.rs @@ -1,9 +1,8 @@ -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use crate::{ - data::{self, Data}, - info::Local, - program, + data, + program::{self, run::CheckInfo}, }; use super::{CompInfo, MersStatement}; @@ -17,7 +16,7 @@ pub struct Function { impl MersStatement for Function { fn has_scope(&self) -> bool { // TODO: what??? - false + true } fn compile_custom( &self, @@ -25,15 +24,21 @@ impl MersStatement for Function { mut comp: CompInfo, ) -> Result, String> { comp.is_init = true; - let arg = self.arg.compile(info, comp)?; + let arg_target = Arc::new(self.arg.compile(info, comp)?); comp.is_init = false; - let run = self.run.compile(info, comp)?; + let run = Arc::new(self.run.compile(info, comp)?); + let arg2 = Arc::clone(&arg_target); + let run2 = Arc::clone(&run); Ok(Box::new(program::run::function::Function { func_no_info: data::function::Function { - info: program::run::Info::neverused(), - out: Arc::new(|_i| todo!()), - run: Arc::new(move |i, info| { - data::defs::assign(i, &arg.run(info)); + info: Arc::new(program::run::Info::neverused()), + info_check: Arc::new(Mutex::new(CheckInfo::neverused())), + out: Arc::new(move |a, i| { + arg2.check(i, Some(a))?; + Ok(run2.check(i, None)?) + }), + run: Arc::new(move |arg, info| { + data::defs::assign(arg, &arg_target.run(info)); run.run(info) }), }, diff --git a/mers_lib/src/program/parsed/if.rs b/mers_lib/src/program/parsed/if.rs index 958368f..8804349 100755 --- a/mers_lib/src/program/parsed/if.rs +++ b/mers_lib/src/program/parsed/if.rs @@ -20,7 +20,7 @@ impl MersStatement for If { ) -> Result, String> { Ok(Box::new(program::run::r#if::If { condition: self.condition.compile(info, comp)?, - on_true: self.condition.compile(info, comp)?, + on_true: self.on_true.compile(info, comp)?, on_false: if let Some(v) = &self.on_false { Some(v.compile(info, comp)?) } else { diff --git a/mers_lib/src/program/parsed/init_to.rs b/mers_lib/src/program/parsed/init_to.rs index 71519a1..34b41e1 100755 --- a/mers_lib/src/program/parsed/init_to.rs +++ b/mers_lib/src/program/parsed/init_to.rs @@ -1,4 +1,4 @@ -use crate::{info::Local, program}; +use crate::program; use super::{CompInfo, MersStatement}; @@ -22,6 +22,7 @@ impl MersStatement for InitTo { comp.is_init = false; let source = self.source.compile(info, comp)?; Ok(Box::new(program::run::assign_to::AssignTo { + is_init: true, target, source, })) diff --git a/mers_lib/src/program/parsed/loop.rs b/mers_lib/src/program/parsed/loop.rs deleted file mode 100755 index e8b42db..0000000 --- a/mers_lib/src/program/parsed/loop.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::program; - -use super::{CompInfo, MersStatement}; - -#[derive(Debug)] -pub struct Loop { - pub inner: Box, -} - -impl MersStatement for Loop { - fn has_scope(&self) -> bool { - true - } - fn compile_custom( - &self, - info: &mut crate::info::Info, - comp: CompInfo, - ) -> Result, String> { - Ok(Box::new(program::run::r#loop::Loop { - inner: self.inner.compile(info, comp)?, - })) - } -} diff --git a/mers_lib/src/program/parsed/mod.rs b/mers_lib/src/program/parsed/mod.rs index 44948d0..7f54117 100755 --- a/mers_lib/src/program/parsed/mod.rs +++ b/mers_lib/src/program/parsed/mod.rs @@ -15,17 +15,13 @@ pub mod r#if; #[cfg(feature = "parse")] pub mod init_to; #[cfg(feature = "parse")] -pub mod r#loop; -#[cfg(feature = "parse")] -pub mod switch; -#[cfg(feature = "parse")] pub mod tuple; #[cfg(feature = "parse")] pub mod value; #[cfg(feature = "parse")] pub mod variable; -pub trait MersStatement: Debug { +pub trait MersStatement: Debug + Send + Sync { fn has_scope(&self) -> bool; fn compile_custom( &self, diff --git a/mers_lib/src/program/parsed/switch.rs b/mers_lib/src/program/parsed/switch.rs deleted file mode 100755 index c654260..0000000 --- a/mers_lib/src/program/parsed/switch.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::data::Type; - -use super::{CompInfo, MersStatement}; - -#[derive(Debug)] -pub struct Switch { - source: Box, - arms: Vec, -} - -#[derive(Debug)] -pub struct SwitchArm { - requires_type: Type, -} - -impl MersStatement for Switch { - fn has_scope(&self) -> bool { - true - } - fn compile_custom( - &self, - info: &mut crate::info::Info, - comp: CompInfo, - ) -> Result, String> { - todo!() - } -} diff --git a/mers_lib/src/program/parsed/value.rs b/mers_lib/src/program/parsed/value.rs index 80f5cf8..8e20eaa 100755 --- a/mers_lib/src/program/parsed/value.rs +++ b/mers_lib/src/program/parsed/value.rs @@ -11,8 +11,8 @@ impl MersStatement for Value { } fn compile_custom( &self, - info: &mut crate::info::Info, - comp: CompInfo, + _info: &mut crate::info::Info, + _comp: CompInfo, ) -> Result, String> { Ok(Box::new(program::run::value::Value { val: self.0.clone(), diff --git a/mers_lib/src/program/parsed/variable.rs b/mers_lib/src/program/parsed/variable.rs index 9fd8cc1..a94118d 100755 --- a/mers_lib/src/program/parsed/variable.rs +++ b/mers_lib/src/program/parsed/variable.rs @@ -27,6 +27,7 @@ impl MersStatement for Variable { ) } Ok(Box::new(program::run::variable::Variable { + is_init: comp.is_init, is_ref: comp.is_init || self.is_ref, var: if let Some(v) = info.get_var(&self.var) { *v diff --git a/mers_lib/src/program/run/assign_to.rs b/mers_lib/src/program/run/assign_to.rs index 4d8c1a4..545ba30 100755 --- a/mers_lib/src/program/run/assign_to.rs +++ b/mers_lib/src/program/run/assign_to.rs @@ -1,14 +1,29 @@ -use crate::data; +use crate::data::{self, Type}; -use super::MersStatement; +use super::{CheckError, CheckInfo, MersStatement}; #[derive(Debug)] pub struct AssignTo { + pub is_init: bool, pub target: Box, pub source: Box, } impl MersStatement for AssignTo { + fn check_custom( + &self, + info: &mut CheckInfo, + init_to: Option<&Type>, + ) -> Result { + if init_to.is_some() { + return Err(CheckError( + "can't init to statement type AssignTo".to_string(), + )); + } + let source = self.source.check(info, None)?; + let target = self.target.check(info, Some(&source))?; + Ok(source) + } fn run_custom(&self, info: &mut super::Info) -> crate::data::Data { let source = self.source.run(info); let target = self.target.run(info); diff --git a/mers_lib/src/program/run/block.rs b/mers_lib/src/program/run/block.rs index 6b5181b..10a3212 100755 --- a/mers_lib/src/program/run/block.rs +++ b/mers_lib/src/program/run/block.rs @@ -1,12 +1,26 @@ -use std::sync::Arc; +use crate::data::Type; -use super::MersStatement; +use super::{CheckError, MersStatement}; #[derive(Debug)] pub struct Block { pub statements: Vec>, } impl MersStatement for Block { + fn check_custom( + &self, + info: &mut super::CheckInfo, + init_to: Option<&Type>, + ) -> Result { + if init_to.is_some() { + return Err(CheckError("can't init to statement type Block".to_string())); + } + let mut o = Type::empty_tuple(); + for s in &self.statements { + o = s.check(info, None)?; + } + Ok(o) + } fn run_custom(&self, info: &mut super::Info) -> crate::data::Data { self.statements .iter() diff --git a/mers_lib/src/program/run/chain.rs b/mers_lib/src/program/run/chain.rs index cf42d76..1647c2a 100755 --- a/mers_lib/src/program/run/chain.rs +++ b/mers_lib/src/program/run/chain.rs @@ -1,8 +1,8 @@ -use std::{any::Any, sync::Arc}; +use std::sync::Arc; -use crate::data::{function::Function, Data}; +use crate::data::{Data, Type}; -use super::MersStatement; +use super::{CheckError, MersStatement}; #[derive(Debug)] pub struct Chain { @@ -10,6 +10,37 @@ pub struct Chain { pub chained: Box, } impl MersStatement for Chain { + fn check_custom( + &self, + info: &mut super::CheckInfo, + init_to: Option<&Type>, + ) -> Result { + if init_to.is_some() { + return Err(CheckError("can't init to statement type Chain".to_string())); + } + let arg = self.first.check(info, None)?; + let func = self.chained.check(info, None)?; + let mut o = Type::empty(); + for func in &func.types { + if let Some(func) = func + .as_any() + .downcast_ref::() + { + match (func.0)(&arg) { + Ok(t) => o.add(Arc::new(t)), + Err(e) => + return Err(CheckError(format!( + "cannot run this function with this argument (type: {arg}), because it would cause the following error:\n{e}" + ))), + } + } else { + return Err(CheckError(format!( + "cannot chain with a non-function ({func})" + ))); + } + } + Ok(o) + } fn run_custom(&self, info: &mut super::Info) -> Data { let f = self.first.run(info); let c = self.chained.run(info); diff --git a/mers_lib/src/program/run/function.rs b/mers_lib/src/program/run/function.rs index 51b5974..f34ca7e 100755 --- a/mers_lib/src/program/run/function.rs +++ b/mers_lib/src/program/run/function.rs @@ -1,6 +1,8 @@ -use crate::data::{self, Data, MersData}; +use std::sync::Arc; -use super::MersStatement; +use crate::data::{self, Data, MersData, Type}; + +use super::{CheckError, MersStatement}; #[derive(Debug)] pub struct Function { @@ -8,10 +10,23 @@ pub struct Function { } impl MersStatement for Function { - fn has_scope(&self) -> bool { - false + fn check_custom( + &self, + info: &mut super::CheckInfo, + init_to: Option<&Type>, + ) -> Result { + if init_to.is_some() { + return Err(CheckError( + "can't init to statement type Function".to_string(), + )); + } + self.func_no_info.with_info_check(info.clone()); + Ok(self.func_no_info.as_type()) } fn run_custom(&self, info: &mut super::Info) -> Data { - Data::new(self.func_no_info.with_info(info.clone())) + Data::new(self.func_no_info.with_info_run(Arc::new(info.clone()))) + } + fn has_scope(&self) -> bool { + true } } diff --git a/mers_lib/src/program/run/if.rs b/mers_lib/src/program/run/if.rs index 2e54544..113bfe6 100755 --- a/mers_lib/src/program/run/if.rs +++ b/mers_lib/src/program/run/if.rs @@ -1,4 +1,8 @@ -use super::MersStatement; +use std::sync::Arc; + +use crate::data::{self, Data, MersType, Type}; + +use super::{CheckError, MersStatement}; #[derive(Debug)] pub struct If { @@ -8,9 +12,45 @@ pub struct If { } impl MersStatement for If { + fn check_custom( + &self, + info: &mut super::CheckInfo, + init_to: Option<&Type>, + ) -> Result { + if init_to.is_some() { + return Err(CheckError("can't init to statement type If".to_string())); + } + if !self + .condition + .check(info, None)? + .is_included_in(&data::bool::BoolT) + { + return Err(CheckError(format!( + "condition in an if-statement must return bool" + ))); + } + let mut t = self.on_true.check(info, None)?; + if let Some(f) = &self.on_false { + t.add(Arc::new(f.check(info, None)?)); + } else { + t.add(Arc::new(Type::empty_tuple())); + } + Ok(t) + } fn run_custom(&self, info: &mut super::Info) -> crate::data::Data { - self.condition.run(info); - todo!("what now?") + if let Some(data::bool::Bool(true)) = self + .condition + .run(info) + .get() + .as_any() + .downcast_ref::() + { + self.on_true.run(info) + } else if let Some(on_false) = &self.on_false { + on_false.run(info) + } else { + Data::empty_tuple() + } } fn has_scope(&self) -> bool { true diff --git a/mers_lib/src/program/run/loop.rs b/mers_lib/src/program/run/loop.rs deleted file mode 100755 index 9286d43..0000000 --- a/mers_lib/src/program/run/loop.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::data::MersData; - -use super::MersStatement; - -#[derive(Debug)] -pub struct Loop { - pub inner: Box, -} - -impl MersStatement for Loop { - fn run_custom(&self, info: &mut super::Info) -> crate::data::Data { - loop { - if let Some(break_val) = self.inner.run(info).get().matches() { - break break_val; - } - } - } - fn has_scope(&self) -> bool { - true - } -} diff --git a/mers_lib/src/program/run/mod.rs b/mers_lib/src/program/run/mod.rs index 3f53659..97521ae 100755 --- a/mers_lib/src/program/run/mod.rs +++ b/mers_lib/src/program/run/mod.rs @@ -1,4 +1,7 @@ -use std::sync::Arc; +use std::{ + fmt::{Debug, Display}, + sync::{Arc, Mutex}, +}; use crate::{ data::{self, Data, Type}, @@ -16,21 +19,31 @@ pub mod function; #[cfg(feature = "run")] pub mod r#if; #[cfg(feature = "run")] -pub mod r#loop; -#[cfg(feature = "run")] -pub mod switch; -#[cfg(feature = "run")] pub mod tuple; #[cfg(feature = "run")] pub mod value; #[cfg(feature = "run")] pub mod variable; -pub trait MersStatement: std::fmt::Debug { +pub trait MersStatement: Debug + Send + Sync { + fn check_custom( + &self, + info: &mut CheckInfo, + init_to: Option<&Type>, + ) -> Result; fn run_custom(&self, info: &mut Info) -> Data; /// if true, local variables etc. will be contained inside their own scope. fn has_scope(&self) -> bool; - // fn outputs(&self) -> Type; + fn check(&self, info: &mut CheckInfo, assign: Option<&Type>) -> Result { + if self.has_scope() { + info.create_scope(); + } + let o = self.check_custom(info, assign); + if self.has_scope() { + info.end_scope(); + } + o + } fn run(&self, info: &mut Info) -> Data { if self.has_scope() { info.create_scope(); @@ -43,18 +56,54 @@ pub trait MersStatement: std::fmt::Debug { } } +#[derive(Clone, Debug)] +pub struct CheckError(pub String); +impl Display for CheckError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + pub type Info = info::Info; +pub type CheckInfo = info::Info; #[derive(Default, Clone, Debug)] pub struct Local { - vars: Vec, + vars: Vec>>, +} +#[derive(Default, Clone, Debug)] +pub struct CheckLocal { + vars: Vec, } impl info::Local for Local { type VariableIdentifier = usize; - type VariableData = Data; + type VariableData = Arc>; fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { + let nothing = Arc::new(Mutex::new(Data::new(data::bool::Bool(false)))); while self.vars.len() <= id { - self.vars.push(Data::new(data::bool::Bool(false))); + self.vars.push(Arc::clone(¬hing)); + } + self.vars[id] = value; + } + fn get_var(&self, id: &Self::VariableIdentifier) -> Option<&Self::VariableData> { + match self.vars.get(*id) { + Some(v) => Some(v), + None => None, + } + } + fn get_var_mut(&mut self, id: &Self::VariableIdentifier) -> Option<&mut Self::VariableData> { + match self.vars.get_mut(*id) { + Some(v) => Some(v), + None => None, + } + } +} +impl info::Local for CheckLocal { + type VariableIdentifier = usize; + type VariableData = Type; + fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { + while self.vars.len() <= id { + self.vars.push(Type::empty()); } self.vars[id] = value; } diff --git a/mers_lib/src/program/run/switch.rs b/mers_lib/src/program/run/switch.rs deleted file mode 100755 index 4ffc43b..0000000 --- a/mers_lib/src/program/run/switch.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::data::Type; - -use super::MersStatement; - -#[derive(Debug)] -pub struct Switch { - source: Box, - arms: Vec, -} - -#[derive(Debug)] -pub struct SwitchArm { - requires_type: Type, -} - -impl MersStatement for Switch { - fn has_scope(&self) -> bool { - true - } - fn run_custom(&self, info: &mut super::Info) -> crate::data::Data { - todo!("switch") - } -} diff --git a/mers_lib/src/program/run/tuple.rs b/mers_lib/src/program/run/tuple.rs index d4d6eb1..ef5bee1 100755 --- a/mers_lib/src/program/run/tuple.rs +++ b/mers_lib/src/program/run/tuple.rs @@ -1,12 +1,61 @@ -use crate::data::{self, Data}; +use std::sync::Arc; -use super::MersStatement; +use crate::data::{self, tuple::TupleT, Data, Type}; + +use super::{CheckError, MersStatement}; #[derive(Debug)] pub struct Tuple { pub elems: Vec>, } impl MersStatement for Tuple { + fn check_custom( + &self, + info: &mut super::CheckInfo, + init_to: Option<&Type>, + ) -> Result { + let mut it = if let Some(init_to) = init_to { + let mut vec = (0..self.elems.len()) + .map(|_| Type::empty()) + .collect::>(); + for t in init_to.types.iter() { + if let Some(t) = t.as_any().downcast_ref::() { + if t.0.len() == self.elems.len() { + for (i, e) in t.0.iter().enumerate() { + vec[i].add(Arc::new(e.clone())); + } + } else { + return Err(CheckError( + format!("can't init to statement type Tuple with value type {t}, which is part of {init_to} - only tuples with the same length ({}) can be assigned to tuples", self.elems.len()), + )); + } + } else { + return Err(CheckError( + "can't init to statement type Tuple with value type {t}, which is part of {init_to} - only tuples can be assigned to tuples".to_string(), + )); + } + } + Some(vec) + } else { + None + }; + Ok(Type::new(data::tuple::TupleT( + self.elems + .iter() + .map(|v| { + v.check( + info, + if let Some(it) = &mut it { + Some(it.pop().unwrap()) + } else { + None + } + .as_ref(), + ) + }) + .collect::>()?, + ))) + } fn run_custom(&self, info: &mut super::Info) -> crate::data::Data { Data::new(data::tuple::Tuple( self.elems.iter().map(|s| s.run(info)).collect(), diff --git a/mers_lib/src/program/run/value.rs b/mers_lib/src/program/run/value.rs index 2724c81..d4487d7 100755 --- a/mers_lib/src/program/run/value.rs +++ b/mers_lib/src/program/run/value.rs @@ -1,6 +1,6 @@ -use crate::data::{Data, MersData}; +use crate::data::{Data, Type}; -use super::MersStatement; +use super::{CheckError, MersStatement}; #[derive(Debug)] pub struct Value { @@ -11,7 +11,17 @@ impl MersStatement for Value { fn has_scope(&self) -> bool { false } - fn run_custom(&self, info: &mut super::Info) -> Data { + fn check_custom( + &self, + info: &mut super::CheckInfo, + init_to: Option<&Type>, + ) -> Result { + if init_to.is_some() { + return Err(CheckError("can't init to statement type Value".to_string())); + } + Ok(self.val.get().as_type()) + } + fn run_custom(&self, _info: &mut super::Info) -> Data { self.val.clone() } } diff --git a/mers_lib/src/program/run/variable.rs b/mers_lib/src/program/run/variable.rs index 14dc9e9..1546fbe 100755 --- a/mers_lib/src/program/run/variable.rs +++ b/mers_lib/src/program/run/variable.rs @@ -1,9 +1,12 @@ -use crate::data::{self, Data}; +use std::sync::{Arc, Mutex}; + +use crate::data::{self, Data, Type}; use super::MersStatement; #[derive(Debug)] pub struct Variable { + pub is_init: bool, pub is_ref: bool, pub var: (usize, usize), } @@ -12,19 +15,44 @@ impl MersStatement for Variable { fn has_scope(&self) -> bool { false } - fn run_custom(&self, info: &mut super::Info) -> Data { - while info.scopes[self.var.0].vars.len() <= self.var.1 { - info.scopes[self.var.0] - .vars - .push(Data::new(data::bool::Bool(false))); + fn check_custom( + &self, + info: &mut super::CheckInfo, + init_to: Option<&Type>, + ) -> Result { + if self.is_init { + while info.scopes[self.var.0].vars.len() <= self.var.1 { + info.scopes[self.var.0].vars.push(Type::empty()); + } + info.scopes[self.var.0].vars[self.var.1] = init_to + .expect("variable's is_init was true, but check_custom's assign was None? How?") + .clone(); } - if self.is_ref { - Data::new(data::reference::Reference( + Ok(if self.is_ref { + Type::new(data::reference::ReferenceT( info.scopes[self.var.0].vars[self.var.1].clone(), )) } else { - // Full-Clones! - Data::new_boxed(info.scopes[self.var.0].vars[self.var.1].get().clone()) + info.scopes[self.var.0].vars[self.var.1].clone() + }) + } + fn run_custom(&self, info: &mut super::Info) -> Data { + if self.is_init { + let nothing = Arc::new(Mutex::new(Data::new(data::bool::Bool(false)))); + while info.scopes[self.var.0].vars.len() <= self.var.1 { + info.scopes[self.var.0].vars.push(Arc::clone(¬hing)); + } + info.scopes[self.var.0].vars[self.var.1] = nothing; + } + if self.is_ref { + Data::new(data::reference::Reference(Arc::clone( + &info.scopes[self.var.0].vars[self.var.1], + ))) + } else { + info.scopes[self.var.0].vars[self.var.1] + .lock() + .unwrap() + .clone() } } }