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.

This commit is contained in:
mark 2023-05-09 22:11:13 +02:00
parent 9b92c5b353
commit a7bb3e67fa
13 changed files with 1127 additions and 518 deletions

17
http.mers Normal file
View File

@ -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()

View File

@ -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,
};
}

542
mers/src/libs/comms.rs Normal file
View File

@ -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<R>(data: &mut R) -> Result<Self, std::io::Error>
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<u8>);
fn as_byte_data_vec(&self) -> Vec<u8> {
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<R>(data: &mut R) -> Result<Self, std::io::Error>
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<run_function::Message> 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<VData>,
}
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<u8>) {
self.function_id.as_byte_data(vec);
self.args.as_byte_data(vec);
}
}
impl ByteData for Message {
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
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<u8>) {
self.result.as_byte_data(vec);
}
}
impl ByteData for Response {
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
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<u8>) {
(*self as UsizeConstLen).as_byte_data(vec)
}
}
impl ByteData for usize {
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
where
R: std::io::Read,
{
Ok(UsizeConstLen::from_byte_data(data)? as _)
}
}
impl ByteDataA for isize {
fn as_byte_data(&self, vec: &mut Vec<u8>) {
(*self as IsizeConstLen).as_byte_data(vec)
}
}
impl ByteData for isize {
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
where
R: std::io::Read,
{
Ok(IsizeConstLen::from_byte_data(data)? as _)
}
}
impl ByteDataA for i32 {
fn as_byte_data(&self, vec: &mut Vec<u8>) {
vec.extend_from_slice(&self.to_be_bytes())
}
}
impl ByteData for i32 {
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
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<u8>) {
vec.extend_from_slice(&self.to_be_bytes())
}
}
impl ByteData for u32 {
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
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<u8>) {
vec.extend_from_slice(&self.to_be_bytes())
}
}
impl ByteData for i64 {
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
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<u8>) {
vec.extend_from_slice(&self.to_be_bytes())
}
}
impl ByteData for u64 {
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
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<u8>) {
vec.extend_from_slice(&self.to_be_bytes())
}
}
impl ByteData for u128 {
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
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<u8>) {
vec.extend_from_slice(&self.to_be_bytes())
}
}
impl ByteData for i128 {
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
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<u8>) {
vec.extend_from_slice(&self.to_be_bytes());
}
}
impl ByteData for f64 {
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
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<u8>) {
self.len().as_byte_data(vec);
vec.extend_from_slice(self.as_bytes());
}
}
impl ByteData for String {
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
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<T> ByteDataA for &T
where
T: ByteDataA,
{
fn as_byte_data(&self, vec: &mut Vec<u8>) {
(*self).as_byte_data(vec)
}
}
impl<T> ByteDataA for Vec<T>
where
T: ByteDataA,
{
fn as_byte_data(&self, vec: &mut Vec<u8>) {
self.len().as_byte_data(vec);
for elem in self {
elem.as_byte_data(vec);
}
}
}
impl<T> ByteData for Vec<T>
where
T: ByteData,
{
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
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<A, B> ByteDataA for (A, B)
where
A: ByteDataA,
B: ByteDataA,
{
fn as_byte_data(&self, vec: &mut Vec<u8>) {
self.0.as_byte_data(vec);
self.1.as_byte_data(vec);
}
}
impl<A, B> ByteData for (A, B)
where
A: ByteData,
B: ByteData,
{
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
where
R: std::io::Read,
{
Ok((
ByteData::from_byte_data(data)?,
ByteData::from_byte_data(data)?,
))
}
}
impl<A, B, C, D, E> 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<u8>) {
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<A, B, C, D, E> ByteData for (A, B, C, D, E)
where
A: ByteData,
B: ByteData,
C: ByteData,
D: ByteData,
E: ByteData,
{
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
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<u8>) {
self.types.as_byte_data(vec)
}
}
impl ByteData for VType {
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
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<u8>) {
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<R>(data: &mut R) -> Result<Self, std::io::Error>
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<u8>) {
self.data.as_byte_data(vec)
}
}
impl ByteData for VData {
fn from_byte_data<R>(data: &mut R) -> Result<Self, std::io::Error>
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<u8>) {
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<R>(data: &mut R) -> Result<Self, std::io::Error>
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<VData> = 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"),
})
}
}

View File

@ -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>, VType)>,
enum_variants: HashMap<String, usize>,
// name: String,
version: (u32, u32),
// description: String,
// functions: Vec<(String, Vec<VType>, 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>, VType)>,
) -> (Self, MyLibTaskCompletion) {
(
Self {
name,
version,
description,
functions,
enum_variants: HashMap::new(),
},
MyLibTaskCompletion { _priv: () },
)
functions: Vec<(String, Vec<(Vec<VType>, 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<usize> {
self.enum_variants.get(e).map(|v| *v)
pub fn get_enums(&self) -> &Vec<(String, usize)> {
&self.enum_variants
}
pub fn run<I, O>(
&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::<String>(),
func.2
)
.unwrap();
}
writeln!(stdout, "x").unwrap();
None
fn get_one_msg(&mut self) -> Result<Result<(), Message>, 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<M> {
id: u128,
pub msg: M,
}
pub struct MyLibTaskRunFunction {
pub function: usize,
pub args: Vec<VData>,
}
impl MyLibTaskRunFunction {
pub fn done<O>(self, o: &mut O, returns: VData) -> MyLibTaskCompletion
where
O: Write,
{
data_to_bytes(&returns, o);
MyLibTaskCompletion { _priv: () }
impl<M> Respondable<M> {
fn new(id: u128, msg: M) -> Self {
Self { id, msg }
}
}
pub struct MyLibTaskCompletion {
_priv: (),
impl<M> Respondable<M>
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<M> Respondable<M>
where
M: Into<Message>,
{
pub fn to_general(self) -> Respondable<Message> {
Respondable::new(self.id, self.msg.into())
}
}
pub struct Callbacks {
pub run_function: Callback<comms::run_function::Message>,
}
impl Callbacks {
pub fn empty() -> Self {
Self {
run_function: Callback::empty(),
}
}
}
pub struct Callback<M>
where
M: super::comms::RespondableMessage,
{
pub nonconsuming: Vec<Box<dyn FnMut(&M)>>,
pub consuming: Option<Box<dyn FnMut(Respondable<M>)>>,
}
impl<M> Callback<M>
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<M>) -> Result<(), Respondable<M>> {
for f in self.nonconsuming.iter_mut() {
f(&msg.msg);
}
if let Some(f) = self.consuming.as_mut() {
Ok(f(msg))
} else {
Err(msg)
}
}
}

View File

@ -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<Mutex<u128>>,
stdin: Arc<Mutex<ChildStdin>>,
stdout: Arc<Mutex<BufReader<ChildStdout>>>,
pub registered_fns: Vec<(String, Vec<VType>, VType)>,
// stdout: Arc<Mutex<BufReader<ChildStdout>>>,
task_sender: Arc<
Mutex<std::sync::mpsc::Sender<(u128, Box<dyn FnOnce(&mut BufReader<ChildStdout>) + Send>)>>,
>,
pub registered_fns: Vec<(String, Vec<(Vec<VType>, 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>, 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<dyn FnOnce(&mut BufReader<ChildStdout>) + 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>) -> 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>) -> VData {
self.get_response(comms::run_function::Message {
function_id: fnid as _,
args,
})
.result
}
fn get_response<M>(&self, msg: M) -> M::Response
where
M: RespondableMessage,
<M as comms::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<String, io::Error>;
fn one_byte(&mut self) -> Result<u8, io::Error>;
}
impl<T> DirectReader for T
where
T: BufRead,
{
fn line(&mut self) -> Result<String, io::Error> {
let mut buf = String::new();
self.read_line(&mut buf)?;
Ok(buf)
}
fn one_byte(&mut self) -> Result<u8, io::Error> {
let mut b = [0];
self.read(&mut b)?;
Ok(b[0])
}
}
pub fn data_to_bytes<T>(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::<Vec<u8>>()
.as_slice(),
)
.unwrap();
data_to_bytes(v.as_ref(), stdin);
}
}
stdin.flush().unwrap();
}
pub fn data_from_bytes<T>(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}'."),
}
}

View File

@ -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(),
)
}
}
};

View File

@ -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);

View File

@ -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 {

View File

@ -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<GlobalScriptInfo>;
@ -44,4 +44,4 @@ impl GlobalScriptInfo {
.map(|(i, v)| (v.to_string(), i))
.collect()
}
}
}

View File

@ -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<VType>, 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::<String>()),
Self::InvalidType {
expected,
found,
@ -146,7 +153,10 @@ struct LInfo {
fns: HashMap<String, Arc<RFunction>>,
}
pub fn to_runnable(s: SFunction, mut ginfo: GlobalScriptInfo) -> Result<RScript, (ToRunnableError, GSInfo)> {
pub fn to_runnable(
s: SFunction,
mut ginfo: GlobalScriptInfo,
) -> Result<RScript, (ToRunnableError, GSInfo)> {
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<RScript,
Err(e) => 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<RBlock, ToRunnableError> {
fn block(
s: &SBlock,
ginfo: &mut GlobalScriptInfo,
mut linfo: LInfo,
) -> Result<RBlock, ToRunnableError> {
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<R
Ok(RBlock { statements })
}
fn stypes(t: &mut VType, ginfo: &mut GlobalScriptInfo) -> 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<VType>, inputs: &Vec<(Vec<VType>, VType)>, ginfo: &GlobalScriptInfo) -> Option<VType> {
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()));

View File

@ -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<VType> {
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}"),
}

34
mers/tests/lib_comms.rs Normal file
View File

@ -0,0 +1,34 @@
use std::io::Cursor;
use mers_libs::prelude::*;
use mers_libs::{ByteData, ByteDataA};
#[test]
fn list_type() {
let a: Vec<i32> = vec![14, 26];
let bytes = a.as_byte_data_vec();
println!("{bytes:?}");
assert_eq!(
Vec::<i32>::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
);
}

View File

@ -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)
// }