Skip to content

Commit

Permalink
background
Browse files Browse the repository at this point in the history
  • Loading branch information
harryscholes committed Oct 26, 2024
1 parent 5c2302d commit 3ed4c3b
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 22 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A shell implementation in Rust.
- [x] Redirection
- [x] Pipes
- [x] AND and OR
- [ ] Background
- [x] Background
- [ ] Variables
- [ ] Variable substitution
- [ ] Command substitution
2 changes: 1 addition & 1 deletion src/ast.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::grammar::Token;

#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub enum Ast {
Command { command: Token, args: Vec<Token> },
Pipe { left: Box<Ast>, right: Box<Ast> },
Expand Down
22 changes: 13 additions & 9 deletions src/bin/shell.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use std::{
f32::consts::E,
io::{self, BufRead, Write},
};
use std::io::{self, BufRead, Write};

use shell::pipeline::Pipeline;
use shell::{exec::RunningProcess, pipeline::Pipeline};

fn main() {
let mut line = String::new();
Expand All @@ -15,11 +12,18 @@ fn main() {
io::stdin().lock().read_line(&mut line).unwrap();

match Pipeline::run(line.trim()) {
Ok(status) => {
if !status.success() {
eprintln!("Exit status code: {:?}", status.code());
Ok(p) => match p {
RunningProcess::Foreground(status) => {
if !status.success() {
if let Some(code) = status.code() {
eprintln!("Error: {}", code);
}
}
}
}
RunningProcess::Background => {
println!("Background process started");
}
},
Err(e) => eprintln!("Error: {}", e),
}

Expand Down
8 changes: 3 additions & 5 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@ use crate::grammar::Token;

#[derive(thiserror::Error, Debug, PartialEq)]
pub enum Error {
#[error("parse error near `{0}`")]
#[error("parse error near {0}")]
Parse(Token),
}

impl From<Error> for std::io::Error {
fn from(e: Error) -> std::io::Error {
match e {
Error::Parse(token) => {
std::io::Error::new(std::io::ErrorKind::InvalidInput, token.to_string())
}
match &e {
Error::Parse(_) => std::io::Error::new(std::io::ErrorKind::InvalidInput, e),
}
}
}
32 changes: 30 additions & 2 deletions src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,36 @@ use std::{

use crate::{ast::Ast, grammar::Token};

pub fn execute(ast: &Ast) -> io::Result<ExitStatus> {
exec_impl(ast, None, None)?.wait()
pub enum RunningProcess {
Background,
Foreground(ExitStatus),
}

impl RunningProcess {
pub fn success(&self) -> bool {
match self {
RunningProcess::Background => true,
RunningProcess::Foreground(status) => status.success(),
}
}
}

pub fn execute(ast: &Ast) -> io::Result<RunningProcess> {
match ast {
Ast::Background { inner } => {
let inner = inner.clone();
std::thread::spawn(move || {
let mut child = exec_impl(&inner, None, None)?;
child.wait()
});
Ok(RunningProcess::Background)
}
_ => {
let mut child = exec_impl(ast, None, None)?;
let status = child.wait()?;
Ok(RunningProcess::Foreground(status))
}
}
}

fn exec_impl(ast: &Ast, stdin: Option<Stdio>, stdout: Option<Stdio>) -> io::Result<Child> {
Expand Down
22 changes: 21 additions & 1 deletion src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub enum Token {

impl std::fmt::Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self.as_ref())
write!(f, "{}", self.as_ref().to_string_lossy())
}
}

Expand All @@ -40,3 +40,23 @@ impl AsRef<OsStr> for Token {
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_token_display() {
assert_eq!(Token::Input("foo".to_string()).to_string(), "foo");
assert_eq!(Token::Pipe.to_string(), "|");
assert_eq!(Token::RedirectOut.to_string(), ">");
assert_eq!(Token::RedirectAppend.to_string(), ">>");
assert_eq!(Token::RedirectIn.to_string(), "<");
assert_eq!(Token::And.to_string(), "&&");
assert_eq!(Token::Or.to_string(), "||");
assert_eq!(Token::Background.to_string(), "&");
assert_eq!(Token::Semicolon.to_string(), ";");
assert_eq!(Token::OpenParenthesis.to_string(), "(");
assert_eq!(Token::CloseParenthesis.to_string(), ")");
}
}
10 changes: 7 additions & 3 deletions src/pipeline.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use std::{io, process::ExitStatus};
use std::io;

use crate::{exec::execute, lex::Lexer, parse::Parser};
use crate::{
exec::{execute, RunningProcess},
lex::Lexer,
parse::Parser,
};

pub struct Pipeline;

impl Pipeline {
pub fn run(input: &str) -> io::Result<ExitStatus> {
pub fn run(input: &str) -> io::Result<RunningProcess> {
let tokens = Lexer::lex(input)?;
let ast = Parser::parse(&tokens)?;
execute(&ast)
Expand Down

0 comments on commit 3ed4c3b

Please sign in to comment.