diff --git a/src/libs/mod.rs b/src/libs/mod.rs index f4a6baf..e29b260 100644 --- a/src/libs/mod.rs +++ b/src/libs/mod.rs @@ -1,36 +1,200 @@ use std::{ - io, - process::{Child, ChildStdin, ChildStdout, Command}, + io::{self, BufRead, BufReader, Read, Write}, + process::{Child, ChildStdin, ChildStdout, Command, Stdio}, + sync::{Arc, Mutex}, }; +use crate::{ + parse::{file::File, parse}, + script::{ + 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 + TODO! (currently nothing) + 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) + + +*/ + +#[derive(Debug)] pub struct Lib { process: Child, - stdin: ChildStdin, - stdout: ChildStdout, + stdin: Arc>, + stdout: Arc>>, + pub registered_fns: Vec<(String, Vec, VType)>, } impl Lib { pub fn launch(mut exec: Command) -> Result { - let mut handle = match exec.spawn() { + let mut handle = match exec + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .spawn() + { Ok(v) => v, Err(e) => return Err(LaunchError::CouldNotSpawnProcess(e)), }; - if let (Some(stdin), Some(stdout), stderr) = ( - handle.stdin.take(), - handle.stdout.take(), - handle.stderr.take(), - ) { + 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 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()); + let mut fn_in = vec![]; + loop { + let t = parse::parse_type_adv(&mut fn_signature, true).unwrap(); + fn_in.push(t.0); + if t.1 { + 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..]); + registered_fns.push((name.to_string(), fn_in, fn_out)); + } + _ => break, + } + } Ok(Self { process: handle, - stdin, - stdout, + stdin: Arc::new(Mutex::new(stdin)), + stdout: Arc::new(Mutex::new(stdout)), + registered_fns, }) } else { return Err(LaunchError::NoStdio); } } + + 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 + } } +#[derive(Debug)] pub enum LaunchError { NoStdio, CouldNotSpawnProcess(io::Error), } + +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]) + } +} + +fn data_to_bytes(data: &VData, stdin: &mut ChildStdin) { + match &data.data { + VDataEnum::Bool(false) => write!(stdin, "b").unwrap(), + VDataEnum::Bool(true) => write!(stdin, "B").unwrap(), + VDataEnum::Int(_) => todo!(), + VDataEnum::Float(_) => todo!("floats are not yet implemented for LibFunction calls."), + VDataEnum::String(s) => { + write!(stdin, "\"").unwrap(); + stdin.write(&(s.len() as u64).to_be_bytes()).unwrap(); + stdin.write(s.as_bytes()).unwrap(); + } + VDataEnum::Tuple(_) => todo!(), + VDataEnum::List(..) => todo!(), + VDataEnum::Function(..) | VDataEnum::Reference(..) | VDataEnum::Thread(..) => { + panic!("cannot use functions, references or threads in LibFunctions.") + } + } + stdin.flush().unwrap(); +} +fn data_from_bytes(stdout: &mut BufReader) -> VDataEnum { + match stdout.one_byte().unwrap().into() { + 'b' => VDataEnum::Bool(false), + 'B' => VDataEnum::Bool(true), + '1' | '2' | '5' | '6' => todo!(), + '"' => { + 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()) + } + _ => todo!(), + } +} diff --git a/src/parse/file.rs b/src/parse/file.rs index 5b13ee0..b4d680e 100644 --- a/src/parse/file.rs +++ b/src/parse/file.rs @@ -8,6 +8,7 @@ pub struct File { chars: Vec<(usize, char)>, pos: FilePosition, } +#[derive(Clone, Copy)] pub struct FilePosition { current_char_index: usize, current_line: usize, @@ -100,6 +101,17 @@ impl File { None => None, } } + pub fn next_line(&mut self) -> String { + let mut o = String::new(); + for ch in self { + if ch == '\n' { + break; + } else { + o.push(ch); + } + } + o + } pub fn peek(&self) -> Option { match self.chars.get(self.pos.current_char_index) { Some((_, c)) => Some(*c), diff --git a/src/parse/parse.rs b/src/parse/parse.rs index a0cc0e5..80b70d2 100644 --- a/src/parse/parse.rs +++ b/src/parse/parse.rs @@ -1,10 +1,16 @@ -use crate::script::{ - block::{ - to_runnable, to_runnable::ToRunnableError, RScript, SBlock, SFunction, SStatement, - SStatementEnum, +use std::{path::PathBuf, process::Command, sync::Arc}; + +use crate::{ + libs, + script::{ + block::{ + to_runnable::ToRunnableError, + to_runnable::{self, GInfo}, + RScript, SBlock, SFunction, SStatement, SStatementEnum, + }, + val_data::VDataEnum, + val_type::{VSingleType, VType}, }, - val_data::VDataEnum, - val_type::{VSingleType, VType}, }; use super::file::File; @@ -28,6 +34,32 @@ impl From for ScriptError { pub enum ParseError {} pub fn parse(file: &mut File) -> Result { + let mut libs = vec![]; + loop { + file.skip_whitespaces(); + let pos = file.get_pos().clone(); + let line = file.next_line(); + if line.starts_with("lib ") { + let path_to_executable: PathBuf = line[4..].into(); + let mut cmd = Command::new(&path_to_executable); + if let Some(parent) = path_to_executable.parent() { + cmd.current_dir(parent.clone()); + } + match libs::Lib::launch(cmd) { + Ok(lib) => { + libs.push(lib); + eprintln!("Loaded library!"); + } + Err(e) => panic!( + "Unable to load library at {}: {e:?}", + path_to_executable.to_string_lossy().as_ref(), + ), + } + } else { + file.set_pos(pos); + break; + } + } let func = SFunction::new( vec![( "args".to_string(), @@ -36,10 +68,11 @@ pub fn parse(file: &mut File) -> Result { parse_block_advanced(file, Some(false), true, true, false)?, ); eprintln!(); + #[cfg(debug_assertions)] eprintln!("Parsed: {func}"); #[cfg(debug_assertions)] eprintln!("Parsed: {func:#?}"); - let run = to_runnable(func)?; + let run = to_runnable::to_runnable(func, GInfo::new(Arc::new(libs)))?; #[cfg(debug_assertions)] eprintln!("Runnable: {run:#?}"); Ok(run) @@ -400,13 +433,16 @@ fn parse_function(file: &mut File) -> Result { Ok(SFunction::new(args, parse_block(file)?)) } -fn parse_type(file: &mut File) -> Result { +pub(crate) fn parse_type(file: &mut File) -> Result { match parse_type_adv(file, false) { Ok((v, _)) => Ok(v), Err(e) => Err(e), } } -fn parse_type_adv(file: &mut File, in_fn_args: bool) -> Result<(VType, bool), ParseError> { +pub(crate) fn parse_type_adv( + file: &mut File, + in_fn_args: bool, +) -> Result<(VType, bool), ParseError> { let mut types = vec![]; let mut closed_fn_args = false; loop { diff --git a/src/script/block.rs b/src/script/block.rs index 533c469..da54dd5 100644 --- a/src/script/block.rs +++ b/src/script/block.rs @@ -6,7 +6,9 @@ use std::{ sync::{Arc, Mutex}, }; -use self::to_runnable::ToRunnableError; +use crate::libs; + +use self::to_runnable::{ToRunnableError, GInfo}; use super::{ builtins::BuiltinFunction, @@ -84,8 +86,8 @@ fn am(i: T) -> Am { Arc::new(Mutex::new(i)) } -pub fn to_runnable(f: SFunction) -> Result { - to_runnable::to_runnable(f) +pub fn to_runnable(f: SFunction, ginfo: GInfo) -> Result { + to_runnable::to_runnable(f, ginfo) } pub mod to_runnable { @@ -95,10 +97,10 @@ pub mod to_runnable { sync::Arc, }; - use crate::script::{ + use crate::{script::{ val_data::VDataEnum, val_type::{VSingleType, VType}, - }; + }, libs}; use super::{ BuiltinFunction, RBlock, RFunction, RScript, RStatement, RStatementEnum, SBlock, SFunction, @@ -151,8 +153,21 @@ pub mod to_runnable { } // Global, shared between all - struct GInfo { + pub struct GInfo { vars: usize, + libs: Arc>, + lib_fns: HashMap, + } + impl GInfo { + pub fn new(libs: Arc>) -> Self { + let mut lib_fns = HashMap::new(); + for (libid, lib) in libs.iter().enumerate() { + for (fnid, (name, ..)) in lib.registered_fns.iter().enumerate() { + lib_fns.insert(name.to_string(), (libid, fnid)); + } + } + Self { vars: 0, libs, lib_fns, } + } } // Local, used to keep local variables separated #[derive(Clone)] @@ -161,7 +176,7 @@ pub mod to_runnable { fns: HashMap>, } - pub fn to_runnable(s: SFunction) -> Result { + pub fn to_runnable(s: SFunction, mut ginfo: GInfo) -> Result { if s.inputs.len() != 1 || s.inputs[0].0 != "args" { return Err(ToRunnableError::MainWrongInput); } @@ -172,7 +187,6 @@ pub mod to_runnable { })], }) {} - let mut ginfo = GInfo { vars: 0 }; let func = function( &s, &mut ginfo, @@ -181,7 +195,7 @@ pub mod to_runnable { fns: HashMap::new(), }, )?; - Ok(RScript::new(func, ginfo.vars)?) + Ok(RScript::new(func, ginfo.vars, ginfo.libs)?) } // go over every possible known-type input for the given function, returning all possible RFunctions. @@ -313,7 +327,22 @@ pub mod to_runnable { todo!("ERR: Builtin function \"{v}\" with wrong args - this isn't a proper error yet, sorry."); } } 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().fits_in(fn_in).is_empty()) { + RStatementEnum::LibFunction(*libid, *fnid, rargs, fn_out.clone()) + } else { + // TODO! better error here + return Err(if fn_in.len() == rargs.len() { + todo!("Err: Wrong args for LibFunction \"{v}\"."); + } else { + ToRunnableError::FunctionWrongArgCount(v.to_string(), fn_in.len(), rargs.len()) + }); + } + } else { return Err(ToRunnableError::UseOfUndefinedFunction(v.clone())); + } } } } @@ -538,10 +567,10 @@ pub struct RBlock { statements: Vec, } impl RBlock { - pub fn run(&self, vars: &Vec>) -> VData { + pub fn run(&self, vars: &Vec>, libs: &Arc>) -> VData { let mut last = None; for statement in &self.statements { - last = Some(statement.run(vars)); + last = Some(statement.run(vars, libs)); } if let Some(v) = last { v @@ -568,8 +597,8 @@ pub struct RFunction { pub block: RBlock, } impl RFunction { - pub fn run(&self, vars: &Vec>) -> VData { - self.block.run(vars) + pub fn run(&self, vars: &Vec>, libs: &Arc>) -> VData { + self.block.run(vars, libs) } pub fn out(&self, input_types: &Vec) -> VType { self.input_output_map @@ -610,8 +639,8 @@ pub struct RStatement { statement: Box, } impl RStatement { - pub fn run(&self, vars: &Vec>) -> VData { - let out = self.statement.run(vars); + pub fn run(&self, vars: &Vec>, libs: &Arc>) -> VData { + let out = self.statement.run(vars, libs); if let Some(v) = self.output_to { *vars[v].lock().unwrap() = out; VDataEnum::Tuple(vec![]).to() @@ -637,6 +666,7 @@ pub enum RStatementEnum { Variable(usize, VType, bool), // Arc> here, because imagine variable in for loop that is used in a different thread -> we need multiple "same" variables FunctionCall(Arc, Vec), BuiltinFunction(BuiltinFunction, Vec), + LibFunction(usize, usize, Vec, VType), Block(RBlock), If(RStatement, RStatement, Option), While(RStatement), @@ -646,13 +676,13 @@ pub enum RStatementEnum { IndexFixed(RStatement, usize), } impl RStatementEnum { - pub fn run(&self, vars: &Vec>) -> VData { + pub fn run(&self, vars: &Vec>, libs: &Arc>) -> VData { match self { Self::Value(v) => v.clone(), Self::Tuple(v) => { let mut w = vec![]; for v in v { - w.push(v.run(vars)); + w.push(v.run(vars, libs)); } VDataEnum::Tuple(w).to() } @@ -660,7 +690,7 @@ impl RStatementEnum { let mut w = vec![]; let mut out = VType { types: vec![] }; for v in v { - let val = v.run(vars); + let val = v.run(vars, libs); out = out | val.out(); w.push(val); } @@ -675,18 +705,20 @@ impl RStatementEnum { } Self::FunctionCall(func, args) => { for (i, input) in func.inputs.iter().enumerate() { - *vars[*input].lock().unwrap() = args[i].run(vars); + *vars[*input].lock().unwrap() = args[i].run(vars, libs); } - func.run(vars) + func.run(vars, libs) } - Self::Block(b) => b.run(vars), + Self::BuiltinFunction(v, args) => v.run(args, vars, libs), + Self::LibFunction(libid, fnid, args, _) => libs[*libid].run_fn(*fnid, &args.iter().map(|arg| arg.run(vars, libs)).collect()), + Self::Block(b) => b.run(vars, libs), Self::If(c, t, e) => { - if let VDataEnum::Bool(v) = c.run(vars).data { + if let VDataEnum::Bool(v) = c.run(vars, libs).data { if v { - t.run(vars) + t.run(vars, libs) } else { if let Some(e) = e { - e.run(vars) + e.run(vars, libs) } else { VDataEnum::Tuple(vec![]).to() } @@ -697,7 +729,7 @@ impl RStatementEnum { } Self::While(c) => loop { // While loops blocks can return a bool (false to break from the loop) or a 0-1 length tuple (0-length => continue, 1-length => break with value) - match c.run(vars).data { + match c.run(vars, libs).data { VDataEnum::Bool(v) => { if !v { break VDataEnum::Tuple(vec![]).to(); @@ -709,11 +741,11 @@ impl RStatementEnum { } }, Self::For(v, c, b) => { - let c = c.run(vars); + let c = c.run(vars, libs); let mut vars = vars.clone(); let mut in_loop = |c| { vars[*v] = Arc::new(Mutex::new(c)); - b.run(&vars); + b.run(&vars, libs); }; match c.data { @@ -736,14 +768,13 @@ impl RStatementEnum { } VDataEnum::Tuple(vec![]).to() } - Self::BuiltinFunction(v, args) => v.run(args, vars), Self::Switch(switch_on, cases) => { - let switch_on = switch_on.run(vars); + let switch_on = switch_on.run(vars, libs); let switch_on_type = switch_on.out(); let mut out = VDataEnum::Tuple(vec![]).to(); for (case_type, case_action) in cases.iter() { if switch_on_type.fits_in(case_type).is_empty() { - out = case_action.run(vars); + out = case_action.run(vars, libs); break; } } @@ -752,7 +783,7 @@ impl RStatementEnum { Self::Match(match_on, cases) => 'm: { for (case_condition, case_action) in cases { // [t] => Some(t), t => Some(t), [] => None - if let Some(v) = match case_condition.run(vars).data { + if let Some(v) = match case_condition.run(vars, libs).data { VDataEnum::Tuple(mut tuple) => tuple.pop(), VDataEnum::Bool(v) => if v { Some(VDataEnum::Bool(v).to()) } else { None }, other => Some(other.to()), @@ -760,14 +791,14 @@ impl RStatementEnum { let og = { std::mem::replace(&mut *vars[*match_on].lock().unwrap(), v) }; - let res = case_action.run(vars); + let res = case_action.run(vars, libs); *vars[*match_on].lock().unwrap() = og; break 'm res; } } VDataEnum::Tuple(vec![]).to() } - Self::IndexFixed(st, i) => st.run(vars).get(*i).unwrap(), + Self::IndexFixed(st, i) => st.run(vars, libs).get(*i).unwrap(), } } pub fn out(&self) -> VType { @@ -796,6 +827,7 @@ impl RStatementEnum { } } Self::FunctionCall(f, args) => f.out_vt(&args.iter().map(|v| v.out()).collect()), + Self::LibFunction(.., out) => out.clone(), Self::Block(b) => b.out(), Self::If(_, a, b) => { if let Some(b) = b { @@ -877,13 +909,14 @@ impl RStatementEnum { pub struct RScript { main: RFunction, vars: usize, + libs: Arc>, } impl RScript { - fn new(main: RFunction, vars: usize) -> Result { + fn new(main: RFunction, vars: usize, libs: Arc>) -> Result { if main.inputs.len() != 1 { return Err(ToRunnableError::MainWrongInput); } - Ok(Self { main, vars }) + Ok(Self { main, vars, libs }) } pub fn run(&self, args: Vec) -> VData { let mut vars = Vec::with_capacity(self.vars); @@ -897,7 +930,7 @@ impl RScript { for _i in 1..self.vars { vars.push(am(VDataEnum::Tuple(vec![]).to())); } - self.main.run(&vars) + self.main.run(&vars, &self.libs) } } diff --git a/src/script/builtins.rs b/src/script/builtins.rs index 8896c8c..7779b9a 100644 --- a/src/script/builtins.rs +++ b/src/script/builtins.rs @@ -4,6 +4,8 @@ use std::{ time::Duration, }; +use crate::libs; + use super::{ block::RStatement, val_data::{VData, VDataEnum, VDataThreadEnum}, @@ -505,16 +507,21 @@ impl BuiltinFunction { }, } } - pub fn run(&self, args: &Vec, vars: &Vec>>) -> VData { + pub fn run( + &self, + args: &Vec, + vars: &Vec>>, + libs: &Arc>, + ) -> VData { match self { Self::Assume1 => { - if let VDataEnum::Tuple(mut v) = args[0].run(vars).data { + if let VDataEnum::Tuple(mut v) = args[0].run(vars, libs).data { v.pop().unwrap() } else { panic!( "ASSUMPTION FAILED: assume1 :: {}", if args.len() > 1 { - if let VDataEnum::String(v) = args[1].run(vars).data { + if let VDataEnum::String(v) = args[1].run(vars, libs).data { v } else { String::new() @@ -526,7 +533,7 @@ impl BuiltinFunction { } } BuiltinFunction::Print => { - if let VDataEnum::String(arg) = args[0].run(vars).data { + if let VDataEnum::String(arg) = args[0].run(vars, libs).data { print!("{}", arg); VDataEnum::Tuple(vec![]).to() } else { @@ -534,7 +541,7 @@ impl BuiltinFunction { } } BuiltinFunction::Println => { - if let VDataEnum::String(arg) = args[0].run(vars).data { + if let VDataEnum::String(arg) = args[0].run(vars, libs).data { println!("{}", arg); VDataEnum::Tuple(vec![]).to() } else { @@ -542,7 +549,7 @@ impl BuiltinFunction { } } BuiltinFunction::Debug => { - println!("{:#?}", args[0].run(vars).data); + println!("{:#?}", args[0].run(vars, libs).data); VDataEnum::Tuple(vec![]).to() } Self::StdinReadLine => { @@ -551,13 +558,15 @@ impl BuiltinFunction { VDataEnum::String(line.trim_end_matches(['\n', '\r']).to_string()).to() } BuiltinFunction::ToString => { - VDataEnum::String(format!("{}", args[0].run(vars).data)).to() + VDataEnum::String(format!("{}", args[0].run(vars, libs).data)).to() } BuiltinFunction::Format => { - if let VDataEnum::String(mut text) = args.first().unwrap().run(vars).data { + if let VDataEnum::String(mut text) = args.first().unwrap().run(vars, libs).data { for (i, arg) in args.iter().skip(1).enumerate() { - text = - text.replace(&format!("{{{i}}}"), &format!("{}", arg.run(vars).data)); + text = text.replace( + &format!("{{{i}}}"), + &format!("{}", arg.run(vars, libs).data), + ); } VDataEnum::String(text).to() } else { @@ -566,7 +575,7 @@ impl BuiltinFunction { } BuiltinFunction::ParseInt => { if args.len() == 1 { - if let VDataEnum::String(s) = args[0].run(vars).data { + if let VDataEnum::String(s) = args[0].run(vars, libs).data { if let Ok(s) = s.parse() { VDataEnum::Int(s).to() } else { @@ -581,7 +590,7 @@ impl BuiltinFunction { } BuiltinFunction::ParseFloat => { if args.len() == 1 { - if let VDataEnum::String(s) = args[0].run(vars).data { + if let VDataEnum::String(s) = args[0].run(vars, libs).data { if let Ok(s) = s.parse() { VDataEnum::Float(s).to() } else { @@ -596,15 +605,15 @@ impl BuiltinFunction { } BuiltinFunction::Run => { if args.len() >= 1 { - if let VDataEnum::Function(f) = args[0].run(vars).data { + if let VDataEnum::Function(f) = args[0].run(vars, libs).data { if f.inputs.len() != args.len() - 1 { unreachable!() } for (i, var) in f.inputs.iter().enumerate() { - let val = args[i + 1].run(vars); + let val = args[i + 1].run(vars, libs); *vars[*var].lock().unwrap() = val; } - f.run(vars) + f.run(vars, libs) } else { unreachable!() } @@ -614,7 +623,7 @@ impl BuiltinFunction { } BuiltinFunction::Thread => { if args.len() >= 1 { - if let VDataEnum::Function(f) = args[0].run(vars).data { + if let VDataEnum::Function(f) = args[0].run(vars, libs).data { if f.inputs.len() != args.len() - 1 { unreachable!() } @@ -622,14 +631,15 @@ impl BuiltinFunction { let mut thread_vars = vars.clone(); let mut run_input_types = vec![]; for (i, var) in f.inputs.iter().enumerate() { - let val = args[i + 1].run(vars); + let val = args[i + 1].run(vars, libs); run_input_types.push(val.out_single()); thread_vars[*var] = Arc::new(Mutex::new(val)); } let out_type = f.out(&run_input_types); + let libs = libs.clone(); VDataEnum::Thread( VDataThreadEnum::Running(std::thread::spawn(move || { - f.run(&thread_vars) + f.run(&thread_vars, &libs) })) .to(), out_type, @@ -644,7 +654,7 @@ impl BuiltinFunction { } BuiltinFunction::Await => { if args.len() == 1 { - if let VDataEnum::Thread(t, _) = args[0].run(vars).data { + if let VDataEnum::Thread(t, _) = args[0].run(vars, libs).data { t.get() } else { unreachable!() @@ -655,7 +665,7 @@ impl BuiltinFunction { } BuiltinFunction::Sleep => { if args.len() == 1 { - match args[0].run(vars).data { + match args[0].run(vars, libs).data { VDataEnum::Int(v) => std::thread::sleep(Duration::from_secs(v as _)), VDataEnum::Float(v) => std::thread::sleep(Duration::from_secs_f64(v)), _ => unreachable!(), @@ -667,7 +677,7 @@ impl BuiltinFunction { } Self::Exit => { if let Some(s) = args.first() { - if let VDataEnum::Int(v) = s.run(vars).data { + if let VDataEnum::Int(v) = s.run(vars, libs).data { std::process::exit(v as _); } else { std::process::exit(1); @@ -678,7 +688,7 @@ impl BuiltinFunction { } Self::FsList => { if args.len() > 0 { - if let VDataEnum::String(path) = args[0].run(vars).data { + if let VDataEnum::String(path) = args[0].run(vars, libs).data { if args.len() > 1 { todo!("fs_list advanced filters") } @@ -713,7 +723,7 @@ impl BuiltinFunction { } Self::FsRead => { if args.len() > 0 { - if let VDataEnum::String(path) = args[0].run(vars).data { + if let VDataEnum::String(path) = args[0].run(vars, libs).data { if let Ok(data) = std::fs::read(path) { VDataEnum::List( VSingleType::Int.into(), @@ -735,7 +745,7 @@ impl BuiltinFunction { Self::FsWrite => { if args.len() > 1 { if let (VDataEnum::String(path), VDataEnum::List(_, data)) = - (args[0].run(vars).data, args[1].run(vars).data) + (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { if let Some(bytes) = vdata_to_bytes(&data) { let file_path: PathBuf = path.into(); @@ -760,7 +770,7 @@ impl BuiltinFunction { } Self::BytesToString => { if args.len() == 1 { - if let VDataEnum::List(_, byte_data) = args[0].run(vars).data { + if let VDataEnum::List(_, byte_data) = args[0].run(vars, libs).data { if let Some(bytes) = vdata_to_bytes(&byte_data) { match String::from_utf8(bytes) { Ok(v) => VDataEnum::String(v).to(), @@ -788,7 +798,7 @@ impl BuiltinFunction { } Self::StringToBytes => { if args.len() == 1 { - if let VDataEnum::String(s) = args[0].run(vars).data { + if let VDataEnum::String(s) = args[0].run(vars, libs).data { VDataEnum::List( VSingleType::Int.into(), s.bytes().map(|v| VDataEnum::Int(v as isize).to()).collect(), @@ -803,10 +813,10 @@ impl BuiltinFunction { } Self::RunCommand | Self::RunCommandGetBytes => { if args.len() > 0 { - if let VDataEnum::String(s) = args[0].run(vars).data { + if let VDataEnum::String(s) = args[0].run(vars, libs).data { let mut command = std::process::Command::new(s); if args.len() > 1 { - if let VDataEnum::List(_, args) = args[1].run(vars).data { + if let VDataEnum::List(_, args) = args[1].run(vars, libs).data { for arg in args { if let VDataEnum::String(v) = arg.data { command.arg(v); @@ -865,7 +875,7 @@ impl BuiltinFunction { } Self::Add => { if args.len() == 2 { - match (args[0].run(vars).data, args[1].run(vars).data) { + match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a + b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Float(a as f64 + b).to() @@ -882,7 +892,7 @@ impl BuiltinFunction { } Self::Sub => { if args.len() == 2 { - match (args[0].run(vars).data, args[1].run(vars).data) { + match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a - b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Float(a as f64 - b).to() @@ -899,7 +909,7 @@ impl BuiltinFunction { } Self::Mul => { if args.len() == 2 { - match (args[0].run(vars).data, args[1].run(vars).data) { + match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a * b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Float(a as f64 * b).to() @@ -916,7 +926,7 @@ impl BuiltinFunction { } Self::Div => { if args.len() == 2 { - match (args[0].run(vars).data, args[1].run(vars).data) { + match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a / b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Float(a as f64 / b).to() @@ -933,7 +943,7 @@ impl BuiltinFunction { } Self::Mod => { if args.len() == 2 { - match (args[0].run(vars).data, args[1].run(vars).data) { + match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a % b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Float(a as f64 % b).to() @@ -950,7 +960,7 @@ impl BuiltinFunction { } Self::Pow => { if args.len() == 2 { - match (args[0].run(vars).data, args[1].run(vars).data) { + match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(if b == 0 { 1 } else if b > 0 { @@ -976,7 +986,7 @@ impl BuiltinFunction { } Self::Eq => { if args.len() == 2 { - match (args[0].run(vars).data, args[1].run(vars).data) { + match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a == b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Bool(a as f64 == b).to() @@ -996,7 +1006,7 @@ impl BuiltinFunction { } Self::Gt => { if args.len() == 2 { - match (args[0].run(vars).data, args[1].run(vars).data) { + match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a > b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Bool(a as f64 > b).to() @@ -1013,7 +1023,7 @@ impl BuiltinFunction { } Self::Lt => { if args.len() == 2 { - match (args[0].run(vars).data, args[1].run(vars).data) { + match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a < b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Bool((a as f64) < b).to() @@ -1030,7 +1040,7 @@ impl BuiltinFunction { } Self::Gtoe => { if args.len() == 2 { - match (args[0].run(vars).data, args[1].run(vars).data) { + match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a >= b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Bool(a as f64 >= b).to() @@ -1047,7 +1057,7 @@ impl BuiltinFunction { } Self::Ltoe => { if args.len() == 2 { - match (args[0].run(vars).data, args[1].run(vars).data) { + match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a <= b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Bool(a as f64 <= b).to() @@ -1064,7 +1074,7 @@ impl BuiltinFunction { } Self::Min => { if args.len() == 2 { - match (args[0].run(vars).data, args[1].run(vars).data) { + match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a.min(b)).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Float((a as f64).min(b)).to() @@ -1083,7 +1093,7 @@ impl BuiltinFunction { } Self::Max => { if args.len() == 2 { - match (args[0].run(vars).data, args[1].run(vars).data) { + match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a.max(b)).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Float((a as f64).max(b)).to() @@ -1102,9 +1112,9 @@ impl BuiltinFunction { } Self::Push => { if args.len() == 2 { - if let VDataEnum::Reference(v) = args[0].run(vars).data { + if let VDataEnum::Reference(v) = args[0].run(vars, libs).data { if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { - v.push(args[1].run(vars)); + v.push(args[1].run(vars, libs)); } VDataEnum::Tuple(vec![]).to() } else { @@ -1117,10 +1127,10 @@ impl BuiltinFunction { Self::Insert => { if args.len() == 3 { if let (VDataEnum::Reference(v), VDataEnum::Int(i)) = - (args[0].run(vars).data, args[2].run(vars).data) + (args[0].run(vars, libs).data, args[2].run(vars, libs).data) { if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { - v.insert(i as _, args[1].run(vars)); + v.insert(i as _, args[1].run(vars, libs)); } VDataEnum::Tuple(vec![]).to() } else { @@ -1132,7 +1142,7 @@ impl BuiltinFunction { } Self::Pop => { if args.len() == 1 { - if let VDataEnum::Reference(v) = args[0].run(vars).data { + if let VDataEnum::Reference(v) = args[0].run(vars, libs).data { if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { v.pop().unwrap_or_else(|| VDataEnum::Tuple(vec![]).to()) } else { @@ -1148,7 +1158,7 @@ impl BuiltinFunction { Self::Remove => { if args.len() == 2 { if let (VDataEnum::Reference(v), VDataEnum::Int(i)) = - (args[0].run(vars).data, args[1].run(vars).data) + (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { if v.len() > i as _ && i >= 0 { @@ -1169,19 +1179,21 @@ impl BuiltinFunction { Self::Get => { if args.len() == 2 { if let (VDataEnum::Reference(v), VDataEnum::Int(i)) = - (args[0].run(vars).data, args[1].run(vars).data) + (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { - if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { + if let VDataEnum::List(_, v) | VDataEnum::Tuple(v) = + &mut v.lock().unwrap().data + { if i >= 0 { match v.get(i as usize) { - Some(v) => v.clone(), + Some(v) => VDataEnum::Tuple(vec![v.clone()]).to(), None => VDataEnum::Tuple(vec![]).to(), } } else { VDataEnum::Tuple(vec![]).to() } } else { - unreachable!("get: not a list") + unreachable!("get: not a list/tuple") } } else { unreachable!("get: not a reference and index") @@ -1192,7 +1204,7 @@ impl BuiltinFunction { } Self::Len => { if args.len() == 1 { - VDataEnum::Int(match args[0].run(vars).data { + VDataEnum::Int(match args[0].run(vars, libs).data { VDataEnum::String(v) => v.len(), VDataEnum::Tuple(v) => v.len(), VDataEnum::List(_, v) => v.len(), @@ -1205,8 +1217,8 @@ impl BuiltinFunction { } Self::Contains => { if args.len() == 2 { - if let VDataEnum::String(a1) = args[0].run(vars).data { - if let VDataEnum::String(a2) = args[1].run(vars).data { + if let VDataEnum::String(a1) = args[0].run(vars, libs).data { + if let VDataEnum::String(a2) = args[1].run(vars, libs).data { VDataEnum::Bool(a1.contains(a2.as_str())).to() } else { unreachable!() @@ -1220,8 +1232,8 @@ impl BuiltinFunction { } Self::StartsWith => { if args.len() == 2 { - if let VDataEnum::String(a1) = args[0].run(vars).data { - if let VDataEnum::String(a2) = args[1].run(vars).data { + if let VDataEnum::String(a1) = args[0].run(vars, libs).data { + if let VDataEnum::String(a2) = args[1].run(vars, libs).data { VDataEnum::Bool(a1.starts_with(a2.as_str())).to() } else { unreachable!() @@ -1235,8 +1247,8 @@ impl BuiltinFunction { } Self::EndsWith => { if args.len() == 2 { - if let VDataEnum::String(a1) = args[0].run(vars).data { - if let VDataEnum::String(a2) = args[1].run(vars).data { + if let VDataEnum::String(a1) = args[0].run(vars, libs).data { + if let VDataEnum::String(a2) = args[1].run(vars, libs).data { VDataEnum::Bool(a1.ends_with(a2.as_str())).to() } else { unreachable!() @@ -1250,7 +1262,7 @@ impl BuiltinFunction { } Self::Trim => { if args.len() == 1 { - if let VDataEnum::String(a) = args[0].run(vars).data { + if let VDataEnum::String(a) = args[0].run(vars, libs).data { VDataEnum::String(a.trim().to_string()).to() } else { unreachable!() @@ -1261,17 +1273,17 @@ impl BuiltinFunction { } Self::Substring => { if args.len() >= 2 { - if let VDataEnum::String(a) = args[0].run(vars).data { + if let VDataEnum::String(a) = args[0].run(vars, libs).data { if args.len() > 3 { unreachable!() } - let left = if let VDataEnum::Int(left) = args[1].run(vars).data { + let left = if let VDataEnum::Int(left) = args[1].run(vars, libs).data { left } else { unreachable!() }; let len = if args.len() == 3 { - if let VDataEnum::Int(len) = args[2].run(vars).data { + if let VDataEnum::Int(len) = args[2].run(vars, libs).data { Some(len) } else { unreachable!() @@ -1300,7 +1312,7 @@ impl BuiltinFunction { Self::Regex => { if args.len() == 2 { if let (VDataEnum::String(a), VDataEnum::String(regex)) = - (args[0].run(vars).data, args[1].run(vars).data) + (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { match regex::Regex::new(regex.as_str()) { Ok(regex) => {