improved the mers library system

added a GUI library wrapping iced-rs, mainly to test the limits of the current library system.

see gui.txt for an example on how to use the gui library. (note: set the MERS_LIB_DIR environment variable to the path of your local mers_libs/ folder (lib paths are relative to MERS_LIB_DIR if they're not specified as absolute))
This commit is contained in:
Dummi26 2023-04-04 23:48:01 +02:00
parent 38d641ffcd
commit 5b051e72f1
16 changed files with 3836 additions and 81 deletions

View File

@ -5,5 +5,9 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "mers"
path = "src/main.rs"
[dependencies] [dependencies]
regex = "1.7.2" regex = "1.7.2"

51
gui.txt Normal file
View File

@ -0,0 +1,51 @@
lib gui.sh
base = gui_init()
column = base.gui_add(Column: [])
text = column.gui_add(Text: "Welcome to MERS GUI!")
button = column.gui_add(Button: "This is a button.")
second_button = column.gui_add(Button: "This is a second button.")
text_state = -2
second_text = column.gui_add(Text: "press the button above to remove this text!")
while {
for event gui_updates() {
switch! event {
ButtonPressed([int]) {
e = event.noenum()
match e {
&e.eq(&button) println("First button pressed")
&e.eq(&second_button) {
// don't match on text_state because we need to change the original from inside the match statement
state = text_state
match state {
// the first, third, fifth, ... time the button is pressed: remove the text
text_state.mod(2).eq(0) {
if text_state.eq(-2) {
// the first time the button is pressed
text_state = 0
set_title("keep pressing the button!")
}
second_text.gui_remove()
}
// the 2nd, 4th, 6th, ... time the button is pressed: add the text back
text_state.eq(1) second_text = column.gui_add(Text: "i'm back!")
text_state.eq(3) second_text = column.gui_add(Text: "you can't fully get rid of me!")
true {
second_text = column.gui_add(Text: "i always come back")
// restart (set text_state to 0)
text_state = -1
}
}
text_state = text_state.add(1)
}
true println("A different button was pressed (unreachable)")
}
}
}
}
[]
}

3
mers_libs/gui.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
cd ./gui_v1
cargo run --release

2921
mers_libs/gui_v1/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
[package]
name = "gui_v1"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
iced = { version = "0.8.0", features = ["smol"] }
mers = { path = "../../" }

View File

@ -0,0 +1,451 @@
use std::{
collections::HashMap,
io::{self, Read},
rc::Rc,
sync::{mpsc, Arc},
time::Duration,
};
use iced::{
executor, time,
widget::{self, button, column, row, text},
Application, Command, Element, Renderer, Settings, Subscription, Theme,
};
use mers::{
libs::inlib::{MyLib, MyLibTask},
script::{
val_data::{VData, VDataEnum},
val_type::{VSingleType, VType},
},
};
/*
Path: Vec<usize>
*/
fn single_gui_element() -> VType {
VType {
types: vec![
VSingleType::EnumVariantS("Row".to_string(), VSingleType::Tuple(vec![]).to()),
VSingleType::EnumVariantS("Column".to_string(), VSingleType::Tuple(vec![]).to()),
VSingleType::EnumVariantS("Text".to_string(), VSingleType::String.to()),
VSingleType::EnumVariantS(
"Button".to_string(),
VType {
types: vec![VSingleType::Tuple(vec![]), VSingleType::String],
},
),
],
}
}
fn single_gui_update() -> VType {
VType {
types: vec![VSingleType::EnumVariantS(
"ButtonPressed".to_string(),
VSingleType::List(VSingleType::Int.to()).to(),
)],
}
}
fn main() {
let (sender, recv) = mpsc::channel();
let (sender2, recv2) = mpsc::channel();
std::thread::spawn(move || {
let recv = recv2;
let (mut my_lib, mut run) = MyLib::new(
"GUI-Iced".to_string(),
(0, 0),
"A basic GUI library for mers.".to_string(),
vec![
(
"gui_init".to_string(),
vec![],
VSingleType::List(VSingleType::Int.to()).to(),
),
(
"gui_updates".to_string(),
vec![],
VSingleType::List(single_gui_update()).to(),
),
(
"set_title".to_string(),
vec![VSingleType::String.to()],
VSingleType::Tuple(vec![]).to(),
),
(
"gui_add".to_string(),
vec![
VSingleType::List(VSingleType::Int.to()).to(),
single_gui_element(),
],
VSingleType::List(VSingleType::Int.to()).to(),
),
(
"gui_remove".to_string(),
vec![VSingleType::List(VSingleType::Int.to()).to()],
VSingleType::Tuple(vec![]).to(),
),
],
);
let mut stdin = std::io::stdin().lock();
let mut stdout = std::io::stdout().lock();
let mut layout = Layout::Row(vec![]);
loop {
run = match my_lib.run(run, &mut stdin, &mut stdout) {
MyLibTask::None(v) => v,
MyLibTask::RunFunction(mut f) => {
let return_value = match f.function {
0 => VDataEnum::List(VSingleType::Int.to(), vec![]).to(),
1 => {
let mut v = vec![];
while let Ok(recv) = recv.try_recv() {
match recv {
MessageAdv::ButtonPressed(path) => v.push(
VDataEnum::EnumVariant(
my_lib.get_enum("ButtonPressed"),
Box::new(
VDataEnum::List(VSingleType::Int.to(), path).to(),
),
)
.to(),
),
}
}
VDataEnum::List(single_gui_update(), v).to()
}
2 => {
// set_title
if let VDataEnum::String(new_title) = f.args.remove(0).data {
sender.send(Task::SetTitle(new_title)).unwrap();
VDataEnum::Tuple(vec![]).to()
} else {
unreachable!()
}
}
3 => {
// gui_add
if let (layout_data, VDataEnum::List(_, path)) =
(f.args.remove(1).data, f.args.remove(0).data)
{
let path: Vec<usize> = path
.into_iter()
.map(|v| {
if let VDataEnum::Int(v) = v.data {
v as _
} else {
unreachable!()
}
})
.collect();
let lo = layout_from_vdata(&my_lib, layout_data);
let layout_inner = layout.get_mut(&path, 0);
let new_path: Vec<_> = path
.iter()
.map(|v| VDataEnum::Int(*v as _).to())
.chain(
[VDataEnum::Int(layout_inner.len() as _).to()].into_iter(),
)
.collect();
layout_inner.add(lo.clone());
sender.send(Task::LAdd(path, lo)).unwrap();
VDataEnum::List(VSingleType::Int.to(), new_path).to()
} else {
unreachable!()
}
}
4 => {
// gui_remove
if let VDataEnum::List(_, path) = f.args.remove(0).data {
let mut path: Vec<usize> = path
.into_iter()
.map(|v| {
if let VDataEnum::Int(v) = v.data {
v as _
} else {
unreachable!()
}
})
.collect();
if let Some(remove_index) = path.pop() {
let layout_inner = layout.get_mut(&path, 0);
layout_inner.remove(remove_index);
path.push(remove_index);
sender.send(Task::LRemove(path)).unwrap();
}
VDataEnum::Tuple(vec![]).to()
} else {
unreachable!()
}
}
_ => unreachable!(),
};
f.done(&mut stdout, return_value)
}
}
}
});
App::run(Settings::with_flags((recv, sender2))).unwrap();
}
fn layout_from_vdata(my_lib: &MyLib, d: VDataEnum) -> Layout {
let row = my_lib.get_enum("Row");
let col = my_lib.get_enum("Column");
let text = my_lib.get_enum("Text");
let button = my_lib.get_enum("Button");
if let VDataEnum::EnumVariant(variant, inner_data) = d {
if variant == row {
Layout::Row(vec![])
} else if variant == col {
Layout::Column(vec![])
} else if variant == text {
Layout::Text(if let VDataEnum::String(s) = inner_data.data {
s
} else {
String::new()
})
} else if variant == button {
Layout::Button(Box::new(Layout::Text(
if let VDataEnum::String(s) = inner_data.data {
s
} else {
String::new()
},
)))
} else {
unreachable!()
}
} else {
unreachable!()
}
}
enum Task {
SetTitle(String),
LAdd(Vec<usize>, Layout),
LSet(Vec<usize>, Layout),
LRemove(Vec<usize>),
}
struct App {
title: String,
recv: mpsc::Receiver<Task>,
sender: mpsc::Sender<MessageAdv>,
buttons: Vec<Vec<usize>>,
layout: Layout,
}
#[derive(Debug, Clone, Copy)]
enum Message {
Tick,
ButtonPressed(usize),
}
enum MessageAdv {
ButtonPressed(Vec<VData>),
}
impl Application for App {
type Executor = executor::Default;
type Message = Message;
type Theme = Theme;
type Flags = (mpsc::Receiver<Task>, mpsc::Sender<MessageAdv>);
fn new(flags: Self::Flags) -> (Self, Command<Self::Message>) {
(
Self {
title: format!("mers gui (using iced)..."),
recv: flags.0,
sender: flags.1,
buttons: vec![],
layout: Layout::Row(vec![]),
},
Command::none(),
)
}
fn title(&self) -> String {
format!("{}", self.title)
}
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
let mut commands = vec![];
match message {
Message::Tick => {
let mut changed_layout = false;
while let Ok(task) = self.recv.try_recv() {
match task {
Task::SetTitle(t) => {
self.title = t;
}
Task::LAdd(path, add) => {
changed_layout = true;
self.layout.get_mut(&path, 0).add(add);
}
Task::LSet(path, add) => {
changed_layout = true;
*self.layout.get_mut(&path, 0) = add;
}
Task::LRemove(mut path) => {
if let Some(last) = path.pop() {
changed_layout = true;
self.layout.get_mut(&path, 0).remove(last);
}
}
}
}
if changed_layout {
self.calc_layout_stats();
}
}
Message::ButtonPressed(bid) => self
.sender
.send(MessageAdv::ButtonPressed(
self.buttons[bid]
.iter()
.map(|v| VDataEnum::Int(*v as _).to())
.collect(),
))
.unwrap(),
}
Command::batch(commands)
}
fn subscription(&self) -> Subscription<Message> {
time::every(Duration::from_millis(10)).map(|_| Message::Tick)
}
fn view(&self) -> Element<'_, Self::Message, Renderer<Self::Theme>> {
self.viewl(&mut vec![], &self.layout, &mut 0)
}
}
impl App {
fn viewl(
&self,
p: &mut Vec<usize>,
l: &Layout,
current_button: &mut usize,
) -> Element<'_, <Self as Application>::Message, Renderer<<Self as Application>::Theme>> {
match l {
Layout::Row(v) => row(v
.iter()
.enumerate()
.map(|(i, v)| {
p.push(i);
let o = self.viewl(p, v, current_button);
p.pop();
o
})
.collect())
.into(),
Layout::Column(v) => column(
v.iter()
.enumerate()
.map(|(i, v)| {
p.push(i);
let o = self.viewl(p, v, current_button);
p.pop();
o
})
.collect(),
)
.into(),
Layout::Text(txt) => text(txt).into(),
Layout::Button(content) => button({
p.push(0);
let o = self.viewl(p, content, current_button);
p.pop();
o
})
.on_press(Message::ButtonPressed({
let o = *current_button;
*current_button = *current_button + 1;
o
}))
.into(),
}
}
fn calc_layout_stats(&mut self) {
Self::calc_layout_stats_rec(&self.layout, &mut vec![], &mut self.buttons)
}
fn calc_layout_stats_rec(
layout: &Layout,
path: &mut Vec<usize>,
buttons: &mut Vec<Vec<usize>>,
) {
match layout {
Layout::Row(v) | Layout::Column(v) => {
for (i, v) in v.iter().enumerate() {
path.push(i);
Self::calc_layout_stats_rec(v, path, buttons);
path.pop();
}
}
Layout::Button(c) => {
buttons.push(path.clone());
path.push(0);
Self::calc_layout_stats_rec(c, path, buttons);
path.pop();
}
Layout::Text(_) => (),
}
}
}
#[derive(Clone, Debug)]
pub enum Layout {
Row(Vec<Self>),
Column(Vec<Self>),
Text(String),
Button(Box<Self>),
}
impl Layout {
pub fn get_mut(&mut self, path: &Vec<usize>, index: usize) -> &mut Self {
if index >= path.len() {
return self;
}
match self {
Self::Row(v) => v[path[index]].get_mut(path, index + 1),
Self::Column(v) => v[path[index]].get_mut(path, index + 1),
Self::Button(c) => c.as_mut().get_mut(path, index + 1),
Self::Text(_) => {
panic!("cannot index this layout type! ({:?})", self)
}
}
}
pub fn add(&mut self, add: Layout) {
match self {
Self::Row(v) | Self::Column(v) => v.push(add),
_ => panic!("cannot add to this layout type! ({:?})", self),
}
}
pub fn remove(&mut self, remove: usize) {
match self {
Self::Row(v) | Self::Column(v) => {
if remove < v.len() {
v.remove(remove);
}
}
_ => panic!("cannot add to this layout type! ({:?})", self),
}
}
pub fn len(&self) -> usize {
match self {
Self::Row(v) | Self::Column(v) => v.len(),
_ => panic!("cannot get len of this layout type! ({:?})", self),
}
}
}
trait DirectRead {
fn nbyte(&mut self) -> Result<u8, io::Error>;
fn nchar(&mut self) -> Result<char, io::Error>;
}
impl<T> DirectRead for T
where
T: Read,
{
fn nbyte(&mut self) -> Result<u8, io::Error> {
let mut b = [0];
self.read(&mut b)?;
Ok(b[0])
}
fn nchar(&mut self) -> Result<char, io::Error> {
Ok(self.nbyte()?.into())
}
}

139
src/libs/inlib.rs Normal file
View File

@ -0,0 +1,139 @@
use std::{
collections::HashMap,
io::{BufRead, Write},
};
use crate::{
libs::DirectReader,
script::{
val_data::{VData, VDataEnum},
val_type::VType,
},
};
use super::{data_from_bytes, data_to_bytes};
pub struct MyLib {
name: String,
version: (u8, u8),
description: String,
functions: Vec<(String, Vec<VType>, VType)>,
enum_variants: HashMap<String, usize>,
}
impl MyLib {
pub fn new(
name: String,
version: (u8, u8),
description: String,
functions: Vec<(String, Vec<VType>, VType)>,
) -> (Self, MyLibTaskCompletion) {
(
Self {
name,
version,
description,
functions,
enum_variants: HashMap::new(),
},
MyLibTaskCompletion { _priv: () },
)
}
pub fn get_enum(&self, e: &str) -> usize {
*self.enum_variants.get(e).unwrap()
}
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
}
'I' => {
let mut line = String::new();
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!(),
}
}
None
}
'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),
RunFunction(MyLibTaskRunFunction),
}
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: () }
}
}
pub struct MyLibTaskCompletion {
_priv: (),
}

View File

@ -1,5 +1,10 @@
pub mod inlib;
pub mod path;
use std::{ use std::{
collections::{HashMap, HashSet},
io::{self, BufRead, BufReader, Read, Write}, io::{self, BufRead, BufReader, Read, Write},
path::PathBuf,
process::{Child, ChildStdin, ChildStdout, Command, Stdio}, process::{Child, ChildStdin, ChildStdout, Command, Stdio},
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
@ -55,7 +60,10 @@ pub struct Lib {
pub registered_fns: Vec<(String, Vec<VType>, VType)>, pub registered_fns: Vec<(String, Vec<VType>, VType)>,
} }
impl Lib { impl Lib {
pub fn launch(mut exec: Command) -> Result<Self, LaunchError> { pub fn launch(
mut exec: Command,
enum_variants: &mut HashMap<String, usize>,
) -> Result<Self, LaunchError> {
let mut handle = match exec let mut handle = match exec
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
@ -92,22 +100,33 @@ impl Lib {
let (name, args) = line[1..] let (name, args) = line[1..]
.split_once('(') .split_once('(')
.expect("function signature didn't include the ( character."); .expect("function signature didn't include the ( character.");
let mut fn_signature = File::new(args.to_string()); let mut fn_signature = File::new(args.to_string(), PathBuf::new());
let mut fn_in = vec![]; let mut fn_in = vec![];
fn_signature.skip_whitespaces();
if let Some(')') = fn_signature.peek() {
fn_signature.next();
} else {
loop { loop {
let t = parse::parse_type_adv(&mut fn_signature, true).unwrap(); let mut t = parse::parse_type_adv(&mut fn_signature, true).unwrap();
t.0.enum_variants(enum_variants);
fn_in.push(t.0); fn_in.push(t.0);
if t.1 { if t.1 {
break; break;
} }
} }
let fn_out = parse::parse_type(&mut fn_signature).unwrap(); }
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 })[1..]); let mut fn_out = parse::parse_type(&mut fn_signature).unwrap();
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)); registered_fns.push((name.to_string(), fn_in, fn_out));
} }
_ => break, Some('x') => break,
_ => todo!(),
} }
} }
for (enum_name, enum_id) in enum_variants.iter() {
writeln!(stdin, "Iset_enum_id {enum_name} {enum_id}").unwrap();
}
Ok(Self { Ok(Self {
process: handle, process: handle,
stdin: Arc::new(Mutex::new(stdin)), stdin: Arc::new(Mutex::new(stdin)),
@ -126,9 +145,9 @@ impl Lib {
write!(stdin, "f").unwrap(); write!(stdin, "f").unwrap();
stdin.write(&[fnid as _]).unwrap(); stdin.write(&[fnid as _]).unwrap();
for (_i, arg) in args.iter().enumerate() { for (_i, arg) in args.iter().enumerate() {
data_to_bytes(arg, &mut stdin); data_to_bytes(arg, &mut *stdin);
} }
let o = data_from_bytes(&mut stdout).to(); let o = data_from_bytes(&mut *stdout).to();
o o
} }
} }
@ -139,7 +158,7 @@ pub enum LaunchError {
CouldNotSpawnProcess(io::Error), CouldNotSpawnProcess(io::Error),
} }
trait DirectReader { pub trait DirectReader {
fn line(&mut self) -> Result<String, io::Error>; fn line(&mut self) -> Result<String, io::Error>;
fn one_byte(&mut self) -> Result<u8, io::Error>; fn one_byte(&mut self) -> Result<u8, io::Error>;
} }
@ -159,31 +178,112 @@ where
} }
} }
fn data_to_bytes(data: &VData, stdin: &mut ChildStdin) { pub fn data_to_bytes<T>(data: &VData, stdin: &mut T)
where
T: Write,
{
match &data.data { match &data.data {
VDataEnum::Bool(false) => write!(stdin, "b").unwrap(), VDataEnum::Bool(false) => write!(stdin, "b").unwrap(),
VDataEnum::Bool(true) => write!(stdin, "B").unwrap(), VDataEnum::Bool(true) => write!(stdin, "B").unwrap(),
VDataEnum::Int(_) => todo!(), VDataEnum::Int(v) => {
VDataEnum::Float(_) => todo!("floats are not yet implemented for LibFunction calls."), 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) => { VDataEnum::String(s) => {
write!(stdin, "\"").unwrap(); write!(stdin, "\"").unwrap();
stdin.write(&(s.len() as u64).to_be_bytes()).unwrap(); stdin.write(&(s.len() as u64).to_be_bytes()).unwrap();
stdin.write(s.as_bytes()).unwrap(); stdin.write(s.as_bytes()).unwrap();
} }
VDataEnum::Tuple(_) => todo!(), VDataEnum::Tuple(v) => {
VDataEnum::List(..) => todo!(), 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(..) => { VDataEnum::Function(..) | VDataEnum::Reference(..) | VDataEnum::Thread(..) => {
panic!("cannot use functions, references or threads in LibFunctions.") panic!("cannot use functions, references or threads in LibFunctions.")
} }
VDataEnum::EnumVariant(..) => todo!(), 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(); stdin.flush().unwrap();
} }
fn data_from_bytes(stdout: &mut BufReader<ChildStdout>) -> VDataEnum { pub fn data_from_bytes<T>(stdout: &mut T) -> VDataEnum
match stdout.one_byte().unwrap().into() { where
T: BufRead,
{
let id_byte = stdout.one_byte().unwrap().into();
match id_byte {
'b' => VDataEnum::Bool(false), 'b' => VDataEnum::Bool(false),
'B' => VDataEnum::Bool(true), 'B' => VDataEnum::Bool(true),
'1' | '2' | '5' | '6' => todo!(), '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 _ {
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; let mut len_bytes = 0u64;
for _ in 0..8 { for _ in 0..8 {
@ -196,6 +296,12 @@ fn data_from_bytes(stdout: &mut BufReader<ChildStdout>) -> VDataEnum {
} }
VDataEnum::String(String::from_utf8_lossy(&buf).into_owned()) VDataEnum::String(String::from_utf8_lossy(&buf).into_owned())
} }
_ => todo!(), '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}'."),
} }
} }

19
src/libs/path.rs Normal file
View File

@ -0,0 +1,19 @@
use std::path::PathBuf;
pub fn path_from_string(path: &str, script_directory: &PathBuf) -> Option<PathBuf> {
let path = PathBuf::from(path);
if path.is_absolute() {
return Some(path);
}
let p = script_directory.join(&path);
if p.exists() {
return Some(p);
}
if let Ok(mers_lib_dir) = std::env::var("MERS_LIB_DIR") {
let p = PathBuf::from(mers_lib_dir).join(&path);
if p.exists() {
return Some(p);
}
}
None
}

View File

@ -1,12 +1,14 @@
use std::time::Instant; use std::time::Instant;
pub(crate) mod libs; pub mod libs;
pub(crate) mod parse; pub mod parse;
pub(crate) mod script; pub mod script;
fn main() { fn main() {
let path = std::env::args().nth(1).unwrap();
let script = parse::parse::parse(&mut parse::file::File::new( let script = parse::parse::parse(&mut parse::file::File::new(
std::fs::read_to_string(std::env::args().nth(1).unwrap()).unwrap(), std::fs::read_to_string(&path).unwrap(),
path.into(),
)) ))
.unwrap(); .unwrap();
println!(" - - - - -"); println!(" - - - - -");

View File

@ -1,9 +1,11 @@
use std::{ use std::{
fmt::Display, fmt::Display,
ops::{Index, Range, RangeFrom, RangeTo}, ops::{Index, Range, RangeFrom, RangeTo},
path::PathBuf,
}; };
pub struct File { pub struct File {
path: PathBuf,
data: String, data: String,
chars: Vec<(usize, char)>, chars: Vec<(usize, char)>,
pos: FilePosition, pos: FilePosition,
@ -25,7 +27,7 @@ impl Display for FilePosition {
} }
impl File { impl File {
pub fn new(data: String) -> Self { pub fn new(data: String, path: PathBuf) -> Self {
let mut chs = data.chars(); let mut chs = data.chars();
let mut data = String::with_capacity(data.len()); let mut data = String::with_capacity(data.len());
loop { loop {
@ -63,6 +65,7 @@ impl File {
} }
let chars = data.char_indices().collect(); let chars = data.char_indices().collect();
Self { Self {
path,
data, data,
chars, chars,
pos: FilePosition { pos: FilePosition {
@ -80,6 +83,9 @@ impl File {
} }
} }
} }
pub fn path(&self) -> &PathBuf {
&self.path
}
pub fn get_pos(&self) -> &FilePosition { pub fn get_pos(&self) -> &FilePosition {
&self.pos &self.pos
} }

View File

@ -1,4 +1,4 @@
use std::{path::PathBuf, process::Command, sync::Arc}; use std::{process::Command, sync::Arc};
use crate::{ use crate::{
libs, libs,
@ -35,17 +35,21 @@ pub enum ParseError {}
pub fn parse(file: &mut File) -> Result<RScript, ScriptError> { pub fn parse(file: &mut File) -> Result<RScript, ScriptError> {
let mut libs = vec![]; let mut libs = vec![];
let mut enum_variants = GInfo::default_enum_variants();
loop { loop {
file.skip_whitespaces(); file.skip_whitespaces();
let pos = file.get_pos().clone(); let pos = file.get_pos().clone();
let line = file.next_line(); let line = file.next_line();
if line.starts_with("lib ") { if line.starts_with("lib ") {
let path_to_executable: PathBuf = line[4..].into(); let path_to_executable = match libs::path::path_from_string(&line[4..], file.path()) {
Some(v) => v,
None => panic!("Couldn't find a path for the library with the path '{}'. Maybe set the MERS_LIB_DIR env variable?", &line[4..]),
};
let mut cmd = Command::new(&path_to_executable); let mut cmd = Command::new(&path_to_executable);
if let Some(parent) = path_to_executable.parent() { if let Some(parent) = path_to_executable.parent() {
cmd.current_dir(parent.clone()); cmd.current_dir(parent.clone());
} }
match libs::Lib::launch(cmd) { match libs::Lib::launch(cmd, &mut enum_variants) {
Ok(lib) => { Ok(lib) => {
libs.push(lib); libs.push(lib);
eprintln!("Loaded library!"); eprintln!("Loaded library!");
@ -72,7 +76,7 @@ pub fn parse(file: &mut File) -> Result<RScript, ScriptError> {
eprintln!("Parsed: {func}"); eprintln!("Parsed: {func}");
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
eprintln!("Parsed: {func:#?}"); eprintln!("Parsed: {func:#?}");
let run = to_runnable::to_runnable(func, GInfo::new(Arc::new(libs)))?; let run = to_runnable::to_runnable(func, GInfo::new(Arc::new(libs), enum_variants))?;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
eprintln!("Runnable: {run:#?}"); eprintln!("Runnable: {run:#?}");
Ok(run) Ok(run)
@ -463,10 +467,14 @@ pub(crate) fn parse_type_adv(
Some('/') => { Some('/') => {
file.next(); file.next();
} }
Some(ch) => { Some(')') => {
closed_fn_args = true;
file.next();
break; break;
} }
_ => break, Some(_) => break,
None => break,
} }
} }
Ok((VType { types }, closed_fn_args)) Ok((VType { types }, closed_fn_args))
@ -506,6 +514,13 @@ fn parse_single_type_adv(
} }
types.push(parse_type(file)?); types.push(parse_type(file)?);
} }
if in_fn_args {
file.skip_whitespaces();
if let Some(')') = file.peek() {
closed_bracket_in_fn_args = true;
file.next();
}
}
if types.len() == 1 { if types.len() == 1 {
VSingleType::List(types.pop().unwrap()) VSingleType::List(types.pop().unwrap())
} else { } else {
@ -529,14 +544,13 @@ fn parse_single_type_adv(
VSingleType::EnumVariantS(name, { VSingleType::EnumVariantS(name, {
let po = parse_type_adv(file, true)?; let po = parse_type_adv(file, true)?;
if !po.1 { if !po.1 {
eprintln!("enum type should be closed by ')', but apparently wasn't?"); // eprintln!("enum type should be closed by ')', but apparently wasn't?");
assert_eq!(file.next(), Some(')')); assert_eq!(file.next(), Some(')'));
} }
po.0 po.0
}) })
}; };
} }
Some(')') if in_fn_args => { Some(')') if in_fn_args => {
closed_bracket_in_fn_args = true; closed_bracket_in_fn_args = true;
break; break;
@ -550,7 +564,10 @@ fn parse_single_type_adv(
"int" => VSingleType::Int, "int" => VSingleType::Int,
"float" => VSingleType::Float, "float" => VSingleType::Float,
"string" => VSingleType::String, "string" => VSingleType::String,
_ => todo!("Err: Invalid type: \"{}\"", name.trim()), _ => {
eprintln!("in_fn_args: {in_fn_args}");
todo!("Err: Invalid type: \"{}\"", name.trim())
}
} }
} }
None => todo!("Err: EOF in type (1)"), None => todo!("Err: EOF in type (1)"),

View File

@ -87,10 +87,6 @@ fn am<T>(i: T) -> Am<T> {
Arc::new(Mutex::new(i)) Arc::new(Mutex::new(i))
} }
pub fn to_runnable(f: SFunction, ginfo: GInfo) -> Result<RScript, ToRunnableError> {
to_runnable::to_runnable(f, ginfo)
}
pub mod to_runnable { pub mod to_runnable {
use std::{ use std::{
collections::HashMap, collections::HashMap,
@ -118,7 +114,6 @@ pub mod to_runnable {
found: VType, found: VType,
problematic: Vec<VSingleType>, problematic: Vec<VSingleType>,
}, },
InvalidTypeForWhileLoop(VType),
CaseForceButTypeNotCovered(VType), CaseForceButTypeNotCovered(VType),
MatchConditionInvalidReturn(VType), MatchConditionInvalidReturn(VType),
NotIndexableFixed(VType, usize), NotIndexableFixed(VType, usize),
@ -145,7 +140,6 @@ pub mod to_runnable {
} => { } => {
write!(f, "Invalid type: Expected {expected:?} but found {found:?}, which includes {problematic:?}, which is not covered.") write!(f, "Invalid type: Expected {expected:?} but found {found:?}, which includes {problematic:?}, which is not covered.")
} }
Self::InvalidTypeForWhileLoop(v) => write!(f, "Invalid type: Expected bool or Tuples of length 0 or 1 as return types for the while loop, but found {v:?} instead."),
Self::CaseForceButTypeNotCovered(v) => write!(f, "Switch! statement, but not all types covered. Types to cover: {v}"), Self::CaseForceButTypeNotCovered(v) => write!(f, "Switch! statement, but not all types covered. Types to cover: {v}"),
Self::MatchConditionInvalidReturn(v) => write!(f, "match statement condition returned {v}, which is not necessarily a tuple of size 0 to 1."), Self::MatchConditionInvalidReturn(v) => write!(f, "match statement condition returned {v}, which is not necessarily a tuple of size 0 to 1."),
Self::NotIndexableFixed(t, i) => write!(f, "Cannot use fixed-index {i} on type {t}."), Self::NotIndexableFixed(t, i) => write!(f, "Cannot use fixed-index {i} on type {t}."),
@ -161,14 +155,17 @@ pub mod to_runnable {
enum_variants: HashMap<String, usize>, enum_variants: HashMap<String, usize>,
} }
impl GInfo { impl GInfo {
pub fn new(libs: Arc<Vec<libs::Lib>>) -> Self { pub fn default_enum_variants() -> HashMap<String, usize> {
builtins::EVS.iter().enumerate().map(|(i, v)| (v.to_string(), i)).collect()
}
pub fn new(libs: Arc<Vec<libs::Lib>>, enum_variants: HashMap<String, usize>) -> Self {
let mut lib_fns = HashMap::new(); let mut lib_fns = HashMap::new();
for (libid, lib) in libs.iter().enumerate() { for (libid, lib) in libs.iter().enumerate() {
for (fnid, (name, ..)) in lib.registered_fns.iter().enumerate() { for (fnid, (name, ..)) in lib.registered_fns.iter().enumerate() {
lib_fns.insert(name.to_string(), (libid, fnid)); lib_fns.insert(name.to_string(), (libid, fnid));
} }
} }
Self { vars: 0, libs, lib_fns, enum_variants: builtins::EVS.iter().enumerate().map(|(i, v)| (v.to_string(), i)).collect() } Self { vars: 0, libs, lib_fns, enum_variants }
} }
} }
// Local, used to keep local variables separated // Local, used to keep local variables separated
@ -182,13 +179,11 @@ pub mod to_runnable {
if s.inputs.len() != 1 || s.inputs[0].0 != "args" { if s.inputs.len() != 1 || s.inputs[0].0 != "args" {
return Err(ToRunnableError::MainWrongInput); return Err(ToRunnableError::MainWrongInput);
} }
if s.inputs[0].1 assert_eq!(s.inputs[0].1,VType {
!= (VType {
types: vec![VSingleType::List(VType { types: vec![VSingleType::List(VType {
types: vec![VSingleType::String], types: vec![VSingleType::String],
})], })],
}) });
{}
let func = function( let func = function(
&s, &s,
&mut ginfo, &mut ginfo,

View File

@ -265,19 +265,7 @@ impl BuiltinFunction {
false false
} }
} }
Self::Eq => { Self::Eq => input.len() == 2,
if input.len() == 2 {
let num = &VType {
types: vec![VSingleType::Int, VSingleType::Float],
};
let string = &VSingleType::String.to();
(input[0].fits_in(num).is_empty() && input[1].fits_in(num).is_empty())
|| (input[0].fits_in(string).is_empty()
&& input[1].fits_in(string).is_empty())
} else {
false
}
}
Self::Add => { Self::Add => {
input.len() == 2 && { input.len() == 2 && {
let num = VType { let num = VType {
@ -1059,20 +1047,7 @@ impl BuiltinFunction {
} }
Self::Eq => { Self::Eq => {
if args.len() == 2 { if args.len() == 2 {
match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { VDataEnum::Bool(args[0].run(vars, libs) == args[1].run(vars, libs)).to()
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a == b).to(),
(VDataEnum::Int(a), VDataEnum::Float(b)) => {
VDataEnum::Bool(a as f64 == b).to()
}
(VDataEnum::Float(a), VDataEnum::Int(b)) => {
VDataEnum::Bool(a == b as f64).to()
}
(VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Bool(a == b).to(),
(VDataEnum::String(a), VDataEnum::String(b)) => {
VDataEnum::Bool(a == b).to()
}
_ => unreachable!("eq: not a number"),
}
} else { } else {
unreachable!("eq: not 2 args") unreachable!("eq: not 2 args")
} }

View File

@ -10,7 +10,7 @@ use super::{
val_type::{VSingleType, VType}, val_type::{VSingleType, VType},
}; };
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub struct VData { pub struct VData {
// parents: Vec<()>, // parents: Vec<()>,
pub data: VDataEnum, pub data: VDataEnum,
@ -29,6 +29,24 @@ pub enum VDataEnum {
Reference(Arc<Mutex<VData>>), Reference(Arc<Mutex<VData>>),
EnumVariant(usize, Box<VData>), EnumVariant(usize, Box<VData>),
} }
impl PartialEq for VDataEnum {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Reference(a), Self::Reference(b)) => *a.lock().unwrap() == *b.lock().unwrap(),
(Self::Reference(a), b) => a.lock().unwrap().data == *b,
(a, Self::Reference(b)) => *a == b.lock().unwrap().data,
(Self::Bool(a), Self::Bool(b)) => *a == *b,
(Self::Int(a), Self::Int(b)) => *a == *b,
(Self::Float(a), Self::Float(b)) => *a == *b,
(Self::String(a), Self::String(b)) => *a == *b,
(Self::Tuple(a), Self::Tuple(b)) | (Self::List(_, a), Self::List(_, b)) => {
a.len() == b.len() && a.iter().zip(b.iter()).all(|(a, b)| a == b)
}
(Self::EnumVariant(a1, a2), Self::EnumVariant(b1, b2)) => *a1 == *b1 && *a2 == *b2,
_ => false,
}
}
}
impl VData { impl VData {
pub fn out(&self) -> VType { pub fn out(&self) -> VType {

View File

@ -1,4 +1,4 @@
use std::{fmt::Debug, ops::BitOr}; use std::{collections::HashMap, fmt::Debug, ops::BitOr};
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct VType { pub struct VType {
@ -87,6 +87,11 @@ impl VType {
} }
out out
} }
pub fn enum_variants(&mut self, enum_variants: &mut HashMap<String, usize>) {
for t in &mut self.types {
t.enum_variants(enum_variants);
}
}
pub fn contains(&self, t: &VSingleType) -> bool { pub fn contains(&self, t: &VSingleType) -> bool {
self.types.contains(t) self.types.contains(t)
} }
@ -154,6 +159,39 @@ impl VSingleType {
v => v.to(), v => v.to(),
} }
} }
pub fn enum_variants(&mut self, enum_variants: &mut HashMap<String, usize>) {
match self {
Self::Bool | Self::Int | Self::Float | Self::String => (),
Self::Tuple(v) => {
for t in v {
t.enum_variants(enum_variants);
}
}
Self::List(t) => t.enum_variants(enum_variants),
Self::Function(f) => {
for f in f {
for t in &mut f.0 {
t.enum_variants(enum_variants);
}
f.1.enum_variants(enum_variants);
}
}
Self::Thread(v) => v.enum_variants(enum_variants),
Self::Reference(v) => v.enum_variants(enum_variants),
Self::EnumVariant(_e, v) => v.enum_variants(enum_variants),
Self::EnumVariantS(e, v) => {
let e = if let Some(e) = enum_variants.get(e) {
*e
} else {
let v = enum_variants.len();
enum_variants.insert(e.clone(), v);
v
};
v.enum_variants(enum_variants);
*self = Self::EnumVariant(e, v.clone());
}
}
}
pub fn fits_in(&self, rhs: &Self) -> bool { pub fn fits_in(&self, rhs: &Self) -> bool {
match (self, rhs) { match (self, rhs) {
(Self::Reference(r), Self::Reference(b)) => r.fits_in(b), (Self::Reference(r), Self::Reference(b)) => r.fits_in(b),