mirror of
https://github.com/danbulant/rush
synced 2026-05-19 04:18:35 +00:00
feat: working executing simple commands
This commit is contained in:
parent
847e49ec92
commit
bd72bdb17f
5 changed files with 198 additions and 48 deletions
26
src/main.rs
26
src/main.rs
|
|
@ -67,24 +67,24 @@ impl Shell {
|
|||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
loop {
|
||||
print!("$: ");
|
||||
io::stdout().flush();
|
||||
let mut shell = collect();
|
||||
shell.term.input += "\n";
|
||||
parser::exec(&mut shell.term.input.as_bytes(), shell.ctx);
|
||||
fn collect(&mut self) {
|
||||
let stdin = std::io::stdin();
|
||||
let v = stdin.lock().lines().next().unwrap().unwrap();
|
||||
self.term.input = v;
|
||||
}
|
||||
}
|
||||
|
||||
fn collect() -> Shell {
|
||||
fn main() {
|
||||
let mut shell = Shell::new();
|
||||
let stdin = std::io::stdin();
|
||||
let v = stdin.lock().lines().next().unwrap().unwrap();
|
||||
shell.term.input = v;
|
||||
shell
|
||||
shell.ctx.add_scope(true);
|
||||
loop {
|
||||
print!("$: ");
|
||||
io::stdout().flush();
|
||||
shell.collect();
|
||||
shell.term.input += "\n";
|
||||
parser::exec(&mut shell.term.input.as_bytes(), &mut shell.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn editor() -> Shell {
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ pub enum Value {
|
|||
ArrayFunction(DefinedFunction),
|
||||
StringFunction(DefinedFunction),
|
||||
Expressions(Vec<Expression>),
|
||||
Expression(Expression),
|
||||
Values(Vec<Value>)
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +118,6 @@ impl Tree {
|
|||
fn parse_call(&mut self, end: usize) -> Expression {
|
||||
let mut values: Vec<CommandValue> = Vec::new();
|
||||
let mut buf: Vec<Value> = Vec::new();
|
||||
dbg!("call parse");
|
||||
let mut token = self.get_current_token();
|
||||
loop {
|
||||
if matches!(token, Tokens::Space) {
|
||||
|
|
@ -130,7 +128,6 @@ impl Tree {
|
|||
if self.i >= end - 1 { break }
|
||||
self.i += 1;
|
||||
token = self.get_current_token();
|
||||
dbg!("skip space");
|
||||
continue;
|
||||
}
|
||||
let val = match &token {
|
||||
|
|
@ -145,6 +142,9 @@ impl Tree {
|
|||
Tokens::FileWrite => break,
|
||||
Tokens::FileRead => break,
|
||||
Tokens::RedirectInto => break,
|
||||
Tokens::And => break,
|
||||
Tokens::Or => break,
|
||||
Tokens::JobCommandEnd => break,
|
||||
Tokens::ParenthesisEnd => {
|
||||
if self.i >= end - 1 {
|
||||
break;
|
||||
|
|
@ -160,14 +160,11 @@ impl Tree {
|
|||
self.i += 1;
|
||||
token = self.tokens.get(self.i).unwrap();
|
||||
if matches!(token, Tokens::CommandEnd(_)) { break }
|
||||
dbg!("parse call loop");
|
||||
}
|
||||
dbg!(&token);
|
||||
match &token {
|
||||
Tokens::FileWrite | Tokens::FileRead | Tokens::RedirectInto => self.i -= 1,
|
||||
_ => {}
|
||||
}
|
||||
dbg!(&self);
|
||||
// self.next();
|
||||
if buf.len() > 0 {
|
||||
values.push(CommandValue::Value(Value::Values(buf)));
|
||||
|
|
@ -201,7 +198,6 @@ impl Tree {
|
|||
let mut val_end = self.i;
|
||||
let mut found_first = false;
|
||||
for token in &self.tokens[self.i..] {
|
||||
dbg!(&token);
|
||||
val_end += 1;
|
||||
match token {
|
||||
Tokens::Space => if found_first { break },
|
||||
|
|
@ -211,7 +207,6 @@ impl Tree {
|
|||
_ => { found_first = true; }
|
||||
}
|
||||
}
|
||||
dbg!(&self.tokens[self.i..val_end]);
|
||||
val_end -= 1;
|
||||
let source = Box::new(self.get_value(val_end));
|
||||
self.inc();
|
||||
|
|
@ -271,7 +266,6 @@ impl Tree {
|
|||
loop {
|
||||
if self.i >= end - 1 { break; }
|
||||
expressions.push(self.get_expression(end));
|
||||
dbg!(&expressions);
|
||||
}
|
||||
expressions
|
||||
}
|
||||
|
|
@ -317,8 +311,6 @@ impl Tree {
|
|||
len += 1;
|
||||
}
|
||||
// self.inc();
|
||||
dbg!(&self);
|
||||
dbg!(len, lvl);
|
||||
if lvl != 0 {
|
||||
panic!("Sub not ended properly");
|
||||
}
|
||||
|
|
@ -359,20 +351,15 @@ impl Tree {
|
|||
|
||||
fn get_expression(&mut self, end: usize) -> Expression {
|
||||
let mut token = self.get_current_token();
|
||||
dbg!("expr");
|
||||
let mut expr: Option<Expression> = None;
|
||||
loop {
|
||||
dbg!(&token);
|
||||
match token {
|
||||
Tokens::Space => {self.inc();},
|
||||
Tokens::CommandEnd(_) => {self.inc();},
|
||||
Tokens::CommandEnd(_) => { if matches!(expr, Some(_)) { break }; self.inc();},
|
||||
Tokens::Literal(t) => if matches!(expr, Some(_)) {
|
||||
dbg!(t);
|
||||
dbg!(expr);
|
||||
panic!("Unexpected literal. After file redirect, you need to use a semicolon or newline.");
|
||||
} else {
|
||||
expr = Some(self.parse_call(end));
|
||||
dbg!(&self);
|
||||
},
|
||||
Tokens::ExportSet => panic!("Unexpected token EXPORT_SET (=)"),
|
||||
Tokens::Function => return Expression::Function(self.parse_function(end)),
|
||||
|
|
@ -382,13 +369,10 @@ impl Tree {
|
|||
None => panic!("Unexpected token REDIRECT (|)"),
|
||||
Some(_) => {
|
||||
self.i += 1;
|
||||
dbg!(&self);
|
||||
expr = Some(Expression::RedirectTargetExpression(RedirectTargetExpression { source: Box::new(expr.unwrap()), target: Box::new(self.get_expression(end)) }));
|
||||
dbg!("after redirect");
|
||||
}
|
||||
},
|
||||
Tokens::ParenthesisStart => if matches!(expr, Some(_)) {
|
||||
dbg!(expr);
|
||||
panic!("Unexpected parenthesis. After file redirect, you need to use a semicolon or newline.");
|
||||
} else {
|
||||
let mut len = 1;
|
||||
|
|
@ -411,28 +395,48 @@ impl Tree {
|
|||
panic!("Parenthesis not ended properly.");
|
||||
}
|
||||
expr = Some(self.get_expression(self.i + len));
|
||||
dbg!(&self);
|
||||
self.inc();
|
||||
},
|
||||
Tokens::ParenthesisEnd => panic!("Unexpected token PARENTHESIS END ())"),
|
||||
Tokens::ArrayFunction(_) => panic!("Unexpected array function"),
|
||||
Tokens::StringFunction(_) => panic!("Unexpected string function"),
|
||||
Tokens::SubStart => return self.parse_call(end),
|
||||
Tokens::SubStart => if matches!(expr, Some(_)) {
|
||||
panic!("Unexpected literal. After file redirect, you need to use a semicolon or newline.");
|
||||
} else {
|
||||
expr = Some(self.parse_call(end));
|
||||
},
|
||||
Tokens::Else => panic!("Unexpected token ELSE"),
|
||||
Tokens::End => panic!("Unexpected token END"),
|
||||
Tokens::For => return Expression::ForExpression(self.parse_for(end)),
|
||||
Tokens::If => return Expression::IfExpression(self.parse_if(end)),
|
||||
Tokens::For => match expr {
|
||||
Some(_) => panic!("Commands must be ended properly"),
|
||||
None => expr = Some(Expression::ForExpression(self.parse_for(end))),
|
||||
},
|
||||
Tokens::If => match expr {
|
||||
Some(_) => panic!("Commands must be ended properly"),
|
||||
None => expr = Some(Expression::IfExpression(self.parse_if(end))),
|
||||
}
|
||||
Tokens::Let => return self.parse_let(end),
|
||||
Tokens::While => return Expression::WhileExpression(self.parse_while(end)),
|
||||
Tokens::StringVariable(_, _) => if matches!(expr, Some(_)) {
|
||||
dbg!(expr);
|
||||
panic!("Unexpected variable. After file redirect, you need to use a semicolon or newline.");
|
||||
} else {
|
||||
expr = Some(self.parse_call(end));
|
||||
dbg!(&self);
|
||||
},
|
||||
Tokens::ArrayVariable(_, _) => panic!("Unexpected array variable"),
|
||||
Tokens::And => panic!("And not yet implemented"),
|
||||
Tokens::Or => panic!("Or not yet implemented"),
|
||||
Tokens::And => match expr {
|
||||
None => panic!("Unexpected AND (&&)"),
|
||||
Some(_) => {
|
||||
self.inc();
|
||||
expr = Some(Expression::AndExpression(AndExpression { first: Box::new(expr.unwrap()), second: Box::new(self.get_expression(end)) }));
|
||||
}
|
||||
},
|
||||
Tokens::Or => match expr {
|
||||
None => panic!("Unexpected OR (||)"),
|
||||
Some(_) => {
|
||||
self.inc();
|
||||
expr = Some(Expression::OrExpression(OrExpression { first: Box::new(expr.unwrap()), second: Box::new(self.get_expression(end)) }));
|
||||
}
|
||||
},
|
||||
Tokens::JobCommandEnd => panic!("Jobs not yet implemented")
|
||||
}
|
||||
if self.i >= end - 1 { break }
|
||||
|
|
@ -454,7 +458,6 @@ impl Tree {
|
|||
}
|
||||
|
||||
pub fn build_tree(tokens: Vec<Tokens>) -> Vec<Expression> {
|
||||
println!("Building tree");
|
||||
let mut expressions: Vec<Expression> = Vec::new();
|
||||
let mut tree = Tree { tokens, i: 0 };
|
||||
loop {
|
||||
|
|
@ -462,7 +465,5 @@ pub fn build_tree(tokens: Vec<Tokens>) -> Vec<Expression> {
|
|||
let val = tree.get_expression(tree.tokens.len());
|
||||
expressions.push(val);
|
||||
}
|
||||
dbg!(&expressions);
|
||||
|
||||
expressions
|
||||
}
|
||||
|
|
|
|||
137
src/parser/exec.rs
Normal file
137
src/parser/exec.rs
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
use std::error::Error;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::io;
|
||||
use std::ops::Deref;
|
||||
use crate::parser::ast::{CommandValue, Expression, LetExpression, RedirectTargetExpression, Value};
|
||||
use crate::parser::vars;
|
||||
use crate::parser::vars::Variable;
|
||||
|
||||
trait ExecExpression {
|
||||
fn exec(self, ctx: &mut vars::Context) -> Option<Command>;
|
||||
}
|
||||
|
||||
trait GetValue {
|
||||
fn get(self, ctx: &mut vars::Context) -> Variable;
|
||||
}
|
||||
|
||||
impl GetValue for CommandValue {
|
||||
fn get(self, ctx: &mut vars::Context) -> Variable {
|
||||
match self {
|
||||
CommandValue::Value(val) => val.get(ctx),
|
||||
CommandValue::Var(_, _) => panic!("Broken executor")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GetValue for Value {
|
||||
fn get(self, ctx: &mut vars::Context) -> Variable {
|
||||
match self {
|
||||
Value::Literal(str) => {
|
||||
Variable::String(str)
|
||||
},
|
||||
Value::Variable(str) => ctx.get_var(&str).unwrap_or(&mut Variable::String(String::from(""))).clone(),
|
||||
Value::ArrayVariable(str) => ctx.get_var(&str).unwrap_or(&mut Variable::Array(Vec::new())).clone(),
|
||||
Value::ArrayFunction(_) => panic!("Not implemented yet"),
|
||||
Value::StringFunction(_) => panic!("Not implemented yet"),
|
||||
Value::Expressions(expressions) => {
|
||||
let mut out = String::new();
|
||||
ctx.add_scope(true);
|
||||
for mut expr in expressions {
|
||||
let res = expr.exec(ctx);
|
||||
match res {
|
||||
None => {},
|
||||
Some(mut cmd) => {
|
||||
out += &*String::from_utf8_lossy(&cmd.output().expect("Failed to read output of command").stdout);
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.pop_scope();
|
||||
Variable::String(out)
|
||||
},
|
||||
Value::Values(vec) => {
|
||||
let mut out = Vec::new();
|
||||
for mut val in vec {
|
||||
out.push(val.get(ctx));
|
||||
}
|
||||
Variable::Array(out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecExpression for Expression {
|
||||
fn exec(self, ctx: &mut vars::Context) -> Option<Command> {
|
||||
match self {
|
||||
Expression::LetExpression(expr) => expr.exec(ctx),
|
||||
Expression::Command(expr) => expr.exec(ctx),
|
||||
Expression::JobCommand(_) => panic!("Not implemented yet"),
|
||||
Expression::Function(_) => panic!("Not implemented yet"),
|
||||
Expression::IfExpression(_) => panic!("Not implemented yet"),
|
||||
Expression::WhileExpression(_) => panic!("Not implemented yet"),
|
||||
Expression::ForExpression(_) => panic!("Not implemented yet"),
|
||||
Expression::RedirectTargetExpression(expr) => expr.exec(ctx),
|
||||
Expression::FileTargetExpression(_) => panic!("Not implemented yet"),
|
||||
Expression::FileSourceExpression(_) => panic!("Not implemented yet"),
|
||||
Expression::Expressions(_) => panic!("Not implemented yet"),
|
||||
Expression::OrExpression(_) => panic!("Not implemented yet"),
|
||||
Expression::AndExpression(_) => panic!("Not implemented yet")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecExpression for LetExpression {
|
||||
fn exec(mut self, ctx: &mut vars::Context) -> Option<Command> {
|
||||
let key = self.key.get(ctx);
|
||||
let val = self.value.get(ctx);
|
||||
ctx.set_var(key.to_string(), val);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecExpression for Vec<CommandValue> {
|
||||
fn exec(mut self, ctx: &mut vars::Context) -> Option<Command> {
|
||||
if self.len() == 0 { panic!("Command with 0 length"); }
|
||||
let mut first = self.remove(0);
|
||||
let command_name = first.get(ctx).to_string();
|
||||
let mut cmd = Command::new(command_name);
|
||||
for mut value in self {
|
||||
cmd.arg(value.get(ctx).to_string());
|
||||
}
|
||||
Some(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecExpression for RedirectTargetExpression {
|
||||
fn exec(mut self, ctx: &mut vars::Context) -> Option<Command> {
|
||||
let mut src = self.source.exec(ctx).unwrap();
|
||||
let mut target = self.target.exec(ctx).unwrap();
|
||||
src.stdout(Stdio::piped());
|
||||
match src.spawn() {
|
||||
Result::Err(e) => { println!("Error executing: {}", e)},
|
||||
Result::Ok(mut res) => {
|
||||
target.stdin(res.stdout.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
Some(target)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec_tree(tree: Vec<Expression>, ctx: &mut vars::Context) {
|
||||
println!("Executing");
|
||||
for mut expression in tree {
|
||||
let mut cmd = expression.exec(ctx);
|
||||
match cmd {
|
||||
None => {},
|
||||
Some(mut cmd) => match cmd.spawn() {
|
||||
Result::Err(e) => {
|
||||
println!("Error executing: {}", e);
|
||||
},
|
||||
Result::Ok(mut res) => {
|
||||
let out = res.wait().unwrap();
|
||||
ctx.set_var(String::from("!"), Variable::I32(out.code().unwrap_or(1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
pub mod vars;
|
||||
pub mod ast;
|
||||
pub mod tokens;
|
||||
mod exec;
|
||||
|
||||
use crate::parser::ast::{build_tree};
|
||||
use crate::parser::exec::exec_tree;
|
||||
use crate::parser::tokens::{tokenize};
|
||||
|
||||
pub fn exec(reader: &mut dyn std::io::BufRead, ctx: vars::Context) {
|
||||
pub fn exec(reader: &mut dyn std::io::BufRead, ctx: &mut vars::Context) {
|
||||
let tokens = tokenize(reader).unwrap();
|
||||
|
||||
dbg!(&tokens);
|
||||
|
|
@ -13,6 +15,8 @@ pub fn exec(reader: &mut dyn std::io::BufRead, ctx: vars::Context) {
|
|||
let expressions = build_tree(tokens);
|
||||
|
||||
dbg!(&expressions);
|
||||
|
||||
exec_tree(expressions, ctx);
|
||||
}
|
||||
|
||||
pub fn escape(str: String) -> String {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
use crate::parser::ast::FunctionDefinitionExpression;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Variable {
|
||||
String(String),
|
||||
I32(i32),
|
||||
|
|
@ -19,7 +19,9 @@ pub enum Variable {
|
|||
impl Variable {
|
||||
pub fn to_string(self: &Self) -> String {
|
||||
match self {
|
||||
Variable::String(var) => String::from(var),
|
||||
Variable::String(var) => {
|
||||
var.clone()
|
||||
},
|
||||
Variable::I32(num) => num.to_string(),
|
||||
Variable::I64(num) => num.to_string(),
|
||||
Variable::I128(num) => num.to_string(),
|
||||
|
|
@ -32,10 +34,16 @@ impl Variable {
|
|||
String::from("[Object object]")
|
||||
},
|
||||
Variable::Array(vars) => {
|
||||
let len = vars.len();
|
||||
if len == 1 { return vars.get(0).unwrap().to_string(); }
|
||||
let mut str = String::new();
|
||||
let mut i = 0;
|
||||
for var in vars {
|
||||
str += &*var.clone().to_string();
|
||||
str += " ";
|
||||
if i < len - 1 {
|
||||
str += " ";
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
str
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue