changed get to return [] or [v] instead of [] or v because v might be []. This also matches the 0-or-1-length-tuple patterns for optionals (so it can be unwrap()d using .assume1()).

This commit is contained in:
Dummi26 2023-03-30 18:38:05 +02:00
parent 45186e3803
commit 2ba1ed270d
5 changed files with 377 additions and 120 deletions

View File

@ -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<Mutex<ChildStdin>>,
stdout: Arc<Mutex<BufReader<ChildStdout>>>,
pub registered_fns: Vec<(String, Vec<VType>, VType)>,
}
impl Lib {
pub fn launch(mut exec: Command) -> Result<Self, LaunchError> {
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>) -> 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<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])
}
}
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<ChildStdout>) -> 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!(),
}
}

View File

@ -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<char> {
match self.chars.get(self.pos.current_char_index) {
Some((_, c)) => Some(*c),

View File

@ -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<ToRunnableError> for ScriptError {
pub enum ParseError {}
pub fn parse(file: &mut File) -> Result<RScript, ScriptError> {
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<RScript, ScriptError> {
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<SFunction, ParseError> {
Ok(SFunction::new(args, parse_block(file)?))
}
fn parse_type(file: &mut File) -> Result<VType, ParseError> {
pub(crate) fn parse_type(file: &mut File) -> Result<VType, ParseError> {
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 {

View File

@ -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<T>(i: T) -> Am<T> {
Arc::new(Mutex::new(i))
}
pub fn to_runnable(f: SFunction) -> Result<RScript, ToRunnableError> {
to_runnable::to_runnable(f)
pub fn to_runnable(f: SFunction, ginfo: GInfo) -> Result<RScript, ToRunnableError> {
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<Vec<libs::Lib>>,
lib_fns: HashMap<String, (usize, usize)>,
}
impl GInfo {
pub fn new(libs: Arc<Vec<libs::Lib>>) -> 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<String, Arc<RFunction>>,
}
pub fn to_runnable(s: SFunction) -> Result<RScript, ToRunnableError> {
pub fn to_runnable(s: SFunction, mut ginfo: GInfo) -> Result<RScript, ToRunnableError> {
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<RStatement>,
}
impl RBlock {
pub fn run(&self, vars: &Vec<Am<VData>>) -> VData {
pub fn run(&self, vars: &Vec<Am<VData>>, libs: &Arc<Vec<libs::Lib>>) -> 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<Am<VData>>) -> VData {
self.block.run(vars)
pub fn run(&self, vars: &Vec<Am<VData>>, libs: &Arc<Vec<libs::Lib>>) -> VData {
self.block.run(vars, libs)
}
pub fn out(&self, input_types: &Vec<VSingleType>) -> VType {
self.input_output_map
@ -610,8 +639,8 @@ pub struct RStatement {
statement: Box<RStatementEnum>,
}
impl RStatement {
pub fn run(&self, vars: &Vec<Am<VData>>) -> VData {
let out = self.statement.run(vars);
pub fn run(&self, vars: &Vec<Am<VData>>, libs: &Arc<Vec<libs::Lib>>) -> 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<Mutex<..>> here, because imagine variable in for loop that is used in a different thread -> we need multiple "same" variables
FunctionCall(Arc<RFunction>, Vec<RStatement>),
BuiltinFunction(BuiltinFunction, Vec<RStatement>),
LibFunction(usize, usize, Vec<RStatement>, VType),
Block(RBlock),
If(RStatement, RStatement, Option<RStatement>),
While(RStatement),
@ -646,13 +676,13 @@ pub enum RStatementEnum {
IndexFixed(RStatement, usize),
}
impl RStatementEnum {
pub fn run(&self, vars: &Vec<Am<VData>>) -> VData {
pub fn run(&self, vars: &Vec<Am<VData>>, libs: &Arc<Vec<libs::Lib>>) -> 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<Vec<libs::Lib>>,
}
impl RScript {
fn new(main: RFunction, vars: usize) -> Result<Self, ToRunnableError> {
fn new(main: RFunction, vars: usize, libs: Arc<Vec<libs::Lib>>) -> Result<Self, ToRunnableError> {
if main.inputs.len() != 1 {
return Err(ToRunnableError::MainWrongInput);
}
Ok(Self { main, vars })
Ok(Self { main, vars, libs })
}
pub fn run(&self, args: Vec<String>) -> 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)
}
}

View File

@ -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<RStatement>, vars: &Vec<Arc<Mutex<VData>>>) -> VData {
pub fn run(
&self,
args: &Vec<RStatement>,
vars: &Vec<Arc<Mutex<VData>>>,
libs: &Arc<Vec<libs::Lib>>,
) -> 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) => {