From a7bb3e67faf6260ef19537d79d8f6b072afd8eaf Mon Sep 17 00:00:00 2001 From: mark Date: Tue, 9 May 2023 22:11:13 +0200 Subject: [PATCH] libraries can now work threaded if they wish (see http_requests). This means that multiple functions provided by one library can run at the same time (using thread(), see http.mers) and actually do the work they need to do simultaneously. --- http.mers | 17 + mers/src/lib.rs | 12 +- mers/src/libs/comms.rs | 542 +++++++++++++++++++++++++ mers/src/libs/inlib.rs | 246 +++++------ mers/src/libs/mod.rs | 390 ++++++------------ mers/src/main.rs | 5 +- mers/src/parsing/parse.rs | 2 +- mers/src/script/code_runnable.rs | 2 +- mers/src/script/global_info.rs | 4 +- mers/src/script/to_runnable.rs | 92 +++-- mers/src/script/val_type.rs | 105 +++-- mers/tests/lib_comms.rs | 34 ++ mers_libs/http_requests_v1/src/main.rs | 194 +++++---- 13 files changed, 1127 insertions(+), 518 deletions(-) create mode 100644 http.mers create mode 100644 mers/src/libs/comms.rs create mode 100644 mers/tests/lib_comms.rs diff --git a/http.mers b/http.mers new file mode 100644 index 0000000..7c3c78a --- /dev/null +++ b/http.mers @@ -0,0 +1,17 @@ +lib mers_libs/http_requests + +t = thread(() { + // because this downloads for so long, the println() will appear after the other one. + http_get("https:\//raw.githubusercontent.com/dwyl/english-words/master/words.txt").assume_no_enum() + println("got words from word list!") +}) + +sleep(0.5) + +// this will finish before the thread does. +http_get("https:\//github.com/").assume_no_enum() +println("got github start page as html") + +// t.await() + +http_get("not a url").debug() diff --git a/mers/src/lib.rs b/mers/src/lib.rs index b03a815..2597533 100755 --- a/mers/src/lib.rs +++ b/mers/src/lib.rs @@ -5,6 +5,16 @@ mod libs; mod parsing; mod script; -pub use libs::inlib::*; +pub use libs::{ + comms::{ByteData, ByteDataA, Message, RespondableMessage}, + inlib::MyLib, +}; pub use parsing::*; pub use script::{val_data::*, val_type::*}; + +pub mod prelude { + pub use super::{ + script::{val_data::*, val_type::*}, + MyLib, RespondableMessage, + }; +} diff --git a/mers/src/libs/comms.rs b/mers/src/libs/comms.rs new file mode 100644 index 0000000..e322bb2 --- /dev/null +++ b/mers/src/libs/comms.rs @@ -0,0 +1,542 @@ +use crate::script::{ + val_data::{VData, VDataEnum}, + val_type::{VSingleType, VType}, +}; + +/// any message implements this trait. reponding to the message with A generates the response of type B. +pub trait RespondableMessage: MessageResponse { + type With; + type Response: MessageResponse; + fn respond(self, with: Self::With) -> Self::Response; +} + +/// any message or response implements this trait +pub trait MessageResponse: ByteData + ByteDataA { + fn messagetype_id() -> u32; + fn msgtype_id(&self) -> u32 { + Self::messagetype_id() + } +} + +pub trait ByteData: Sized { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read; +} +/// for things like &str, which can't be created easily (String exists for that purpose), but can still be converted to bytes. +pub trait ByteDataA { + fn as_byte_data(&self, vec: &mut Vec); + fn as_byte_data_vec(&self) -> Vec { + let mut vec = Vec::new(); + self.as_byte_data(&mut vec); + vec + } +} + +#[derive(Debug)] +pub enum Message { + RunFunction(run_function::Message), +} +impl ByteData for Message { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + let mut type_id = u32::from_byte_data(data)?; + Ok(match type_id { + 0 => Self::RunFunction(ByteData::from_byte_data(data)?), + other => unreachable!("read unknown type_id byte for message!"), + }) + } +} +impl From for Message { + fn from(value: run_function::Message) -> Self { + Self::RunFunction(value) + } +} + +// implementations for the message/response pairs + +pub mod run_function { + use crate::script::val_data::VData; + + use super::{ByteData, ByteDataA, MessageResponse, RespondableMessage}; + + #[derive(Debug)] + pub struct Message { + pub function_id: u64, + pub args: Vec, + } + pub struct Response { + pub result: VData, + } + impl RespondableMessage for Message { + type With = VData; + type Response = Response; + fn respond(self, with: Self::With) -> Self::Response { + Response { result: with } + } + } + impl MessageResponse for Message { + fn messagetype_id() -> u32 { + 0 + } + } + impl MessageResponse for Response { + fn messagetype_id() -> u32 { + 0 + } + } + impl ByteDataA for Message { + fn as_byte_data(&self, vec: &mut Vec) { + self.function_id.as_byte_data(vec); + self.args.as_byte_data(vec); + } + } + impl ByteData for Message { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + Ok(Self { + function_id: ByteData::from_byte_data(data)?, + args: ByteData::from_byte_data(data)?, + }) + } + } + impl ByteDataA for Response { + fn as_byte_data(&self, vec: &mut Vec) { + self.result.as_byte_data(vec); + } + } + impl ByteData for Response { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + Ok(Self { + result: ByteData::from_byte_data(data)?, + }) + } + } +} + +// implementations of ByteData for other data + +type UsizeConstLen = u32; +type IsizeConstLen = i32; + +impl ByteDataA for usize { + fn as_byte_data(&self, vec: &mut Vec) { + (*self as UsizeConstLen).as_byte_data(vec) + } +} +impl ByteData for usize { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + Ok(UsizeConstLen::from_byte_data(data)? as _) + } +} +impl ByteDataA for isize { + fn as_byte_data(&self, vec: &mut Vec) { + (*self as IsizeConstLen).as_byte_data(vec) + } +} +impl ByteData for isize { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + Ok(IsizeConstLen::from_byte_data(data)? as _) + } +} +impl ByteDataA for i32 { + fn as_byte_data(&self, vec: &mut Vec) { + vec.extend_from_slice(&self.to_be_bytes()) + } +} +impl ByteData for i32 { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + let mut b = [0u8; 4]; + data.read_exact(&mut b)?; + Ok(Self::from_be_bytes(b)) + } +} +impl ByteDataA for u32 { + fn as_byte_data(&self, vec: &mut Vec) { + vec.extend_from_slice(&self.to_be_bytes()) + } +} +impl ByteData for u32 { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + let mut b = [0u8; 4]; + data.read_exact(&mut b)?; + Ok(Self::from_be_bytes(b)) + } +} +impl ByteDataA for i64 { + fn as_byte_data(&self, vec: &mut Vec) { + vec.extend_from_slice(&self.to_be_bytes()) + } +} +impl ByteData for i64 { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + let mut b = [0u8; 8]; + data.read_exact(&mut b)?; + Ok(Self::from_be_bytes(b)) + } +} +impl ByteDataA for u64 { + fn as_byte_data(&self, vec: &mut Vec) { + vec.extend_from_slice(&self.to_be_bytes()) + } +} +impl ByteData for u64 { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + let mut b = [0u8; 8]; + data.read_exact(&mut b)?; + Ok(Self::from_be_bytes(b)) + } +} +impl ByteDataA for u128 { + fn as_byte_data(&self, vec: &mut Vec) { + vec.extend_from_slice(&self.to_be_bytes()) + } +} +impl ByteData for u128 { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + let mut b = [0u8; 16]; + data.read_exact(&mut b)?; + Ok(Self::from_be_bytes(b)) + } +} +impl ByteDataA for i128 { + fn as_byte_data(&self, vec: &mut Vec) { + vec.extend_from_slice(&self.to_be_bytes()) + } +} +impl ByteData for i128 { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + let mut b = [0u8; 16]; + data.read_exact(&mut b)?; + Ok(Self::from_be_bytes(b)) + } +} +impl ByteDataA for f64 { + fn as_byte_data(&self, vec: &mut Vec) { + vec.extend_from_slice(&self.to_be_bytes()); + } +} +impl ByteData for f64 { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + let mut b = [0u8; 8]; + data.read_exact(&mut b)?; + Ok(Self::from_be_bytes(b)) + } +} +impl ByteDataA for String { + fn as_byte_data(&self, vec: &mut Vec) { + self.len().as_byte_data(vec); + vec.extend_from_slice(self.as_bytes()); + } +} +impl ByteData for String { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + let len = ByteData::from_byte_data(data)?; + let mut buf = vec![0; len]; + data.read_exact(buf.as_mut_slice()); + let str = String::from_utf8(buf).unwrap(); + Ok(str) + } +} +impl ByteDataA for &T +where + T: ByteDataA, +{ + fn as_byte_data(&self, vec: &mut Vec) { + (*self).as_byte_data(vec) + } +} +impl ByteDataA for Vec +where + T: ByteDataA, +{ + fn as_byte_data(&self, vec: &mut Vec) { + self.len().as_byte_data(vec); + for elem in self { + elem.as_byte_data(vec); + } + } +} +impl ByteData for Vec +where + T: ByteData, +{ + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + let len = usize::from_byte_data(data)?; + let mut vec = Vec::with_capacity(len); + for _ in 0..len { + vec.push(T::from_byte_data(data)?); + } + Ok(vec) + } +} +impl ByteDataA for (A, B) +where + A: ByteDataA, + B: ByteDataA, +{ + fn as_byte_data(&self, vec: &mut Vec) { + self.0.as_byte_data(vec); + self.1.as_byte_data(vec); + } +} +impl ByteData for (A, B) +where + A: ByteData, + B: ByteData, +{ + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + Ok(( + ByteData::from_byte_data(data)?, + ByteData::from_byte_data(data)?, + )) + } +} +impl ByteDataA for (A, B, C, D, E) +where + A: ByteDataA, + B: ByteDataA, + C: ByteDataA, + D: ByteDataA, + E: ByteDataA, +{ + fn as_byte_data(&self, vec: &mut Vec) { + self.0.as_byte_data(vec); + self.1.as_byte_data(vec); + self.2.as_byte_data(vec); + self.3.as_byte_data(vec); + self.4.as_byte_data(vec); + } +} +impl ByteData for (A, B, C, D, E) +where + A: ByteData, + B: ByteData, + C: ByteData, + D: ByteData, + E: ByteData, +{ + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + Ok(( + ByteData::from_byte_data(data)?, + ByteData::from_byte_data(data)?, + ByteData::from_byte_data(data)?, + ByteData::from_byte_data(data)?, + ByteData::from_byte_data(data)?, + )) + } +} +impl ByteDataA for VType { + fn as_byte_data(&self, vec: &mut Vec) { + self.types.as_byte_data(vec) + } +} +impl ByteData for VType { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + Ok(Self { + types: ByteData::from_byte_data(data)?, + }) + } +} +impl ByteDataA for VSingleType { + fn as_byte_data(&self, vec: &mut Vec) { + match self { + Self::Bool => vec.push(b'b'), + Self::Int => vec.push(b'i'), + Self::Float => vec.push(b'f'), + Self::String => vec.push(b'"'), + Self::Tuple(v) => { + vec.push(b't'); + v.as_byte_data(vec); + } + Self::List(v) => { + vec.push(b'l'); + v.as_byte_data(vec); + } + Self::Function(f) => { + vec.push(b'F'); + f.as_byte_data(vec); + } + Self::Thread(r) => { + vec.push(b'T'); + r.as_byte_data(vec); + } + Self::Reference(r) => { + vec.push(b'R'); + r.as_byte_data(vec); + } + Self::EnumVariant(e, v) => { + vec.push(b'e'); + e.as_byte_data(vec); + v.as_byte_data(vec); + } + Self::EnumVariantS(e, v) => { + vec.push(b'E'); + e.as_byte_data(vec); + v.as_byte_data(vec); + } + Self::CustomType(_) | Self::CustomTypeS(_) => { + unreachable!("CustomType and CustomTypeS cannot be used in libraries [yet?].") + } + } + } +} +impl ByteData for VSingleType { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + let mut switch_byte = [0u8]; + data.read_exact(&mut switch_byte)?; + Ok(match switch_byte[0] { + b'b' => Self::Bool, + b'i' => Self::Int, + b'f' => Self::Float, + b'"' => Self::String, + b't' => Self::Tuple(ByteData::from_byte_data(data)?), + b'l' => Self::List(ByteData::from_byte_data(data)?), + b'F' => Self::Function(ByteData::from_byte_data(data)?), + b'T' => Self::Thread(ByteData::from_byte_data(data)?), + b'R' => Self::Reference(Box::new(ByteData::from_byte_data(data)?)), + b'e' => Self::EnumVariant( + ByteData::from_byte_data(data)?, + ByteData::from_byte_data(data)?, + ), + b'E' => Self::EnumVariantS( + ByteData::from_byte_data(data)?, + ByteData::from_byte_data(data)?, + ), + _ => unreachable!("unexpected byte while reading single type"), + }) + } +} +impl ByteDataA for VData { + fn as_byte_data(&self, vec: &mut Vec) { + self.data.as_byte_data(vec) + } +} +impl ByteData for VData { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + Ok(Self { + data: ByteData::from_byte_data(data)?, + }) + } +} +impl ByteDataA for VDataEnum { + fn as_byte_data(&self, vec: &mut Vec) { + match self { + Self::Bool(false) => vec.push(b'b'), + Self::Bool(true) => vec.push(b'B'), + Self::Int(num) => { + vec.push(b'i'); + num.as_byte_data(vec); + } + Self::Float(num) => { + vec.push(b'f'); + num.as_byte_data(vec); + } + Self::String(s) => { + vec.push(b'"'); + s.as_byte_data(vec); + } + Self::Tuple(c) => { + vec.push(b't'); + c.as_byte_data(vec); + } + Self::List(_, data) => { + vec.push(b'l'); + data.as_byte_data(vec); + } + // TODO? + Self::Function(_) => vec.push(b'F'), + Self::Thread(..) => vec.push(b'T'), + Self::Reference(r) => vec.push(b'R'), + Self::EnumVariant(enum_id, inner) => { + vec.push(b'E'); + enum_id.as_byte_data(vec); + inner.as_byte_data(vec); + } + } + } +} +impl ByteData for VDataEnum { + fn from_byte_data(data: &mut R) -> Result + where + R: std::io::Read, + { + let mut switch_byte = [0u8]; + data.read_exact(&mut switch_byte)?; + Ok(match switch_byte[0] { + b'b' => Self::Bool(false), + b'B' => Self::Bool(true), + b'i' => Self::Int(ByteData::from_byte_data(data)?), + b'f' => Self::Float(ByteData::from_byte_data(data)?), + b'"' => Self::String(ByteData::from_byte_data(data)?), + b't' => Self::Tuple(ByteData::from_byte_data(data)?), + b'l' => { + let entries: Vec = ByteData::from_byte_data(data)?; + Self::List( + entries.iter().fold(VType::empty(), |t, v| t | v.out()), + entries, + ) + } + b'E' => Self::EnumVariant( + ByteData::from_byte_data(data)?, + Box::new(ByteData::from_byte_data(data)?), + ), + _ => unreachable!("read invalid byte"), + }) + } +} diff --git a/mers/src/libs/inlib.rs b/mers/src/libs/inlib.rs index e73aa84..b95c63b 100755 --- a/mers/src/libs/inlib.rs +++ b/mers/src/libs/inlib.rs @@ -1,145 +1,147 @@ use std::{ collections::HashMap, - io::{BufRead, Write}, + io::{BufRead, Stdin, StdinLock, Stdout, StdoutLock, Write}, }; -use crate::{ - libs::DirectReader, - script::{val_data::VData, val_type::VType}, -}; +use crate::script::{val_data::VData, val_type::VType}; -use super::{data_from_bytes, data_to_bytes}; +use super::{ + comms::{self, ByteData, ByteDataA, Message, MessageResponse, RespondableMessage}, + LibInitInfo, LibInitReq, +}; pub struct MyLib { - name: String, - version: (u8, u8), - description: String, - functions: Vec<(String, Vec, VType)>, - enum_variants: HashMap, + // name: String, + version: (u32, u32), + // description: String, + // functions: Vec<(String, Vec, VType)>, + pub callbacks: Callbacks, + enum_variants: Vec<(String, usize)>, + stdin: StdinLock<'static>, + stdin_no_lock: Stdin, } impl MyLib { pub fn new( name: String, - version: (u8, u8), + version: (u32, u32), description: String, - functions: Vec<(String, Vec, VType)>, - ) -> (Self, MyLibTaskCompletion) { - ( - Self { - name, - version, - description, - functions, - enum_variants: HashMap::new(), - }, - MyLibTaskCompletion { _priv: () }, - ) + functions: Vec<(String, Vec<(Vec, VType)>)>, + ) -> Self { + let stdout_no_lock = std::io::stdout(); + let stdin_no_lock = std::io::stdin(); + let mut stdout = stdout_no_lock.lock(); + let mut stdin = stdin_no_lock.lock(); + // comms version + stdout.write(1u128.as_byte_data_vec().as_slice()).unwrap(); + let init_req: LibInitReq = (version.0, version.1, name, description, functions); + stdout + .write(init_req.as_byte_data_vec().as_slice()) + .unwrap(); + stdout.flush(); + let enum_variants = LibInitInfo::from_byte_data(&mut stdin).unwrap(); + Self { + // name: name.clone(), + version, + // description: description.clone(), + // functions: functions.clone(), + callbacks: Callbacks::empty(), + enum_variants, + stdin, + stdin_no_lock, + } } - pub fn get_enum(&self, e: &str) -> Option { - self.enum_variants.get(e).map(|v| *v) + pub fn get_enums(&self) -> &Vec<(String, usize)> { + &self.enum_variants } - pub fn run( - &mut self, - run: MyLibTaskCompletion, - stdin: &mut I, - stdout: &mut O, - ) -> MyLibTask - where - I: BufRead, - O: Write, - { - drop(run); - match match stdin.one_byte().unwrap().into() { - 'i' => { - assert_eq!(stdin.one_byte().unwrap() as char, '\n'); - stdout.write(&[self.version.0, self.version.1]).unwrap(); - writeln!(stdout, "{}", self.name).unwrap(); - stdout - .write(&[self.description.split('\n').count() as _]) - .unwrap(); - writeln!(stdout, "{}", self.description).unwrap(); - for func in self.functions.iter() { - writeln!( - stdout, - "f{}({}) {}", - func.0, - func.1 - .iter() - .enumerate() - .map(|(i, v)| if i == 0 { - format!("{v}") - } else { - format!(" {v}") - }) - .collect::(), - func.2 - ) - .unwrap(); - } - writeln!(stdout, "x").unwrap(); - None + fn get_one_msg(&mut self) -> Result, std::io::Error> { + let id = u128::from_byte_data(&mut self.stdin)?; + let message = Message::from_byte_data(&mut self.stdin)?; + match message { + Message::RunFunction(msg) => self.callbacks.run_function.run(Respondable::new(id, msg)), + }; + Ok(Ok(())) + } + pub fn get_next_unhandled_message(&mut self) -> Result<(), Message> { + loop { + match self.get_one_msg() { + Ok(Ok(())) => {} + // unhandled message. return it to be handeled or included in the error + Ok(Err(msg)) => return Err(msg), + // i/o error, probably because mers exited. return successfully. + Err(e) => return Ok(()), } - 'I' => { - let mut line = String::new(); - loop { - line.clear(); - stdin.read_line(&mut line).unwrap(); - if let Some((task, args)) = line.split_once(' ') { - match task { - "set_enum_id" => { - let (enum_name, enum_id) = args.split_once(' ').unwrap(); - let name = enum_name.trim().to_string(); - let id = enum_id.trim().parse().unwrap(); - self.enum_variants.insert(name.clone(), id); - } - _ => todo!(), - } - } else { - match line.trim_end() { - "init_finished" => break, - _ => unreachable!(), - } - } - } - Some(MyLibTask::FinishedInit(MyLibTaskCompletion { _priv: () })) - } - 'f' => { - let fnid = stdin.one_byte().unwrap() as usize; - Some(MyLibTask::RunFunction(MyLibTaskRunFunction { - function: fnid, - args: self.functions[fnid] - .1 - .iter() - .map(|_| data_from_bytes(stdin).to()) - .collect(), - })) - } - _ => None, - } { - Some(v) => v, - None => MyLibTask::None(MyLibTaskCompletion { _priv: () }), } } } -pub enum MyLibTask { - None(MyLibTaskCompletion), - FinishedInit(MyLibTaskCompletion), - RunFunction(MyLibTaskRunFunction), +pub struct Respondable { + id: u128, + pub msg: M, } -pub struct MyLibTaskRunFunction { - pub function: usize, - pub args: Vec, -} -impl MyLibTaskRunFunction { - pub fn done(self, o: &mut O, returns: VData) -> MyLibTaskCompletion - where - O: Write, - { - data_to_bytes(&returns, o); - MyLibTaskCompletion { _priv: () } +impl Respondable { + fn new(id: u128, msg: M) -> Self { + Self { id, msg } } } -pub struct MyLibTaskCompletion { - _priv: (), +impl Respondable +where + M: RespondableMessage, +{ + pub fn respond(self, with: M::With) { + let mut stdout = std::io::stdout().lock(); + stdout.write(&self.id.as_byte_data_vec()).unwrap(); + stdout + .write(&self.msg.respond(with).as_byte_data_vec()) + .unwrap(); + stdout.flush().unwrap(); + } +} +impl Respondable +where + M: Into, +{ + pub fn to_general(self) -> Respondable { + Respondable::new(self.id, self.msg.into()) + } +} + +pub struct Callbacks { + pub run_function: Callback, +} +impl Callbacks { + pub fn empty() -> Self { + Self { + run_function: Callback::empty(), + } + } +} +pub struct Callback +where + M: super::comms::RespondableMessage, +{ + pub nonconsuming: Vec>, + pub consuming: Option)>>, +} +impl Callback +where + M: super::comms::RespondableMessage, +{ + pub fn empty() -> Self { + Self { + nonconsuming: vec![], + consuming: None, + } + } + /// If the event was handled by a consuming function, returns Ok(r) where r is the returned value from the consuming function. + /// If it wasn't handled (or only handled by nonconsuming functions), Err(m) is returned, giving ownership of the original message back to the caller for further handling. + pub fn run(&mut self, msg: Respondable) -> Result<(), Respondable> { + for f in self.nonconsuming.iter_mut() { + f(&msg.msg); + } + if let Some(f) = self.consuming.as_mut() { + Ok(f(msg)) + } else { + Err(msg) + } + } } diff --git a/mers/src/libs/mod.rs b/mers/src/libs/mod.rs index 810722e..9972cd3 100755 --- a/mers/src/libs/mod.rs +++ b/mers/src/libs/mod.rs @@ -1,3 +1,4 @@ +pub mod comms; pub mod inlib; pub mod path; @@ -10,56 +11,30 @@ use std::{ }; use crate::{ + libs::comms::{ByteData, ByteDataA}, parsing::{file::File, parse}, script::{ + global_info::GlobalScriptInfo, val_data::{VData, VDataEnum}, val_type::VType, }, }; -/* -Libraries are processes that communicate via stdout/stdin. -data in stdout is only expected after it was requested via stdin. ignoring this will likely cause issues. -requests in stdin can be identified via the first byte (ascii char) and end with a \n newline character. -the identifying ascii chars: - i init - reply format: - two bytes, the first for major and the second for minor version number. - the utf8-encoded name of the library followed by a newline - the number of lines in the description (0 for no description) as a byte. (more than 255 lines aren't supported) - a (optionally markdown-formatted [TODO!]) description of the library; all lines (including the last one) must end with a newline - the things you would like to register; one line each unless explicitly stated otherwise; the first byte (char) decides what type to register: - f a function: followed by the function signature, i.e. "my_func(string int/float [string]) string/[float int]" - x end: indicates that you are done registering things - I init 2 - can send some tasks, - must end with a line saying 'init_finished'. - reply should be a single line (only the newline char). If there is data before the newline char, it will be reported as an error and the script will not run. - f call a function: - followed by the function id byte (0 <= id < #funcs; function ids are assigned in ascending order as they were registered in the reply to "i" - and the data for each argument, in order. - reply: the data for the returned value - x exit -sending data: (all ints are encoded so that the most significant data is sent FIRST) - the first byte (ascii char) identifies the type of data: (exceptions listed first: bools) - b false - B true - 1 int - 2 int as string - 5 float - 6 float as string - " string (format: ", 64-bit unsigned int indicating string length, so many bytes utf-8) +use self::comms::{MessageResponse, RespondableMessage}; - -*/ +// Libraries are processes that communicate via stdout/stdin. #[derive(Debug)] pub struct Lib { name: String, process: Child, + current_id: Arc>, stdin: Arc>, - stdout: Arc>>, - pub registered_fns: Vec<(String, Vec, VType)>, + // stdout: Arc>>, + task_sender: Arc< + Mutex) + Send>)>>, + >, + pub registered_fns: Vec<(String, Vec<(Vec, VType)>)>, } impl Drop for Lib { fn drop(&mut self) { @@ -73,6 +48,20 @@ impl Drop for Lib { } } } +/// Sent by the library to request initialization +/// ([ver_major], [ver_minor], [name], [desc], [registered_functions]) +pub type LibInitReq<'a> = ( + u32, + u32, + String, + String, + Vec<(String, Vec<(Vec, VType)>)>, +); +/// Sent by mers to finish initializing a library. +/// [enum variants] +pub type LibInitInfo = Vec<(String, usize)>; +pub type LibInitInfoRef<'a> = Vec<(&'a String, &'a usize)>; + impl Lib { pub fn launch( mut exec: Command, @@ -89,75 +78,84 @@ impl Lib { }; if let (Some(mut stdin), Some(stdout)) = (handle.stdin.take(), handle.stdout.take()) { let mut stdout = BufReader::new(stdout); - writeln!(stdin, "i").unwrap(); - let vernum = { - let mut vernum = [0, 0]; - stdout.read_exact(&mut vernum).unwrap(); - (vernum[0], vernum[1]) - }; - let name = stdout.line().unwrap(); - let name = name.trim(); - eprintln!("- <<< ADDING LIB: {name} v{}.{} >>> -", vernum.0, vernum.1); - let lines_in_desc = stdout.one_byte().unwrap(); - let mut lines_desc = Vec::with_capacity(lines_in_desc as _); - for _ in 0..lines_in_desc { - let line = stdout.line().unwrap(); - let line = line.trim_end_matches('\n'); - eprintln!("| {line}"); - lines_desc.push(line.to_string()); + let comms_version: u128 = ByteData::from_byte_data(&mut stdout).unwrap(); + assert_eq!(comms_version, 1); + let (ver_major, ver_minor, name, description, mut registered_fns) = + LibInitReq::from_byte_data(&mut stdout).unwrap(); + eprintln!("- <<< ADDING LIB: {name} v{ver_major}.{ver_minor} >>> -"); + for line in description.lines() { + eprintln!(" | {line}"); } - let mut registered_fns = vec![]; - loop { - let line = stdout.line().unwrap(); - match line.chars().next() { - Some('f') => { - let (name, args) = line[1..] - .split_once('(') - .expect("function signature didn't include the ( character."); - let mut fn_signature = File::new(args.to_string(), PathBuf::new()); - let mut fn_in = vec![]; - fn_signature.skip_whitespaces(); - if let Some(')') = fn_signature.peek() { - fn_signature.next(); - } else { - loop { - let mut t = match parse::implementation::parse_type_adv( - &mut fn_signature, - true, - ) { - Ok(v) => v, - Err(e) => panic!("{e}"), - }; - t.0.enum_variants(enum_variants); - fn_in.push(t.0); - if t.1 { - break; - } - } - } - let mut fn_out = match parse::implementation::parse_type(&mut fn_signature) - { - Ok(v) => v, - Err(e) => panic!("{e}"), - }; - fn_out.enum_variants(enum_variants); - eprintln!("Registering function \"{name}\" with args \"{}\" and return type \"{fn_out}\"", &fn_in.iter().fold(String::new(), |mut s, v| { s.push_str(format!(" {}", v).as_str()); s }).trim_start_matches(' ')); - registered_fns.push((name.to_string(), fn_in, fn_out)); + for (name, _) in registered_fns.iter() { + eprintln!(" fn {name}"); + } + let mut ginfo = GlobalScriptInfo::default(); + for (_name, func) in registered_fns.iter_mut() { + for (args, out) in func.iter_mut() { + for t in args.iter_mut() { + crate::script::to_runnable::stypes(t, &mut ginfo); } - Some('x') => break, - _ => todo!(), + crate::script::to_runnable::stypes(out, &mut ginfo); } } - write!(stdin, "I").unwrap(); - for (enum_name, enum_id) in enum_variants.iter() { - writeln!(stdin, "set_enum_id {enum_name} {enum_id}").unwrap(); + for (name, id) in ginfo.enum_variants { + if !enum_variants.contains_key(&name) { + enum_variants.insert(name, id); + } } - writeln!(stdin, "init_finished").unwrap(); + let si: LibInitInfoRef = enum_variants.iter().collect(); + stdin.write(si.as_byte_data_vec().as_slice()); + stdin.flush(); + let (task_sender, recv) = std::sync::mpsc::channel::<( + u128, + Box) + Send>, + )>(); + let stdout_reader = std::thread::spawn(move || { + let dur = std::time::Duration::from_millis(20); + let mut pending = HashMap::new(); + loop { + // read id from stdout + if let Ok(id) = u128::from_byte_data(&mut stdout) { + // update pending + loop { + if let Ok((id, sender)) = recv.try_recv() { + pending.insert(id, sender); + } else { + break; + } + } + // find task with that id + if let Some(sender) = pending.remove(&id) { + // call the callback function, which will handle the rest + sender(&mut stdout) + } else { + eprintln!("ID {id} not found! possible decode/encode error?"); + } + std::thread::sleep(dur); + } else { + eprintln!( + "Library has exited, tasks pending: {}", + pending.iter().enumerate().fold( + String::new(), + |mut s, (i, (id, _))| if i == 0 { + format!("{id}") + } else { + s.push_str(format!(", {id}").as_str()); + s + } + ) + ); + break; + } + } + }); Ok(Self { - name: name.to_string(), + name, process: handle, stdin: Arc::new(Mutex::new(stdin)), - stdout: Arc::new(Mutex::new(stdout)), + // stdout: Arc::new(Mutex::new(stdout)), + task_sender: Arc::new(Mutex::new(task_sender)), + current_id: Arc::new(Mutex::new(0)), registered_fns, }) } else { @@ -165,17 +163,45 @@ impl Lib { } } - pub fn run_fn(&self, fnid: usize, args: &Vec) -> VData { - let mut stdin = self.stdin.lock().unwrap(); - let mut stdout = self.stdout.lock().unwrap(); - debug_assert!(args.len() == self.registered_fns[fnid].1.len()); - write!(stdin, "f").unwrap(); - stdin.write(&[fnid as _]).unwrap(); - for (_i, arg) in args.iter().enumerate() { - data_to_bytes(arg, &mut *stdin); - } - let o = data_from_bytes(&mut *stdout).to(); - o + pub fn run_fn(&self, fnid: usize, args: Vec) -> VData { + self.get_response(comms::run_function::Message { + function_id: fnid as _, + args, + }) + .result + } + fn get_response(&self, msg: M) -> M::Response + where + M: RespondableMessage, + ::Response: Send + 'static, + { + let recv = { + let mut id = self.current_id.lock().unwrap(); + let mut stdin = self.stdin.lock().unwrap(); + let (sender, recv) = std::sync::mpsc::sync_channel(2); + self.task_sender + .lock() + .unwrap() + .send(( + *id, + Box::new(move |stdout| { + sender + .send(ByteData::from_byte_data(stdout).unwrap()) + .unwrap(); + }), + )) + .unwrap(); + // id - type_id - message + stdin.write(id.as_byte_data_vec().as_slice()).unwrap(); + stdin + .write(msg.msgtype_id().as_byte_data_vec().as_slice()) + .unwrap(); + stdin.write(msg.as_byte_data_vec().as_slice()).unwrap(); + stdin.flush().unwrap(); + *id = id.wrapping_add(1); + recv + }; + recv.recv().unwrap() } } @@ -192,151 +218,3 @@ impl std::fmt::Display for LaunchError { } } } - -pub trait DirectReader { - fn line(&mut self) -> Result; - fn one_byte(&mut self) -> Result; -} -impl DirectReader for T -where - T: BufRead, -{ - fn line(&mut self) -> Result { - let mut buf = String::new(); - self.read_line(&mut buf)?; - Ok(buf) - } - fn one_byte(&mut self) -> Result { - let mut b = [0]; - self.read(&mut b)?; - Ok(b[0]) - } -} - -pub fn data_to_bytes(data: &VData, stdin: &mut T) -where - T: Write, -{ - match &data.data { - VDataEnum::Bool(false) => write!(stdin, "b").unwrap(), - VDataEnum::Bool(true) => write!(stdin, "B").unwrap(), - VDataEnum::Int(v) => { - let mut v = *v; - let mut b = [0u8; 8]; - for i in (0..8).rev() { - b[i] = (v & 0xFF) as _; - v >>= 8; - } - write!(stdin, "1").unwrap(); - stdin.write(&b).unwrap(); - } - VDataEnum::Float(f) => { - writeln!(stdin, "6{f}").unwrap(); - } - VDataEnum::String(s) => { - write!(stdin, "\"").unwrap(); - stdin.write(&(s.len() as u64).to_be_bytes()).unwrap(); - stdin.write(s.as_bytes()).unwrap(); - } - VDataEnum::Tuple(v) => { - write!(stdin, "t").unwrap(); - for v in v { - write!(stdin, "+").unwrap(); - data_to_bytes(v, stdin); - } - writeln!(stdin).unwrap(); - } - VDataEnum::List(_, v) => { - write!(stdin, "l").unwrap(); - for v in v { - write!(stdin, "+").unwrap(); - data_to_bytes(v, stdin); - } - writeln!(stdin).unwrap(); - } - VDataEnum::Function(..) | VDataEnum::Reference(..) | VDataEnum::Thread(..) => { - panic!("cannot use functions, references or threads in LibFunctions.") - } - VDataEnum::EnumVariant(e, v) => { - stdin - .write( - ['E' as u8] - .into_iter() - .chain((*e as u64).to_be_bytes().into_iter()) - .collect::>() - .as_slice(), - ) - .unwrap(); - data_to_bytes(v.as_ref(), stdin); - } - } - stdin.flush().unwrap(); -} -pub fn data_from_bytes(stdout: &mut T) -> VDataEnum -where - T: BufRead, -{ - let id_byte = stdout.one_byte().unwrap().into(); - match id_byte { - 'b' => VDataEnum::Bool(false), - 'B' => VDataEnum::Bool(true), - '1' => { - let mut num = 0; - for _ in 0..8 { - num <<= 8; - num |= stdout.one_byte().unwrap() as isize; - } - VDataEnum::Int(num) - } - '2' => { - let mut buf = String::new(); - stdout.read_line(&mut buf).unwrap(); - VDataEnum::Int(buf.parse().unwrap()) - } - '5' => { - let mut num = 0; - for _ in 0..8 { - num <<= 8; - num |= stdout.one_byte().unwrap() as u64; - } - VDataEnum::Float(f64::from_bits(num)) - } - '6' => { - let mut buf = String::new(); - stdout.read_line(&mut buf).unwrap(); - VDataEnum::Float(buf.parse().unwrap()) - } - 't' | 'l' => { - let mut v = vec![]; - loop { - if stdout.one_byte().unwrap() == '\n' as u8 { - break if id_byte == 't' { - VDataEnum::Tuple(v) - } else { - VDataEnum::List(VType { types: vec![] }, v) - }; - } - v.push(data_from_bytes(stdout).to()) - } - } - '"' => { - let mut len_bytes = 0u64; - for _ in 0..8 { - len_bytes <<= 8; - len_bytes |= stdout.one_byte().unwrap() as u64; - } - let mut buf = Vec::with_capacity(len_bytes as _); - for _ in 0..len_bytes { - buf.push(stdout.one_byte().unwrap()); - } - VDataEnum::String(String::from_utf8_lossy(&buf).into_owned()) - } - 'E' => { - let mut u = [0u8; 8]; - stdout.read_exact(&mut u).unwrap(); - let u = u64::from_be_bytes(u) as _; - VDataEnum::EnumVariant(u, Box::new(data_from_bytes(stdout).to())) - } - other => todo!("data_from_bytes: found '{other}'."), - } -} diff --git a/mers/src/main.rs b/mers/src/main.rs index 7dff617..b3fd4da 100755 --- a/mers/src/main.rs +++ b/mers/src/main.rs @@ -119,7 +119,10 @@ fn normal_main() { std::process::exit(101); } } else { - parsing::file::File::new(std::fs::read_to_string(&args[0]).unwrap(), args[0].as_str().into()) + parsing::file::File::new( + std::fs::read_to_string(&args[0]).unwrap(), + args[0].as_str().into(), + ) } } }; diff --git a/mers/src/parsing/parse.rs b/mers/src/parsing/parse.rs index 9b944f9..ec32fa4 100755 --- a/mers/src/parsing/parse.rs +++ b/mers/src/parsing/parse.rs @@ -219,7 +219,7 @@ pub fn parse_step_libs_load( let cmd_path = cmd.get_program().to_string_lossy().into_owned(); match libs::Lib::launch(cmd, &mut ginfo.enum_variants) { Ok(lib) => { - for (i, (func, _, _)) in lib.registered_fns.iter().enumerate() { + for (i, (func, _)) in lib.registered_fns.iter().enumerate() { ginfo.lib_fns.insert(func.clone(), (libs.len(), i)); } libs.push(lib); diff --git a/mers/src/script/code_runnable.rs b/mers/src/script/code_runnable.rs index 375a0fb..f8d4d10 100755 --- a/mers/src/script/code_runnable.rs +++ b/mers/src/script/code_runnable.rs @@ -177,7 +177,7 @@ impl RStatementEnum { } Self::BuiltinFunction(v, args) => v.run(args, vars, info), Self::LibFunction(libid, fnid, args, _) => info.libs[*libid] - .run_fn(*fnid, &args.iter().map(|arg| arg.run(vars, info)).collect()), + .run_fn(*fnid, args.iter().map(|arg| arg.run(vars, info)).collect()), Self::Block(b) => b.run(vars, info), Self::If(c, t, e) => { if let VDataEnum::Bool(v) = c.run(vars, info).data { diff --git a/mers/src/script/global_info.rs b/mers/src/script/global_info.rs index a838421..fb15c24 100755 --- a/mers/src/script/global_info.rs +++ b/mers/src/script/global_info.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, sync::Arc}; use crate::libs; -use super::{val_type::VType, builtins}; +use super::{builtins, val_type::VType}; pub type GSInfo = Arc; @@ -44,4 +44,4 @@ impl GlobalScriptInfo { .map(|(i, v)| (v.to_string(), i)) .collect() } -} \ No newline at end of file +} diff --git a/mers/src/script/to_runnable.rs b/mers/src/script/to_runnable.rs index 749b9e8..7d86373 100755 --- a/mers/src/script/to_runnable.rs +++ b/mers/src/script/to_runnable.rs @@ -18,7 +18,8 @@ use super::{ builtins::BuiltinFunction, code_macro::Macro, code_parsed::{SBlock, SFunction, SStatement, SStatementEnum}, - code_runnable::{RBlock, RFunction, RScript, RStatement, RStatementEnum}, global_info::GSInfo, + code_runnable::{RBlock, RFunction, RScript, RStatement, RStatementEnum}, + global_info::GSInfo, }; pub enum ToRunnableError { @@ -29,6 +30,7 @@ pub enum ToRunnableError { CannotDeclareVariableWithDereference(String), CannotDereferenceTypeNTimes(VType, usize, VType), FunctionWrongArgCount(String, usize, usize), + FunctionWrongArgs(Vec, String), InvalidType { expected: VType, found: VType, @@ -56,7 +58,11 @@ impl Display for ToRunnableError { } } impl ToRunnableError { - pub fn fmtgs(&self, f: &mut std::fmt::Formatter, info: Option<&GlobalScriptInfo>) -> std::fmt::Result { + pub fn fmtgs( + &self, + f: &mut std::fmt::Formatter, + info: Option<&GlobalScriptInfo>, + ) -> std::fmt::Result { match self { Self::MainWrongInput => write!( f, @@ -75,6 +81,7 @@ impl ToRunnableError { Ok(()) }, Self::FunctionWrongArgCount(v, a, b) => write!(f, "Tried to call function \"{v}\", which takes {a} arguments, with {b} arguments instead."), + Self::FunctionWrongArgs(args, name) => write!(f, "Wrong args for function \"{name}\":{}", args.iter().map(|v| format!(" {v}")).collect::()), Self::InvalidType { expected, found, @@ -146,7 +153,10 @@ struct LInfo { fns: HashMap>, } -pub fn to_runnable(s: SFunction, mut ginfo: GlobalScriptInfo) -> Result { +pub fn to_runnable( + s: SFunction, + mut ginfo: GlobalScriptInfo, +) -> Result { if s.inputs.len() != 1 || s.inputs[0].0 != "args" { return Err((ToRunnableError::MainWrongInput, ginfo.to_arc())); } @@ -170,10 +180,7 @@ pub fn to_runnable(s: SFunction, mut ginfo: GlobalScriptInfo) -> Result return Err((e, ginfo.to_arc())), }; let ginfo = ginfo.to_arc(); - match RScript::new( - func, - ginfo.clone(), - ) { + match RScript::new(func, ginfo.clone()) { Ok(v) => Ok(v), Err(e) => Err((e, ginfo)), } @@ -207,7 +214,10 @@ fn get_all_functions( vartype.to() } } - out.push((inputs.clone(), block(&s.block, ginfo, linfo.clone())?.out(ginfo))); + out.push(( + inputs.clone(), + block(&s.block, ginfo, linfo.clone())?.out(ginfo), + )); Ok(()) } } @@ -249,7 +259,11 @@ fn function( }) } -fn block(s: &SBlock, ginfo: &mut GlobalScriptInfo, mut linfo: LInfo) -> Result { +fn block( + s: &SBlock, + ginfo: &mut GlobalScriptInfo, + mut linfo: LInfo, +) -> Result { let mut statements = Vec::new(); for st in &s.statements { statements.push(statement(st, ginfo, &mut linfo)?); @@ -257,13 +271,13 @@ fn block(s: &SBlock, ginfo: &mut GlobalScriptInfo, mut linfo: LInfo) -> Result Result<(), ToRunnableError> { +pub fn stypes(t: &mut VType, ginfo: &mut GlobalScriptInfo) -> Result<(), ToRunnableError> { for t in &mut t.types { stype(t, ginfo)?; } Ok(()) } -fn stype(t: &mut VSingleType, ginfo: &mut GlobalScriptInfo) -> Result<(), ToRunnableError> { +pub fn stype(t: &mut VSingleType, ginfo: &mut GlobalScriptInfo) -> Result<(), ToRunnableError> { match t { VSingleType::Bool | VSingleType::Int | VSingleType::Float | VSingleType::String => (), VSingleType::Tuple(v) => { @@ -307,7 +321,7 @@ fn stype(t: &mut VSingleType, ginfo: &mut GlobalScriptInfo) -> Result<(), ToRunn return Err(ToRunnableError::UnknownType(name.to_owned())); }) } - VSingleType::CustomType(_) => () + VSingleType::CustomType(_) => (), } Ok(()) } @@ -342,28 +356,32 @@ fn statement( for arg in args.iter() { rargs.push(statement(arg, ginfo, linfo)?); } - if let Some(func) = linfo.fns.get(v) { - if rargs.len() != func.inputs.len() { - return Err(ToRunnableError::FunctionWrongArgCount( - v.clone(), - func.inputs.len(), - rargs.len(), - )); - } - for (i, rarg) in rargs.iter().enumerate() { - let rarg = rarg.out(ginfo); - let out = rarg.fits_in(&func.input_types[i], ginfo); - if !out.is_empty() { - return Err(ToRunnableError::InvalidType { - expected: func.input_types[i].clone(), - found: rarg, - problematic: VType { types: out }, - }); + fn check_fn_args(args: &Vec, inputs: &Vec<(Vec, VType)>, ginfo: &GlobalScriptInfo) -> Option { + let mut fit_any = false; + let mut out = VType::empty(); + for (inputs, output) in inputs { + if args.len() == inputs.len() && args.iter().zip(inputs.iter()).all(|(arg, input)| arg.fits_in(input, ginfo).is_empty()) { + fit_any = true; + out = out | output; } } - RStatementEnum::FunctionCall(func.clone(), rargs) + if fit_any { + Some(out) + } else { + None + } + } + let arg_types: Vec<_> = rargs.iter().map(|v| v.out(ginfo)).collect(); + if let Some(func) = linfo.fns.get(v) { + if let Some(_out) = check_fn_args(&arg_types, &func.input_output_map.iter().map(|v| (v.0.iter().map(|v| v.clone().to()).collect(), v.1.to_owned())).collect(), ginfo) { + RStatementEnum::FunctionCall(func.clone(), rargs) + } else { + return Err(ToRunnableError::FunctionWrongArgs( + arg_types, + v.to_owned() + )); + } } else { - // TODO: type-checking for builtins if let Some(builtin) = BuiltinFunction::get(v) { let arg_types = rargs.iter().map(|v| v.out(ginfo)).collect(); if builtin.can_take(&arg_types, ginfo) { @@ -374,16 +392,12 @@ fn statement( } else { // LIBRARY FUNCTION? if let Some((libid, fnid)) = ginfo.lib_fns.get(v) { - let (_name, fn_in, fn_out) = &ginfo.libs[*libid].registered_fns[*fnid]; - if fn_in.len() == rargs.len() && fn_in.iter().zip(rargs.iter()).all(|(fn_in, arg)| arg.out(ginfo).fits_in(fn_in, ginfo).is_empty()) { + let lib = &ginfo.libs[*libid]; + let libfn = &lib.registered_fns[*fnid]; + if let Some(fn_out) = check_fn_args(&arg_types, &libfn.1, ginfo) { RStatementEnum::LibFunction(*libid, *fnid, rargs, fn_out.clone()) } else { - // TODO! better error here - return Err(if fn_in.len() == rargs.len() { - ToRunnableError::WrongArgsForLibFunction(v.to_string(), rargs.iter().map(|v| v.out(ginfo)).collect()) - } else { - ToRunnableError::FunctionWrongArgCount(v.to_string(), fn_in.len(), rargs.len()) - }); + return Err(ToRunnableError::WrongArgsForLibFunction(v.to_owned(), arg_types)); } } else { return Err(ToRunnableError::UseOfUndefinedFunction(v.clone())); diff --git a/mers/src/script/val_type.rs b/mers/src/script/val_type.rs index 1061020..72125d1 100755 --- a/mers/src/script/val_type.rs +++ b/mers/src/script/val_type.rs @@ -4,7 +4,7 @@ use std::{ ops::BitOr, }; -use super::global_info::{GlobalScriptInfo, GSInfo, self}; +use super::global_info::{self, GSInfo, GlobalScriptInfo}; #[derive(Clone, Debug, PartialEq, Eq)] pub struct VType { @@ -39,18 +39,28 @@ impl VSingleType { Self::Reference(r) => r.get(i, gsinfo), Self::EnumVariant(_, t) | Self::EnumVariantS(_, t) => t.get(i, gsinfo), Self::CustomType(t) => gsinfo.custom_types[*t].get(i, gsinfo), - &Self::CustomTypeS(_) => unreachable!("CustomTypeS instead of CustomType, compiler bug? [get]"), + &Self::CustomTypeS(_) => { + unreachable!("CustomTypeS instead of CustomType, compiler bug? [get]") + } } } // None => might not always return t, Some(t) => can only return t pub fn get_always(&self, i: usize, info: &GlobalScriptInfo) -> Option { match self { - Self::Bool | Self::Int | Self::Float | Self::String | Self::List(_) | Self::Function(..) | Self::Thread(..) => None, + Self::Bool + | Self::Int + | Self::Float + | Self::String + | Self::List(_) + | Self::Function(..) + | Self::Thread(..) => None, Self::Tuple(t) => t.get(i).cloned(), Self::Reference(r) => r.get_always(i, info), Self::EnumVariant(_, t) | Self::EnumVariantS(_, t) => t.get_always(i, info), Self::CustomType(t) => info.custom_types[*t].get_always(i, info), - Self::CustomTypeS(_) => unreachable!("CustomTypeS instead of CustomType, compiler bug? [get_always]"), + Self::CustomTypeS(_) => { + unreachable!("CustomTypeS instead of CustomType, compiler bug? [get_always]") + } } } } @@ -290,13 +300,23 @@ impl VSingleType { (Self::Reference(_), _) | (_, Self::Reference(_)) => false, (Self::EnumVariant(v1, t1), Self::EnumVariant(v2, t2)) => { *v1 == *v2 && t1.fits_in(&t2, info).is_empty() - }, - (Self::CustomType(a), Self::CustomType(b)) => *a == *b /* || info.custom_types[*a].fits_in(&info.custom_types[*b], info).is_empty() */, - (Self::CustomType(a), b) => info.custom_types[*a].fits_in(&b.clone().to(), info).is_empty(), - (a, Self::CustomType(b)) => a.clone().to().fits_in(&info.custom_types[*b], info).is_empty(), - (Self::CustomTypeS(_), _) | (_, Self::CustomTypeS(_)) => unreachable!("CustomTypeS instead of CustomType - compiler bug?"), + } + (Self::CustomType(a), Self::CustomType(b)) => *a == *b, /* || info.custom_types[*a].fits_in(&info.custom_types[*b], info).is_empty() */ + (Self::CustomType(a), b) => info.custom_types[*a] + .fits_in(&b.clone().to(), info) + .is_empty(), + (a, Self::CustomType(b)) => a + .clone() + .to() + .fits_in(&info.custom_types[*b], info) + .is_empty(), + (Self::CustomTypeS(_), _) | (_, Self::CustomTypeS(_)) => { + unreachable!("CustomTypeS instead of CustomType - compiler bug?") + } (Self::EnumVariant(..), _) | (_, Self::EnumVariant(..)) => false, - (Self::EnumVariantS(..), _) | (_, Self::EnumVariantS(..)) => unreachable!("EnumVariantS instead of EnumVariant - compiler bug?"), + (Self::EnumVariantS(..), _) | (_, Self::EnumVariantS(..)) => { + unreachable!("EnumVariantS instead of EnumVariant - compiler bug?") + } (Self::Bool, Self::Bool) | (Self::Int, Self::Int) | (Self::Float, Self::Float) @@ -304,7 +324,9 @@ impl VSingleType { (Self::Bool | Self::Int | Self::Float | Self::String, _) => false, (Self::Tuple(a), Self::Tuple(b)) => { if a.len() == b.len() { - a.iter().zip(b.iter()).all(|(a, b)| a.fits_in(b, info).is_empty()) + a.iter() + .zip(b.iter()) + .all(|(a, b)| a.fits_in(b, info).is_empty()) } else { false } @@ -338,7 +360,15 @@ impl VSingleType { } pub fn fits_in_type(&self, rhs: &VType, info: &GlobalScriptInfo) -> bool { match self { - Self::CustomType(t) => rhs.types.iter().any(|rhs| if let Self::CustomType(rhs) = rhs { *t == *rhs } else { false }) || info.custom_types[*t].fits_in(rhs, info).is_empty(), + Self::CustomType(t) => { + rhs.types.iter().any(|rhs| { + if let Self::CustomType(rhs) = rhs { + *t == *rhs + } else { + false + } + }) || info.custom_types[*t].fits_in(rhs, info).is_empty() + } _ => rhs.types.iter().any(|t| self.fits_in(t, info)), } } @@ -410,9 +440,13 @@ impl VSingleType { } Self::EnumVariant(variant, inner) => { if let Some(name) = if let Some(info) = info { - info.enum_variants - .iter() - .find_map(|(name, id)| if id == variant { Some(name) } else { None }) + info.enum_variants.iter().find_map(|(name, id)| { + if id == variant { + Some(name) + } else { + None + } + }) } else { None } { @@ -428,14 +462,39 @@ impl VSingleType { inner.fmtgs(f, info)?; write!(f, ")") } - Self::CustomType(t) => if let Some(info) = info { - #[cfg(not(debug_assertions))] - write!(f, "{}", info.custom_type_names.iter().find_map(|(name, id)| if *t == *id { Some(name.to_owned()) } else { None }).unwrap())?; - #[cfg(debug_assertions)] - write!(f, "{}/*{}*/", info.custom_type_names.iter().find_map(|(name, id)| if *t == *id { Some(name.to_owned()) } else { None }).unwrap(), &info.custom_types[*t])?; - Ok(()) - } else { - write!(f, "[custom type #{t}]") + Self::CustomType(t) => { + if let Some(info) = info { + #[cfg(not(debug_assertions))] + write!( + f, + "{}", + info.custom_type_names + .iter() + .find_map(|(name, id)| if *t == *id { + Some(name.to_owned()) + } else { + None + }) + .unwrap() + )?; + #[cfg(debug_assertions)] + write!( + f, + "{}/*{}*/", + info.custom_type_names + .iter() + .find_map(|(name, id)| if *t == *id { + Some(name.to_owned()) + } else { + None + }) + .unwrap(), + &info.custom_types[*t] + )?; + Ok(()) + } else { + write!(f, "[custom type #{t}]") + } } Self::CustomTypeS(t) => write!(f, "{t}"), } diff --git a/mers/tests/lib_comms.rs b/mers/tests/lib_comms.rs new file mode 100644 index 0000000..d2f1fe3 --- /dev/null +++ b/mers/tests/lib_comms.rs @@ -0,0 +1,34 @@ +use std::io::Cursor; + +use mers_libs::prelude::*; +use mers_libs::{ByteData, ByteDataA}; + +#[test] +fn list_type() { + let a: Vec = vec![14, 26]; + let bytes = a.as_byte_data_vec(); + println!("{bytes:?}"); + assert_eq!( + Vec::::from_byte_data(&mut Cursor::new(bytes)).unwrap(), + a + ); + + let a = VSingleType::List(VSingleType::Int.to()).to(); + assert_eq!( + VType::from_byte_data(&mut Cursor::new(a.as_byte_data_vec())).unwrap(), + a + ); + + let a = VSingleType::Tuple(vec![ + VType { + types: vec![VSingleType::Tuple(vec![]), VSingleType::Int], + }, + VSingleType::String.to(), + VSingleType::EnumVariant(12, VSingleType::Float.to()).to(), + ]) + .to(); + assert_eq!( + VType::from_byte_data(&mut Cursor::new(a.as_byte_data_vec())).unwrap(), + a + ); +} diff --git a/mers_libs/http_requests_v1/src/main.rs b/mers_libs/http_requests_v1/src/main.rs index 0705ace..d70c2df 100755 --- a/mers_libs/http_requests_v1/src/main.rs +++ b/mers_libs/http_requests_v1/src/main.rs @@ -1,89 +1,139 @@ -use mers_libs::{MyLib, MyLibTask, VDataEnum, VSingleType, VType}; +use mers_libs::prelude::*; fn main() { - let (mut my_lib, mut run) = MyLib::new( - "HTTP requests for MERS".to_string(), + let mut my_lib = MyLib::new( + "http".to_string(), // "HTTP requests for MERS".to_string(), (0, 0), - "basic HTTP functionality for mers. warning: this is fully single-threaded.".to_string(), - vec![( - "http_get".to_string(), - vec![VSingleType::String.to()], - VType { - types: vec![ - VSingleType::String, - VSingleType::EnumVariantS( - format!("Err"), + "desc".to_string(), // "basic HTTP functionality for mers. warning: this is fully single-threaded.".to_string(), + vec![ + // http_get + ( + "http_get".to_string(), + vec![ + // (String) -> String/Err(ErrBuildingRequest(String)/ErrGettingResponseText(String)) + ( + vec![VSingleType::String.to()], VType { types: vec![ + VSingleType::String, VSingleType::EnumVariantS( - format!("ErrBuildingRequest"), - VSingleType::String.to(), - ), - VSingleType::EnumVariantS( - format!("ErrGettingResponseText"), - VSingleType::String.to(), + format!("Err"), + VType { + types: vec![ + VSingleType::EnumVariantS( + format!("ErrBuildingRequest"), + VSingleType::String.to(), + ), + VSingleType::EnumVariantS( + format!("ErrGettingResponseText"), + VSingleType::String.to(), + ), + ], + }, ), ], }, ), ], - }, - )], + ), + ], ); - let mut stdin = std::io::stdin().lock(); - let mut stdout = std::io::stdout().lock(); - let mut err_general = 0; - let mut err_building_request = 0; - let mut err_getting_response_text = 0; - loop { - run = match my_lib.run(run, &mut stdin, &mut stdout) { - MyLibTask::None(v) => v, - MyLibTask::FinishedInit(v) => { - err_general = my_lib.get_enum("Err").unwrap(); - err_building_request = my_lib.get_enum("ErrBuildingRequest").unwrap(); - err_getting_response_text = my_lib.get_enum("ErrGettingResponseText").unwrap(); - v - } - MyLibTask::RunFunction(f) => { - let return_value = match f.function { - 0 => { - // http_get - if let VDataEnum::String(url) = &f.args[0].data { - match reqwest::blocking::get(url) { - Ok(response) => match response.text() { - Ok(text) => VDataEnum::String(text).to(), - Err(e) => VDataEnum::EnumVariant( - err_general, - Box::new( - VDataEnum::EnumVariant( - err_getting_response_text, - Box::new(VDataEnum::String(e.to_string()).to()), - ) - .to(), - ), - ) - .to(), - }, - Err(e) => VDataEnum::EnumVariant( - err_general, - Box::new( - VDataEnum::EnumVariant( - err_building_request, - Box::new(VDataEnum::String(e.to_string()).to()), - ) - .to(), - ), + let err_general = my_lib + .get_enums() + .iter() + .find(|(name, _)| name.as_str() == "Err") + .unwrap() + .1; + let err_building_request = my_lib + .get_enums() + .iter() + .find(|(name, _)| name.as_str() == "ErrBuildingRequest") + .unwrap() + .1; + let err_getting_response_text = my_lib + .get_enums() + .iter() + .find(|(name, _)| name.as_str() == "ErrGettingResponseText") + .unwrap() + .1; + my_lib.callbacks.run_function.consuming = Some(Box::new(move |msg| { + if let VDataEnum::String(url) = &msg.msg.args[0].data { + let url = url.clone(); + std::thread::spawn(move || { + let r = match reqwest::blocking::get(url) { + Ok(response) => match response.text() { + Ok(text) => VDataEnum::String(text).to(), + Err(e) => VDataEnum::EnumVariant( + err_general, + Box::new( + VDataEnum::EnumVariant( + err_getting_response_text, + Box::new(VDataEnum::String(e.to_string()).to()), ) .to(), - } - } else { - unreachable!() - } - } - _ => unreachable!(), + ), + ) + .to(), + }, + Err(e) => VDataEnum::EnumVariant( + err_general, + Box::new( + VDataEnum::EnumVariant( + err_building_request, + Box::new(VDataEnum::String(e.to_string()).to()), + ) + .to(), + ), + ) + .to(), }; - f.done(&mut stdout, return_value) - } + msg.respond(r) + }); + } else { + unreachable!() } - } + })); + // because we handle all callbacks, this never returns Err(unhandeled message). + // it returns Ok(()) if mers exits (i/o error in stdin/stdout), so we also exit if that happens. + my_lib.get_next_unhandled_message().unwrap(); } +// fn run_function(f: ()) { +// let return_value = match f.function { +// 0 => { +// // http_get +// if let VDataEnum::String(url) = &f.args[0].data { +// match reqwest::blocking::get(url) { +// Ok(response) => match response.text() { +// Ok(text) => VDataEnum::String(text).to(), +// Err(e) => VDataEnum::EnumVariant( +// err_general, +// Box::new( +// VDataEnum::EnumVariant( +// err_getting_response_text, +// Box::new(VDataEnum::String(e.to_string()).to()), +// ) +// .to(), +// ), +// ) +// .to(), +// }, +// Err(e) => VDataEnum::EnumVariant( +// err_general, +// Box::new( +// VDataEnum::EnumVariant( +// err_building_request, +// Box::new(VDataEnum::String(e.to_string()).to()), +// ) +// .to(), +// ), +// ) +// .to(), +// } +// } else { +// unreachable!() +// } +// } +// _ => unreachable!(), +// }; +// f.done(&mut stdout, return_value) +// }