change parse_int/float and debug, add spawn_command and childproc_* functions

This commit is contained in:
Mark 2024-02-17 11:46:07 +01:00
parent 2a218ddbe2
commit 5d752c9969
4 changed files with 350 additions and 50 deletions

View File

@ -11,6 +11,6 @@ repository = "https://github.com/Dummi26/mers"
# 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
[dependencies] [dependencies]
mers_lib = "0.4.0" # mers_lib = "0.4.0"
# mers_lib = { path = "../mers_lib" } mers_lib = { path = "../mers_lib" }
clap = { version = "4.3.19", features = ["derive"] } clap = { version = "4.3.19", features = ["derive"] }

View File

@ -1,6 +1,7 @@
use std::{ use std::{
fmt::Display, fmt::Display,
process::Command, io::{BufRead, BufReader, Read},
process::{ChildStderr, ChildStdin, ChildStdout, Command, Stdio},
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
@ -18,7 +19,10 @@ impl Config {
/// `RunCommandError` holds the error if the command can't be executed /// `RunCommandError` holds the error if the command can't be executed
/// returns (int/(), string, string) on success (status code, stdout, stderr) /// returns (int/(), string, string) on success (status code, stdout, stderr)
pub fn with_command_running(self) -> Self { pub fn with_command_running(self) -> Self {
self.add_var( self
.add_type("RunCommandError".to_owned(), Ok(Arc::new(RunCommandErrorT)))
.add_type("ChildProcess".to_owned(), Ok(Arc::new(ChildProcessT)))
.add_var(
"run_command".to_string(), "run_command".to_string(),
Data::new(data::function::Function { Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()), info: Arc::new(program::run::Info::neverused()),
@ -27,7 +31,7 @@ impl Config {
if a.types.iter().all(|t| t.as_any().downcast_ref::<data::tuple::TupleT>().is_some_and(|t| t.0.len() == 2 && t.0[0].is_included_in(&data::string::StringT) && t.0[1].iterable().is_some_and(|t| t.is_included_in(&data::string::StringT)))) { if a.types.iter().all(|t| t.as_any().downcast_ref::<data::tuple::TupleT>().is_some_and(|t| t.0.len() == 2 && t.0[0].is_included_in(&data::string::StringT) && t.0[1].iterable().is_some_and(|t| t.is_included_in(&data::string::StringT)))) {
Ok(Type::newm(vec![ Ok(Type::newm(vec![
Arc::new(data::tuple::TupleT(vec![ Arc::new(data::tuple::TupleT(vec![
Type::newm(vec![Arc::new(data::int::IntT), Arc::new(data::tuple::TupleT(vec![]))]), Type::newm(vec![Arc::new(data::int::IntT), Arc::new(data::bool::BoolT)]),
Type::new(data::string::StringT), Type::new(data::string::StringT),
Type::new(data::string::StringT), Type::new(data::string::StringT),
])), ])),
@ -38,47 +42,338 @@ impl Config {
} }
}), }),
run: Arc::new(|a, _i| { run: Arc::new(|a, _i| {
if let Some(cmd) = a.get().as_any().downcast_ref::<data::tuple::Tuple>() { let a = a.get();
if let (Some(cmd), Some(args)) = (cmd.get(0), cmd.get(1)) { let cmd = a.as_any().downcast_ref::<data::tuple::Tuple>().unwrap();
if let (Some(cmd), Some(args)) = ( let (cmd, args) = (&cmd.0[0], &cmd.0[1]);
cmd.get().as_any().downcast_ref::<data::string::String>(), let cmd = cmd.get();
args.get().iterable(), let (cmd, args) = (
) { cmd.as_any().downcast_ref::<data::string::String>().unwrap(),
match Command::new(&cmd.0) args.get().iterable().unwrap(),
.args(args.map(|v| v.get().to_string())) );
.output() match Command::new(&cmd.0)
{ .args(args.map(|v| v.get().to_string()))
Ok(output) => { .output()
let status = if let Some(code) = output.status.code() { {
Data::new(data::int::Int(code as _)) Ok(output) => {
} else { let status = if let Some(code) = output.status.code() {
Data::empty_tuple() Data::new(data::int::Int(code as _))
};
let stdout =
String::from_utf8_lossy(&output.stdout).into_owned();
let stderr =
String::from_utf8_lossy(&output.stderr).into_owned();
Data::new(data::tuple::Tuple(vec![
status,
Data::new(data::string::String(stdout)),
Data::new(data::string::String(stderr)),
]))
}
Err(e) => Data::new(RunCommandError(e.to_string())),
}
} else { } else {
unreachable!("run_command called with arguments other than (String, <Iterable>).") Data::new(data::bool::Bool(output.status.success()))
} };
} else { let stdout =
unreachable!("run_command called with too few arguments") String::from_utf8_lossy(&output.stdout).into_owned();
let stderr =
String::from_utf8_lossy(&output.stderr).into_owned();
Data::new(data::tuple::Tuple(vec![
status,
Data::new(data::string::String(stdout)),
Data::new(data::string::String(stderr)),
]))
} }
} else { Err(e) => Data::new(RunCommandError(e.to_string())),
unreachable!("run_command called with non-tuple argument")
} }
}), }),
inner_statements: None, inner_statements: None,
}), }),
) )
.add_var(
"spawn_command".to_string(),
Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new( CheckInfo::neverused())),
out: Arc::new(|a, _i| {
if a.types.iter().all(|t| t.as_any().downcast_ref::<data::tuple::TupleT>().is_some_and(|t| t.0.len() == 2 && t.0[0].is_included_in(&data::string::StringT) && t.0[1].iterable().is_some_and(|t| t.is_included_in(&data::string::StringT)))) {
Ok(Type::newm(vec![
Arc::new(ChildProcessT),
Arc::new(RunCommandErrorT)
]))
} else {
return Err(format!("spawn_command called with invalid arguments (must be (String, Iter<String>))").into());
}
}),
run: Arc::new(|a, _i| {
let a = a.get();
let cmd = a.as_any().downcast_ref::<data::tuple::Tuple>().unwrap();
let (cmd, args) = (&cmd.0[0], &cmd.0[1]);
let cmd = cmd.get();
let (cmd, args) = (
cmd.as_any().downcast_ref::<data::string::String>().unwrap(),
args.get().iterable().unwrap(),
);
match Command::new(&cmd.0)
.args(args.map(|v| v.get().to_string()))
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
{
Ok(mut child) => {
let a = child.stdin.take().unwrap();
let b = BufReader::new(child.stdout.take().unwrap());
let c = BufReader::new(child.stderr.take().unwrap());
Data::new(ChildProcess(Arc::new(Mutex::new((child, a, b, c)))))
}
Err(e) => Data::new(RunCommandError(e.to_string())),
}
}),
inner_statements: None,
}),
)
.add_var(
"childproc_exited".to_string(),
Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new( CheckInfo::neverused())),
out: Arc::new(|a, _i| {
if a.is_included_in(&ChildProcessT) {
Ok(Type::newm(vec![
Arc::new(data::tuple::TupleT(vec![Type::new(data::bool::BoolT)])),
Arc::new(data::tuple::TupleT(vec![])),
]))
} else {
return Err(format!("childproc_exited called on non-ChildProcess type {a}").into());
}
}),
run: Arc::new(|a, _i| {
let a = a.get();
let child = a.as_any().downcast_ref::<ChildProcess>().unwrap();
let mut child = child.0.lock().unwrap();
match child.0.try_wait() {
Ok(Some(_)) => Data::one_tuple(Data::new(data::bool::Bool(true))),
Ok(None) => Data::one_tuple(Data::new(data::bool::Bool(false))),
Err(_) => Data::empty_tuple(),
}
}),
inner_statements: None,
}),
)
.add_var(
"childproc_await".to_string(),
Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new( CheckInfo::neverused())),
out: Arc::new(|a, _i| {
if a.is_included_in(&ChildProcessT) {
Ok(Type::newm(vec![
Arc::new(data::int::IntT),
Arc::new(data::bool::BoolT),
Arc::new(data::tuple::TupleT(vec![])),
]))
} else {
return Err(format!("childproc_exited called on non-ChildProcess type {a}").into());
}
}),
run: Arc::new(|a, _i| {
let a = a.get();
let child = a.as_any().downcast_ref::<ChildProcess>().unwrap();
let mut child = child.0.lock().unwrap();
match child.0.wait() {
Ok(s) => if let Some(s) = s.code() {
Data::new(data::int::Int(s as _))
} else {
Data::new(data::bool::Bool(s.success()))
}
Err(_) => Data::empty_tuple(),
}
}),
inner_statements: None,
}),
)
.add_var(
"childproc_read_byte".to_string(),
Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new( CheckInfo::neverused())),
out: Arc::new(|a, _i| {
if a.is_included_in(&ChildProcessT) {
Ok(Type::newm(vec![
Arc::new(data::tuple::TupleT(vec![Type::new(data::int::IntT)])),
Arc::new(data::tuple::TupleT(vec![])),
]))
} else {
return Err(format!("childproc_read_byte called on non-ChildProcess type {a}").into());
}
}),
run: Arc::new(|a, _i| {
let a = a.get();
let child = a.as_any().downcast_ref::<ChildProcess>().unwrap();
let mut child = child.0.lock().unwrap();
let mut buf = [0];
if child.2.read_exact(&mut buf).is_ok() {
Data::one_tuple(Data::new(data::int::Int(buf[0] as _)))
} else {
Data::empty_tuple()
}
}),
inner_statements: None,
}),
)
.add_var(
"childproc_readerr_byte".to_string(),
Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new( CheckInfo::neverused())),
out: Arc::new(|a, _i| {
if a.is_included_in(&ChildProcessT) {
Ok(Type::newm(vec![
Arc::new(data::tuple::TupleT(vec![Type::new(data::int::IntT)])),
Arc::new(data::tuple::TupleT(vec![])),
]))
} else {
return Err(format!("childproc_readerr_byte called on non-ChildProcess type {a}").into());
}
}),
run: Arc::new(|a, _i| {
let a = a.get();
let child = a.as_any().downcast_ref::<ChildProcess>().unwrap();
let mut child = child.0.lock().unwrap();
let mut buf = [0];
if child.3.read_exact(&mut buf).is_ok() {
Data::one_tuple(Data::new(data::int::Int(buf[0] as _)))
} else {
Data::empty_tuple()
}
}),
inner_statements: None,
}),
)
.add_var(
"childproc_read_line".to_string(),
Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new( CheckInfo::neverused())),
out: Arc::new(|a, _i| {
if a.is_included_in(&ChildProcessT) {
Ok(Type::newm(vec![
Arc::new(data::tuple::TupleT(vec![Type::new(data::string::StringT)])),
Arc::new(data::tuple::TupleT(vec![])),
]))
} else {
return Err(format!("childproc_read_line called on non-ChildProcess type {a}").into());
}
}),
run: Arc::new(|a, _i| {
let a = a.get();
let child = a.as_any().downcast_ref::<ChildProcess>().unwrap();
let mut child = child.0.lock().unwrap();
let mut buf = String::new();
if child.2.read_line(&mut buf).is_ok() {
Data::one_tuple(Data::new(data::string::String(buf)))
} else {
Data::empty_tuple()
}
}),
inner_statements: None,
}),
)
.add_var(
"childproc_readerr_line".to_string(),
Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new( CheckInfo::neverused())),
out: Arc::new(|a, _i| {
if a.is_included_in(&ChildProcessT) {
Ok(Type::newm(vec![
Arc::new(data::tuple::TupleT(vec![Type::new(data::string::StringT)])),
Arc::new(data::tuple::TupleT(vec![])),
]))
} else {
return Err(format!("childproc_read_line called on non-ChildProcess type {a}").into());
}
}),
run: Arc::new(|a, _i| {
let a = a.get();
let child = a.as_any().downcast_ref::<ChildProcess>().unwrap();
let mut child = child.0.lock().unwrap();
let mut buf = String::new();
if child.3.read_line(&mut buf).is_ok() {
Data::one_tuple(Data::new(data::string::String(buf)))
} else {
Data::empty_tuple()
}
}),
inner_statements: None,
}),
)
}
}
#[derive(Clone, Debug)]
pub struct ChildProcess(
Arc<
Mutex<(
std::process::Child,
ChildStdin,
BufReader<ChildStdout>,
BufReader<ChildStderr>,
)>,
>,
);
#[derive(Clone, Debug)]
pub struct ChildProcessT;
impl MersData for ChildProcess {
fn iterable(&self) -> Option<Box<dyn Iterator<Item = Data>>> {
None
}
fn get(&self, _i: usize) -> Option<Data> {
None
}
fn is_eq(&self, other: &dyn MersData) -> bool {
other
.as_any()
.downcast_ref::<Self>()
.is_some_and(|other| Arc::ptr_eq(&self.0, &other.0))
}
fn clone(&self) -> Box<dyn MersData> {
Box::new(Clone::clone(self))
}
fn as_type(&self) -> Type {
Type::new(ChildProcessT)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn mut_any(&mut self) -> &mut dyn std::any::Any {
self
}
fn to_any(self) -> Box<dyn std::any::Any> {
Box::new(self)
}
}
impl Display for ChildProcess {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<ChildProcess>")
}
}
impl MersType for ChildProcessT {
fn iterable(&self) -> Option<Type> {
None
}
fn get(&self) -> Option<Type> {
None
}
fn is_same_type_as(&self, other: &dyn MersType) -> bool {
other.as_any().is::<Self>()
}
fn is_included_in_single(&self, target: &dyn MersType) -> bool {
target.as_any().is::<Self>()
}
fn subtypes(&self, acc: &mut Type) {
acc.add(Arc::new(self.clone()));
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn mut_any(&mut self) -> &mut dyn std::any::Any {
self
}
fn to_any(self) -> Box<dyn std::any::Any> {
Box::new(self)
}
}
impl Display for ChildProcessT {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ChildProcess")
} }
} }
@ -132,7 +427,7 @@ impl MersType for RunCommandErrorT {
} }
impl Display for RunCommandError { impl Display for RunCommandError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RunCommandError: {}", self.0) write!(f, "{}", self.0)
} }
} }
impl Display for RunCommandErrorT { impl Display for RunCommandErrorT {

View File

@ -19,8 +19,8 @@ impl Config {
/// `gt: fn` returns true if the input keeps decreasing, that is, for (a, b), a > b, for (a, b, c), a > b > c, and so on. /// `gt: fn` returns true if the input keeps decreasing, that is, for (a, b), a > b, for (a, b, c), a > b > c, and so on.
/// `ltoe: fn` returns true if the input only increases, that is, for (a, b), a <= b, for (a, b, c), a <= b <= c, and so on. /// `ltoe: fn` returns true if the input only increases, that is, for (a, b), a <= b, for (a, b, c), a <= b <= c, and so on.
/// `gtoe: fn` returns true if the input only decreases, that is, for (a, b), a >= b, for (a, b, c), a >= b >= c, and so on. /// `gtoe: fn` returns true if the input only decreases, that is, for (a, b), a >= b, for (a, b, c), a >= b >= c, and so on.
/// `parse_int: fn` parses a string to an int, returns () on failure /// `parse_int: fn` parses a string to an int, returns () on failure and (Int) otherwise
/// `parse_float: fn` parses a string to an int, returns () on failure /// `parse_float: fn` parses a string to an int, returns () on failure and (Int) otherwise
/// TODO! /// TODO!
/// `as_float: fn` turns integers into floats. returns floats without changing them. /// `as_float: fn` turns integers into floats. returns floats without changing them.
/// `round: fn` rounds the float and returns an int /// `round: fn` rounds the float and returns an int
@ -62,7 +62,9 @@ impl Config {
out: Arc::new(|a, _i| { out: Arc::new(|a, _i| {
if a.is_included_in(&Type::new(data::string::StringT)) { if a.is_included_in(&Type::new(data::string::StringT)) {
Ok(Type::newm(vec![ Ok(Type::newm(vec![
Arc::new(data::float::FloatT), Arc::new(data::tuple::TupleT(vec![
Type::new(data::float::FloatT),
])),
Arc::new(data::tuple::TupleT(vec![])), Arc::new(data::tuple::TupleT(vec![])),
])) ]))
} else { } else {
@ -71,7 +73,7 @@ impl Config {
}), }),
run: Arc::new(|a, _i| { run: Arc::new(|a, _i| {
if let Ok(n) = a.get().as_any().downcast_ref::<data::string::String>().unwrap().0.parse() { if let Ok(n) = a.get().as_any().downcast_ref::<data::string::String>().unwrap().0.parse() {
Data::new(data::float::Float(n)) Data::one_tuple(Data::new(data::float::Float(n)))
} else { } else {
Data::empty_tuple() Data::empty_tuple()
} }
@ -83,7 +85,9 @@ impl Config {
out: Arc::new(|a, _i| { out: Arc::new(|a, _i| {
if a.is_included_in(&Type::new(data::string::StringT)) { if a.is_included_in(&Type::new(data::string::StringT)) {
Ok(Type::newm(vec![ Ok(Type::newm(vec![
Arc::new(data::int::IntT), Arc::new(data::tuple::TupleT(vec![
Type::new(data::int::IntT),
])),
Arc::new(data::tuple::TupleT(vec![])), Arc::new(data::tuple::TupleT(vec![])),
])) ]))
} else { } else {
@ -92,7 +96,7 @@ impl Config {
}), }),
run: Arc::new(|a, _i| { run: Arc::new(|a, _i| {
if let Ok(n) = a.get().as_any().downcast_ref::<data::string::String>().unwrap().0.parse() { if let Ok(n) = a.get().as_any().downcast_ref::<data::string::String>().unwrap().0.parse() {
Data::new(data::int::Int(n)) Data::one_tuple(Data::new(data::int::Int(n)))
} else { } else {
Data::empty_tuple() Data::empty_tuple()
} }

View File

@ -50,11 +50,12 @@ impl Config {
Data::new(data::function::Function { Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()), info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new(CheckInfo::neverused())), info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
out: Arc::new(|_a, _i| Ok(Type::empty_tuple())), out: Arc::new(|a, _i| Ok(a.clone())),
run: Arc::new(|a, _i| { run: Arc::new(|a, _i| {
let a = a.get(); let a2 = a.get();
eprintln!("{} :: {}", a.as_type(), a); eprintln!("{} :: {}", a2.as_type(), a2);
Data::empty_tuple() drop(a2);
a
}), }),
inner_statements: None, inner_statements: None,
}), }),