bash-like syntax

This commit is contained in:
Oliver Atkinson 2024-11-26 11:45:53 -07:00
parent 38b97f39b3
commit e2df94a6c2
3 changed files with 61 additions and 10 deletions

20
bash_info.md Normal file
View File

@ -0,0 +1,20 @@
# invalid
```bash
$a="b"
a = "b"
a= "b"
a ="b"
```
# valid
```bash
a="b"
a=b
```
# examples
```bash
a="b c d"
echo $a
# output: b c d
```

View File

@ -1,10 +1,12 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
fs::{self, ReadDir}, fs,
path::Path, path::Path,
process, process,
}; };
use crate::SPECIAL_CHAR;
type InnerEnv = HashMap<String, String>; type InnerEnv = HashMap<String, String>;
pub struct Env { pub struct Env {
inner: InnerEnv, inner: InnerEnv,
@ -30,7 +32,7 @@ impl Env {
} }
pub fn apply_args_substitution(&self, args: Args) -> Vec<String> { pub fn apply_args_substitution(&self, args: Args) -> Vec<String> {
args.iter().fold(Vec::new(), |mut vec, arg| { args.iter().fold(Vec::new(), |mut vec, arg| {
if arg.starts_with('$') { if arg.starts_with(SPECIAL_CHAR) {
// we take a slice to get rid of the '$' // we take a slice to get rid of the '$'
if let Some(sub) = self.get(&arg[1..]) { if let Some(sub) = self.get(&arg[1..]) {
vec.push(sub.to_owned()); vec.push(sub.to_owned());
@ -66,9 +68,6 @@ impl Command {
} }
pub fn call(&self, words: Args, env: &mut Env) { pub fn call(&self, words: Args, env: &mut Env) {
// apply env
let words: Args = &env.apply_args_substitution(words);
let x = &self.cmd; let x = &self.cmd;
x(words, env) x(words, env)
} }

View File

@ -5,6 +5,8 @@ use command::{Command, Env, Args};
mod command; mod command;
pub static SPECIAL_CHAR: char = '$';
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,7 +16,6 @@ fn main() {
let mut env= Env::new(); let mut env= Env::new();
env.set("HOME", "/a/fake/dir"); env.set("HOME", "/a/fake/dir");
env.set("PS1", "> ");
let commands = &Command::all_commands(&env); let commands = &Command::all_commands(&env);
println!("Loaded {} commands", commands.len()); println!("Loaded {} commands", commands.len());
@ -31,7 +32,10 @@ fn main() {
.read_line(&mut buf) .read_line(&mut buf)
.expect("Failed to read in to buffer."); .expect("Failed to read in to buffer.");
let mut args: Args = &buf.trim().split(' ').map(|f| f.to_owned()).collect::<Vec<String>>(); // TODO split at ' ' but don't split when it's surrounded by quotes '"'
let args= &buf.trim().split(' ').map(|f| f.to_owned()).collect::<Vec<String>>();
let args = env.apply_args_substitution(args);
let mut args: Args = &args;
// let mut args: Args = &buf.trim().split(' ').collect::<Vec<&str>>(); // let mut args: Args = &buf.trim().split(' ').collect::<Vec<&str>>();
let mut options = commands; let mut options = commands;
@ -44,7 +48,7 @@ fn main() {
Some(choice) => { Some(choice) => {
// No sub commands left to choose from, just pass the input // No sub commands left to choose from, just pass the input
if choice.sub_commands.is_empty() { if choice.sub_commands.is_empty() {
choice.call(args, &mut env); choice.call(&args, &mut env);
break; break;
} }
@ -55,13 +59,14 @@ fn main() {
if args.is_empty() if args.is_empty()
|| Command::choose(&args[0], options).is_none() // yes, this means that we search for a choice twice if it misses, but the search takes all of 0ms, so it's fine. || Command::choose(&args[0], options).is_none() // yes, this means that we search for a choice twice if it misses, but the search takes all of 0ms, so it's fine.
{ {
choice.call(args, &mut env); choice.call(&args, &mut env);
break; break;
} }
} }
// When no good choice presents it's self // When no good choice presents it's self
None => { None => {
println!("No option..."); // perhaps they are trying to use builtins?
parse_lang(&args, &mut env);
break; break;
}, },
} }
@ -71,3 +76,30 @@ fn main() {
} }
} }
} }
fn parse_lang(tokens: Args, env: &mut Env) {
if let Some(first) = tokens.first() {
if let Some((var, val)) = first.split_once('=') {
let var = var.replace(SPECIAL_CHAR, "");
// Make it so that strings don't get spliced up
let val = if val.starts_with('"') {
let mut buf = String::new();
buf += &val;
for i in &tokens[1..] {
buf += " ";
buf += i;
if i.ends_with('"') {
break;
}
}
buf.replace('"', "")
} else {
val.to_owned()
};
env.set(&var, &val);
} else {
println!("first token is not a command but also doesn't include '='");
}
}
}