built a better processor

This commit is contained in:
Oliver Atkinson 2024-11-25 15:37:23 -07:00
parent e57e7ff907
commit 9af20c0424

View File

@ -1,9 +1,13 @@
#![feature(iter_intersperse)] #![feature(iter_intersperse)]
#![feature(trait_alias)] #![feature(trait_alias)]
use std::{fs, io::{BufRead, Write}, path::Path, process::{self, Stdio}}; use std::{
fs,
io::{BufRead, Write},
path::Path,
process::{self},
};
fn main() { fn main() {
let stdin = std::io::stdin(); let stdin = std::io::stdin();
let mut in_lock = stdin.lock(); let mut in_lock = stdin.lock();
@ -14,40 +18,69 @@ fn main() {
println!("Loaded {} commands", commands.len()); println!("Loaded {} commands", commands.len());
loop { loop {
let mut buf = String::new(); let mut buf = String::new();
let ps1 = "> ".as_bytes(); let ps1 = "> ".as_bytes();
out_lock.write(ps1).unwrap(); out_lock.write(ps1).unwrap();
out_lock.flush().unwrap(); out_lock.flush().unwrap();
in_lock.read_line(&mut buf).expect("Failed to read in to buffer."); in_lock
let trimmed_input = buf.trim(); .read_line(&mut buf)
.expect("Failed to read in to buffer.");
let o_choice = Command::choose(trimmed_input, commands); let mut args: Param = &buf.trim().split(' ').collect::<Vec<&str>>();
if let Some(choice) = o_choice { let mut options = commands;
let words = trimmed_input.split(' ').collect::<Vec<&str>>();
choice.call(words); loop {
match args.get(0) {
Some(cmd_arg) => {
// Try to find a subcommand to run
match Command::choose(cmd_arg, options) {
Some(choice) => {
if choice.sub_commands.is_empty() {
// No sub commands left to choose from, just pass the input
choice.call(&args);
break;
} else {
args = &args[1..];
options = &choice.sub_commands;
// a sort of last chance before running into hella edgecases
if args.is_empty() {
choice.call(args);
break;
}
}
}
// When no good choice presents it's self
None => break,
}
}
None => todo!("Couldn't get index 0 of the args"),
}
} }
} }
} }
type Param<'a> = Vec<&'a str>; type Param<'a, 'b> = &'b [&'a str];
trait Run = Fn(Param); trait Run = Fn(Param);
type Cmd = Box<dyn Run>; type Cmd = Box<dyn Run>;
// #[derive(Debug)]
struct Command { struct Command {
name: String, name: String,
_sub_commands: Vec<Command>, sub_commands: Vec<Command>,
cmd: Cmd, cmd: Cmd,
} }
impl Command { impl Command {
fn init<T>(name: T, sub_commands: Vec<Self>, f: Cmd) -> Self where T: ToString { fn init<T>(name: T, sub_commands: Vec<Self>, f: Cmd) -> Self
where
T: ToString,
{
Self { Self {
name: name.to_string(), name: name.to_string(),
_sub_commands: sub_commands, sub_commands,
cmd: f, cmd: f,
} }
} }
@ -58,37 +91,65 @@ impl Command {
} }
fn choose<'a>(text: &str, choices: &'a Vec<Self>) -> Option<&'a Self> { fn choose<'a>(text: &str, choices: &'a Vec<Self>) -> Option<&'a Self> {
// TODO there seems to be duplicates in here? // TODO there seems to be duplicates in here?
// See what command the user is trying to call // See what command the user is trying to call
// iter thru all the commands // iter thru all the commands
let mut ranked = choices.iter().fold(Vec::new(), |mut v, cmd| { let mut ranked = choices.iter().fold(Vec::new(), |mut v, cmd| {
// count how many chars match from the command to the word // count how many chars match from the command to the word
let matches = text.chars().zip(cmd.name.chars()).fold(0, |mut f, (word, cmd)| { let matches = text
if word == cmd { f += 1; } .chars()
f .zip(cmd.name.chars())
}); .fold(0, |mut f, (word, cmd)| {
if word == cmd {
f += 1;
}
f
});
v.push((cmd, matches)); v.push((cmd, matches));
v v
}); });
// sort by most matches // sort by most matches
ranked.sort_by(|a,b| b.1.cmp(&a.1)); ranked.sort_by(|a, b| b.1.cmp(&a.1));
// Don't select commands just because they are first. The command also has to have a
// confidence of at least 1
if let Some(first) = ranked.first() {
if first.1 < 1 {
return None;
}
}
// TODO test when 1 100% match exists
if ranked.len() > 1 { if ranked.len() > 1 {
// If the top two command choices are ranked the same, we don't know which to choose! // If the top two command choices are ranked the same, we don't know which to choose!
let top = ranked[0]; let top = ranked[0];
let second = ranked[0]; let second = ranked[1];
if top.1 == second.1 { if top.1 == second.1 {
println!("Ambigous command '{}'.\nCould be any of {}, {}, etc...", text, top.0.name, second.0.name); println!("Ambigous command '{text}'. Could be any of: ");
let iter: Vec<&(&Command, usize)> = ranked
.iter()
// We only care about 100% matches
.filter(|f| f.1 >= text.len())
.collect();
let max_display = if 5 < iter.len() {5} else {iter.len()};
iter[..max_display].iter().for_each(|matching| {
println!("\t- {}", matching.0.name);
});
if iter.len() > max_display {
println!("\tOmitting {} other entries.", iter.len() - max_display);
}
return None; return None;
} }
} }
if let Some((cmd, _)) = ranked.first() { if let Some((cmd, _)) = ranked.first() {
// let words = text.split(' ').collect::<Vec<&str>>();
return Some(cmd); return Some(cmd);
} }
None None
@ -97,7 +158,7 @@ impl Command {
fn commands() -> Vec<Self> { fn commands() -> Vec<Self> {
let path = Path::new("/bin"); let path = Path::new("/bin");
let res = fs::read_dir(path).expect("Failed to read /bin"); let res = fs::read_dir(path).expect("Failed to read /bin");
// load all the programs from /bin // load all the programs from /bin
let bin = res.fold(Vec::new(), |mut v, contents| { let bin = res.fold(Vec::new(), |mut v, contents| {
if let Ok(cmd) = contents { if let Ok(cmd) = contents {
@ -111,34 +172,67 @@ impl Command {
let name = cmd.path().to_str().unwrap().to_owned(); let name = cmd.path().to_str().unwrap().to_owned();
let out = process::Command::new(name) let out = process::Command::new(name)
.args(args) .args(args)
// FIXME not printing to out
.stdout(Stdio::inherit())
.output() .output()
.expect("Failed to call external program"); .expect("Failed to call external program");
// FIXME not printing to out
let y = out.stdout.iter().map(|b| *b as char).collect::<String>(); let y = out.stdout.iter().map(|b| *b as char).collect::<String>();
// TODO load into env // TODO load into env
// let status = out.status.code(); // let status = out.status.code();
println!("{y}"); println!("{y}");
} }),
) );
);
v.push(cmd); v.push(cmd);
} }
}; };
} ; };
v v
}); });
// load custom programs // load custom programs
let mut commands = vec![ let mut commands = vec![
Command::init("exit", vec![], Box::new(|_| { Command::init(
std::process::exit(0); "exit",
})), vec![],
Command::init("custom_echo", vec![], Box::new(|f| { Box::new(|_| {
let f:Vec<&str>=f; std::process::exit(0);
let msg = &f[1..].iter().map(|s| s.to_owned()).intersperse(" ").collect::<String>(); }),
println!("{msg}"); ),
})), Command::init(
"custom_echo",
vec![],
Box::new(|args| {
let msg = &args[1..]
.iter()
.map(|s| s.to_owned())
.intersperse(" ")
.collect::<String>();
println!("{msg}");
}),
),
Command::init(
"random",
vec![
Command::init(
"number",
vec![],
Box::new(|_| {
// how do you know it's not random?
println!("8");
}),
),
Command::init(
"string",
vec![],
Box::new(|_| {
// how do you know it's not random?
println!("hello world");
}),
),
],
Box::new(|args| {
println!("random's debug: {:?}", args);
}),
),
]; ];
// combine with a move // combine with a move
@ -146,4 +240,3 @@ impl Command {
commands commands
} }
} }