diff --git a/executable/bellos b/executable/bellos
index 7043c84..d1a313d 100755
Binary files a/executable/bellos and b/executable/bellos differ
diff --git a/src/bellos.rs b/src/bellos.rs
index eb05af7..dfe751b 100644
--- a/src/bellos.rs
+++ b/src/bellos.rs
@@ -14,806 +14,22 @@
// along with this program. If not, see .
#[allow(dead_code)]
-use glob::glob;
-use std::collections::HashMap;
+pub mod Interpreter {
+ mod interpreter;
+ pub use self::interpreter::*;
+}
+
+pub mod Lexer {
+ mod lexer;
+ pub use self::lexer::*;
+}
+
+pub mod Parser {
+ mod parser;
+ pub use self::parser::*;
+}
+pub mod utilities;
use std::env;
-use std::fs::File;
-use std::io::{self, BufRead, Read, Write};
-use std::os::unix::io::AsRawFd;
-use std::process::{Child, Command, Stdio};
-use std::sync::{Arc, Mutex};
-use std::thread;
-
-#[derive(Debug, Clone, PartialEq)]
-enum Token {
- Word(String),
- Assignment,
- Pipe,
- Redirect(String),
- LeftParen,
- RightParen,
- Semicolon,
- NewLine,
- If,
- Then,
- Else,
- Fi,
- While,
- Do,
- Done,
- For,
- In,
- Function,
- Ampersand,
-}
-
-#[derive(Debug, Clone)]
-enum ASTNode {
- Command {
- name: String,
- args: Vec,
- },
- Assignment {
- name: String,
- value: String,
- },
- Pipeline(Vec),
- Redirect {
- node: Box,
- direction: String,
- target: String,
- },
- Block(Vec),
- If {
- condition: Box,
- then_block: Box,
- else_block: Option>,
- },
- While {
- condition: Box,
- block: Box,
- },
- For {
- var: String,
- list: Vec,
- block: Box,
- },
- Function {
- name: String,
- body: Box,
- },
- Background(Box),
-}
-
-struct Lexer {
- input: Vec,
- position: usize,
-}
-
-impl Lexer {
- fn new(input: String) -> Self {
- Lexer {
- input: input.chars().collect(),
- position: 0,
- }
- }
-
- fn next_token(&mut self) -> Option {
- self.skip_whitespace();
-
- if self.position >= self.input.len() {
- return None;
- }
-
- match self.input[self.position] {
- '=' => {
- self.position += 1;
- Some(Token::Assignment)
- }
- '|' => {
- self.position += 1;
- Some(Token::Pipe)
- }
- '>' => {
- self.position += 1;
- Some(Token::Redirect(">".to_string()))
- }
- '<' => {
- self.position += 1;
- Some(Token::Redirect("<".to_string()))
- }
- '(' => {
- self.position += 1;
- Some(Token::LeftParen)
- }
- ')' => {
- self.position += 1;
- Some(Token::RightParen)
- }
- ';' => {
- self.position += 1;
- Some(Token::Semicolon)
- }
- '\n' => {
- self.position += 1;
- Some(Token::NewLine)
- }
- '&' => {
- self.position += 1;
- Some(Token::Ampersand)
- }
- '"' => Some(self.read_string()),
- _ => Some(self.read_word()),
- }
- }
-
- fn skip_whitespace(&mut self) {
- while self.position < self.input.len() && self.input[self.position].is_whitespace() {
- self.position += 1;
- }
- }
-
- fn read_word(&mut self) -> Token {
- let start = self.position;
- while self.position < self.input.len()
- && !self.input[self.position].is_whitespace()
- && !matches!(
- self.input[self.position],
- '=' | '|' | '>' | '<' | '(' | ')' | ';' | '&' | '\n'
- )
- {
- self.position += 1;
- }
- let word: String = self.input[start..self.position].iter().collect();
- match word.as_str() {
- "if" => Token::If,
- "then" => Token::Then,
- "else" => Token::Else,
- "fi" => Token::Fi,
- "while" => Token::While,
- "for" => Token::For,
- "do" => Token::Do,
- "done" => Token::Done,
- "in" => Token::In,
- _ => Token::Word(word),
- }
- }
-
- fn read_string(&mut self) -> Token {
- self.position += 1; // Skip opening quote
- let start = self.position;
- while self.position < self.input.len() && self.input[self.position] != '"' {
- self.position += 1;
- }
- let result = Token::Word(self.input[start..self.position].iter().collect());
- self.position += 1; // Skip closing quote
- result
- }
-}
-
-struct Parser {
- tokens: Vec,
- position: usize,
-}
-
-impl Parser {
- fn new(tokens: Vec) -> Self {
- Parser {
- tokens,
- position: 0,
- }
- }
-
- fn parse(&mut self) -> Result, String> {
- let mut nodes = Vec::new();
- while self.position < self.tokens.len() {
- nodes.push(self.parse_statement()?);
- self.consume_if(Token::Semicolon);
- self.consume_if(Token::NewLine);
- }
- Ok(nodes)
- }
-
- fn parse_statement(&mut self) -> Result {
- match &self.tokens[self.position] {
- Token::Word(_) => self.parse_command_or_assignment(),
- Token::LeftParen => self.parse_block(),
- Token::If => self.parse_if(),
- Token::While => self.parse_while(),
- Token::For => self.parse_for(),
- Token::Function => self.parse_function(),
- _ => Err(format!(
- "Unexpected token: {:?}",
- self.tokens[self.position]
- )),
- }
- }
-
- fn parse_command_or_assignment(&mut self) -> Result {
- let name = match &self.tokens[self.position] {
- Token::Word(w) => w.clone(),
- _ => {
- return Err(format!(
- "Expected word, found {:?}",
- self.tokens[self.position]
- ))
- }
- };
- self.position += 1;
-
- if self.position < self.tokens.len() && self.tokens[self.position] == Token::Assignment {
- self.position += 1;
- let value = match &self.tokens[self.position] {
- Token::Word(w) => w.clone(),
- _ => String::new(), // Allow empty assignments
- };
- if self.position < self.tokens.len() {
- self.position += 1;
- }
- Ok(ASTNode::Assignment { name, value })
- } else {
- let mut args = Vec::new();
- while self.position < self.tokens.len()
- && !matches!(
- self.tokens[self.position],
- Token::Pipe
- | Token::Redirect(_)
- | Token::Semicolon
- | Token::NewLine
- | Token::Ampersand
- | Token::Assignment
- )
- {
- if let Token::Word(w) = &self.tokens[self.position] {
- args.push(w.clone());
- self.position += 1;
- } else {
- break;
- }
- }
- Ok(ASTNode::Command { name, args })
- }
- }
-
- fn parse_pipeline_or_redirect(&mut self, left: ASTNode) -> Result {
- if self.position >= self.tokens.len() {
- return Ok(left);
- }
-
- match &self.tokens[self.position] {
- Token::Pipe => {
- self.position += 1;
- let right = self.parse_command_or_assignment()?;
- let pipeline = ASTNode::Pipeline(vec![left, right]);
- self.parse_pipeline_or_redirect(pipeline)
- }
- Token::Redirect(direction) => {
- self.position += 1;
- let target = match &self.tokens[self.position] {
- Token::Word(w) => w.clone(),
- _ => {
- return Err(format!(
- "Expected word after redirect, found {:?}",
- self.tokens[self.position]
- ))
- }
- };
- self.position += 1;
- let redirect = ASTNode::Redirect {
- node: Box::new(left),
- direction: direction.clone(),
- target,
- };
- self.parse_pipeline_or_redirect(redirect)
- }
- Token::Ampersand => {
- self.position += 1;
- Ok(ASTNode::Background(Box::new(left)))
- }
- _ => Ok(left),
- }
- }
-
- fn parse_if(&mut self) -> Result {
- self.position += 1; // Consume 'if'
- let condition = Box::new(self.parse_command()?);
- self.expect_token(Token::Then)?;
- let then_block = Box::new(self.parse_block()?);
- let else_block = if self.consume_if(Token::Else) {
- Some(Box::new(self.parse_block()?))
- } else {
- None
- };
- self.expect_token(Token::Fi)?;
- Ok(ASTNode::If {
- condition,
- then_block,
- else_block,
- })
- }
-
- fn parse_while(&mut self) -> Result {
- self.position += 1; // Consume 'while'
- let condition = Box::new(self.parse_command()?);
- self.expect_token(Token::Do)?;
- let block = Box::new(self.parse_block()?);
- self.expect_token(Token::Done)?;
- Ok(ASTNode::While { condition, block })
- }
-
- fn parse_for(&mut self) -> Result {
- self.position += 1; // Consume 'for'
- let var = match &self.tokens[self.position] {
- Token::Word(w) => w.clone(),
- _ => return Err("Expected variable name after 'for'".to_string()),
- };
- self.position += 1;
- self.expect_token(Token::In)?;
- let mut list = Vec::new();
- while self.position < self.tokens.len() && self.tokens[self.position] != Token::Do {
- if let Token::Word(w) = &self.tokens[self.position] {
- list.push(w.clone());
- self.position += 1;
- } else {
- break;
- }
- }
- self.expect_token(Token::Do)?;
- let block = Box::new(self.parse_block()?);
- self.expect_token(Token::Done)?;
- Ok(ASTNode::For { var, list, block })
- }
-
- fn parse_block(&mut self) -> Result {
- let mut statements = Vec::new();
- while self.position < self.tokens.len()
- && !matches!(
- self.tokens[self.position],
- Token::Fi | Token::Done | Token::Else
- )
- {
- statements.push(self.parse_statement()?);
- self.consume_if(Token::Semicolon);
- self.consume_if(Token::NewLine);
- }
- Ok(ASTNode::Block(statements))
- }
-
- fn parse_command(&mut self) -> Result {
- let mut args = Vec::new();
- while self.position < self.tokens.len()
- && !matches!(
- self.tokens[self.position],
- Token::Then | Token::Do | Token::Done | Token::Fi | Token::Else
- )
- {
- if let Token::Word(w) = &self.tokens[self.position] {
- args.push(w.clone());
- self.position += 1;
- } else {
- break;
- }
- }
- if args.is_empty() {
- Err("Expected command".to_string())
- } else {
- Ok(ASTNode::Command {
- name: args[0].clone(),
- args: args[1..].to_vec(),
- })
- }
- }
-
- fn parse_function(&mut self) -> Result {
- self.position += 1; // Consume 'function'
- let name = match &self.tokens[self.position] {
- Token::Word(w) => w.clone(),
- _ => {
- return Err(format!(
- "Expected function name, found {:?}",
- self.tokens[self.position]
- ))
- }
- };
- self.position += 1;
- let body = Box::new(self.parse_block()?);
- Ok(ASTNode::Function { name, body })
- }
-
- fn expect_token(&mut self, expected: Token) -> Result<(), String> {
- if self.position < self.tokens.len() && self.tokens[self.position] == expected {
- self.position += 1;
- Ok(())
- } else {
- Err(format!(
- "Expected {:?}, found {:?}",
- expected,
- self.tokens.get(self.position)
- ))
- }
- }
-
- fn consume_if(&mut self, token: Token) -> bool {
- if self.position < self.tokens.len() && self.tokens[self.position] == token {
- self.position += 1;
- true
- } else {
- false
- }
- }
-}
-
-struct Interpreter {
- variables: HashMap,
- functions: HashMap,
- background_jobs: Arc>>,
-}
-
-impl Interpreter {
- fn new() -> Self {
- Interpreter {
- variables: HashMap::new(),
- functions: HashMap::new(),
- background_jobs: Arc::new(Mutex::new(Vec::new())),
- }
- }
-
- fn interpret(&mut self, nodes: Vec) -> Result<(), String> {
- for node in nodes {
- self.interpret_node(Box::new(node))?;
- }
- Ok(())
- }
-
- fn interpret_node(&mut self, node: Box) -> Result