diff --git a/src/main.rs b/src/main.rs index 8624fe9..86e6707 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,16 @@ mod parser; -use std::io::{self, BufRead, Read, Stdout, Write}; +use std::io::{self, BufRead, Stdout, Write}; use std::cmp; use std::collections::HashMap; use std::convert::TryInto; -use std::ops::Add; use std::process; use termion::raw::{IntoRawMode, RawTerminal}; use termion::input::TermRead; -use termion::cursor::{self, DetectCursorPos}; +use termion::cursor::{DetectCursorPos}; use termion::event::*; -use termion::input::{MouseTerminal}; +// use termion::input::{MouseTerminal}; struct Term { input: String, @@ -72,6 +71,8 @@ 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); diff --git a/src/parser/ast.rs b/src/parser/ast.rs index c07bf4c..f0164a5 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -1,12 +1,23 @@ use crate::parser::tokens::Tokens; -use crate::parser::vars::Variable; #[derive(Debug)] pub struct LetExpression { - pub key: String, + pub key: Box, pub value: Box } +#[derive(Debug)] +pub struct AndExpression { + pub first: Box, + pub second: Box +} + +#[derive(Debug)] +pub struct OrExpression { + pub first: Box, + pub second: Box +} + #[derive(Debug)] pub struct IfExpression { pub condition: Box, @@ -47,6 +58,7 @@ pub enum Value { ArrayFunction(DefinedFunction), StringFunction(DefinedFunction), Expressions(Vec), + Expression(Expression), Values(Vec) } @@ -84,108 +96,197 @@ pub enum CommandValue { pub enum Expression { LetExpression(LetExpression), Command(Vec), + JobCommand(Box), Function(FunctionDefinitionExpression), IfExpression(IfExpression), WhileExpression(WhileExpression), ForExpression(ForExpression), RedirectTargetExpression(RedirectTargetExpression), FileTargetExpression(FileTargetExpression), - FileSourceExpression(FileSourceExpression) + FileSourceExpression(FileSourceExpression), + Expressions(Vec), + OrExpression(OrExpression), + AndExpression(AndExpression) } +#[derive(Debug)] struct Tree { tokens: Vec, i: usize } impl Tree { - fn parse_call(&mut self) -> Vec { + fn parse_call(&mut self, end: usize) -> Expression { let mut values: Vec = Vec::new(); - let mut token = self.get_current_token(); let mut buf: Vec = Vec::new(); - + dbg!("call parse"); + let mut token = self.get_current_token(); loop { if matches!(token, Tokens::Space) { if buf.len() > 0 { values.push(CommandValue::Value(Value::Values(buf))); buf = Vec::new(); } - token = self.next(); + if self.i >= end - 1 { break } + self.i += 1; + token = self.get_current_token(); + dbg!("skip space"); continue; } - let val = match token { + let val = match &token { Tokens::Literal(str) => Value::Literal(str.clone()), - Tokens::SubStart => Value::Expressions(self.parse_sub()), + Tokens::SubStart => { + let val = self.get_value(end); + token = self.get_current_token(); + val + }, Tokens::StringVariable(str, _) => Value::Variable(str.clone()), Tokens::ArrayVariable(str, _) => Value::ArrayVariable(str.clone()), - Tokens::FileWrite => { - + Tokens::FileWrite => break, + Tokens::FileRead => break, + Tokens::RedirectInto => break, + Tokens::ParenthesisEnd => { + if self.i >= end - 1 { + break; + } + Value::Literal(token.to_str()) } _ => { Value::Literal(token.to_str()) } }; buf.push(val); - if self.i == self.tokens.len() { break } - token = self.next(); + if self.i >= end - 1 { break } + 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))); } - values + Expression::Command(values) } - fn parse_let(&self) -> LetExpression { - panic!("Let not yet implemented"); + fn parse_let(&mut self, end: usize) -> Expression { + if end < self.i + 2 { panic!("Let needs name and equal sign (=) at minimum") } + self.inc(); + let mut len = 0; + for token in &self.tokens[self.i..] { + match token { + Tokens::ExportSet => { break }, + _ => len += 1 + } + } + let key = Box::new(self.get_value(self.i + len)); + self.inc(); // ???? + self.inc(); + let value = Box::new(self.get_value(end)); + Expression::LetExpression(LetExpression { key, value }) } - fn parse_read(&self) -> FileSourceExpression { - panic!("Read not yet implemented"); + fn parse_read(&mut self, target: Option, end: usize) -> Expression { + let target = match target { + Some(source) => Some(Box::new(source)), + None => None + }; + self.i += 1; + 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 }, + Tokens::CommandEnd(_) => if !found_first { panic!("Unexpected command end") } else { break }, + Tokens::FileRead => panic!("Unexpected file read (<)"), + Tokens::FileWrite => panic!("Unexpected file write (>)"), + _ => { found_first = true; } + } + } + dbg!(&self.tokens[self.i..val_end]); + val_end -= 1; + let source = Box::new(self.get_value(val_end)); + self.inc(); + Expression::FileSourceExpression(FileSourceExpression { source, target }) } - fn parse_write(&self, source: Option) -> FileTargetExpression { + fn parse_write(&mut self, source: Option, end: usize) -> Expression { let source = match source { Some(source) => Some(Box::new(source)), None => None }; - + self.i += 1; + let mut val_end = self.i; + let mut found_first = false; + for token in &self.tokens[self.i..] { + val_end += 1; + match token { + Tokens::Space => if found_first { break }, + Tokens::CommandEnd(_) => if !found_first { panic!("Unexpected command end") } else { break }, + Tokens::FileRead => panic!("Unexpected file read (<)"), + Tokens::FileWrite => panic!("Unexpected file write (>)"), + _ => { found_first = true; } + } + } + val_end -= 1; + let target = Box::new(self.get_value(val_end)); + self.inc(); + Expression::FileTargetExpression(FileTargetExpression { source, target }) } - fn parse_function(&self) -> FunctionDefinitionExpression { + fn parse_function(&self, end: usize) -> FunctionDefinitionExpression { panic!("Functions not yet implemented") } - fn parse_array_func(&self, str: String) -> DefinedFunction { + fn parse_array_func(&self, str: String, end: usize) -> DefinedFunction { panic!("Array functions not yet implemented"); } - fn parse_for(&self) -> ForExpression { + fn parse_string_func(&self, str: String, end: usize) -> DefinedFunction { + panic!("Array functions not yet implemented"); + } + + fn parse_for(&self, end: usize) -> ForExpression { panic!("For loop not yet implemented"); } - fn parse_if(&self) -> IfExpression { + fn parse_if(&self, end: usize) -> IfExpression { panic!("If not yet implemented"); } - fn parse_while(&self) -> WhileExpression { + fn parse_while(&self, end: usize) -> WhileExpression { panic!("While not yet implemented"); } - fn parse_sub(&self) -> Vec { - panic!("Sub not yet implemented") + fn parse_sub(&mut self, end: usize) -> Vec { + let mut expressions: Vec = Vec::new(); + loop { + if self.i >= end - 1 { break; } + expressions.push(self.get_expression(end)); + dbg!(&expressions); + } + expressions } - fn get_value(&mut self) -> Value { + fn get_value(&mut self, end: usize) -> Value { let mut token = self.get_current_token(); let mut values: Vec = Vec::new(); let mut buf: Vec = Vec::new(); loop { match token { Tokens::Space => { - if buf.len() == 0 { continue; } + if buf.len() == 0 { token = self.inc().get_current_token(); continue; } values.push(Value::Values(buf)); buf = Vec::new(); + if self.i >= end - 1 { break } }, Tokens::CommandEnd(_) => break, Tokens::Literal(str) => buf.push(Value::Literal(str.clone())), @@ -194,57 +295,155 @@ impl Tree { Tokens::Function => buf.push(Value::Literal(token.to_str())), Tokens::FileWrite => buf.push(Value::Literal(token.to_str())), Tokens::RedirectInto => panic!("Unexpected token REDIRECT (|)"), - Tokens::FunctionCallEnd => panic!("Unexpected token FUNCTION CALL END ())"), + Tokens::ParenthesisEnd => panic!("Unexpected token FUNCTION CALL END ())"), Tokens::ArrayFunction(_) => panic!("Unexpected array function"), Tokens::StringFunction(_) => panic!("Unexpected string function"), - Tokens::SubStart => /* SUB START PROCESS */, - Tokens::SubEnd => panic!("Unexpected token SUB END ())"), + Tokens::ParenthesisStart => panic!("Parenthesis not yet implemented"), + Tokens::SubStart => { + let mut len = 1; + let mut lvl = 1; + self.inc(); + for token in &self.tokens[self.i..] { + match token { + Tokens::SubStart => lvl += 1, + Tokens::ParenthesisEnd => lvl -= 1, + _ => {} + } + if lvl == 0 { + break; + } + + if len + self.i == end { break } + len += 1; + } + // self.inc(); + dbg!(&self); + dbg!(len, lvl); + if lvl != 0 { + panic!("Sub not ended properly"); + } + return Value::Expressions(self.parse_sub(self.i + len)); + }, Tokens::Else => buf.push(Value::Literal(token.to_str())), Tokens::End => buf.push(Value::Literal(token.to_str())), - Tokens::For => return Expression::ForExpression(self.parse_for()), - Tokens::If => return Expression::IfExpression(self.parse_if()), - Tokens::Let => return Expression::LetExpression(self.parse_let()), - Tokens::While => return Expression::WhileExpression(self.parse_while()), - Tokens::StringVariable(_, _) => return Expression::Command(self.parse_call()), - Tokens::ArrayVariable(_, _) => panic!("Unexpected array variable") + Tokens::For => buf.push(Value::Literal(token.to_str())), + Tokens::If => buf.push(Value::Literal(token.to_str())), + Tokens::Let => buf.push(Value::Literal(token.to_str())), + Tokens::While => buf.push(Value::Literal(token.to_str())), + Tokens::StringVariable(str, _) => { + if buf.len() != 0 { + values.push(Value::Values(buf)); + buf = Vec::new(); + } + values.push(Value::Variable(str.clone())); + }, + Tokens::ArrayVariable(str, _) => { + if buf.len() != 0 { + values.push(Value::Values(buf)); + buf = Vec::new(); + } + values.push(Value::ArrayVariable(str.clone())); + }, + Tokens::And => panic!("Unexpected AND (&&)"), + Tokens::Or => panic!("Unexpected OR (||)"), + Tokens::JobCommandEnd => panic!("Unexpected job command end (&)"), } - token = self.next(); + if self.i >= end - 1 { break } + token = self.inc().get_current_token(); } + if buf.len() > 0 { + values.push(Value::Values(buf)); + } + Value::Values(values) } - fn get_expression(&mut self, is_sub: bool) -> Expression { + fn get_expression(&mut self, end: usize) -> Expression { let mut token = self.get_current_token(); + dbg!("expr"); + let mut expr: Option = None; loop { + dbg!(&token); match token { - Tokens::Space => continue, - Tokens::CommandEnd(_) => continue, - Tokens::Literal(_) => return Expression::Command(self.parse_call()), + Tokens::Space => {self.inc();}, + Tokens::CommandEnd(_) => {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::FileRead => return Expression::FileSourceExpression(self.parse_read()), - Tokens::Function => return Expression::Function(self.parse_function()), - Tokens::FileWrite => return Expression::FileTargetExpression(self.parse_write(None)), - Tokens::RedirectInto => panic!("Unexpected token REDIRECT (|)"), - Tokens::FunctionCallEnd => panic!("Unexpected token FUNCTION CALL END ())"), + Tokens::Function => return Expression::Function(self.parse_function(end)), + Tokens::FileRead => expr = Some(self.parse_read(expr, end)), + Tokens::FileWrite => expr = Some(self.parse_write(expr, end)), + Tokens::RedirectInto => match expr { + 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; + let mut lvl = 1; + self.inc(); + for token in &self.tokens[self.i..] { + match token { + Tokens::ParenthesisStart => lvl += 1, + Tokens::ParenthesisEnd => lvl -= 1, + _ => {} + } + if lvl == 0 { + break; + } + + if len + self.i == end { break } + len += 1; + } + if lvl != 0 { + panic!("Parenthesis not ended properly."); + } + expr = Some(self.get_expression(self.i + len)); + dbg!(&self); + }, + Tokens::ParenthesisEnd => panic!("Unexpected token PARENTHESIS END ())"), Tokens::ArrayFunction(_) => panic!("Unexpected array function"), Tokens::StringFunction(_) => panic!("Unexpected string function"), - Tokens::SubStart => return Expression::Command(self.parse_call()), - Tokens::SubEnd => if !is_sub { panic!("Unexpected token SUB END ())") }, + Tokens::SubStart => return self.parse_call(end), Tokens::Else => panic!("Unexpected token ELSE"), Tokens::End => panic!("Unexpected token END"), - Tokens::For => return Expression::ForExpression(self.parse_for()), - Tokens::If => return Expression::IfExpression(self.parse_if()), - Tokens::Let => return Expression::LetExpression(self.parse_let()), - Tokens::While => return Expression::WhileExpression(self.parse_while()), - Tokens::StringVariable(_, _) => return Expression::Command(self.parse_call()), - Tokens::ArrayVariable(_, _) => panic!("Unexpected array variable") + Tokens::For => return Expression::ForExpression(self.parse_for(end)), + Tokens::If => return 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::JobCommandEnd => panic!("Jobs not yet implemented") } - token = self.next(); + if self.i >= end - 1 { break } + token = self.get_current_token(); } + expr.unwrap() } - fn next(&mut self) -> &Tokens { + fn inc(&mut self) -> &Self { self.i += 1; - self.get_current_token() + self } fn get_current_token(&self) -> &Tokens { self.tokens.get(self.i).unwrap() @@ -255,12 +454,15 @@ impl Tree { } pub fn build_tree(tokens: Vec) -> Vec { + println!("Building tree"); let mut expressions: Vec = Vec::new(); let mut tree = Tree { tokens, i: 0 }; loop { if tree.i == tree.tokens.len() - 1 { break; } - expressions.push(tree.get_expression()); + let val = tree.get_expression(tree.tokens.len()); + expressions.push(val); } + dbg!(&expressions); expressions -} \ No newline at end of file +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index a1f8df1..6aa6280 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2,19 +2,14 @@ pub mod vars; pub mod ast; pub mod tokens; -use std::io; -use std::io::Read; -//use std::io::prelude::*; -use utf8_chars::BufReadCharsExt; -use crate::parser::ast::{build_tree, Expression}; -use crate::parser::tokens::{tokenize, Tokens}; +use crate::parser::ast::{build_tree}; +use crate::parser::tokens::{tokenize}; pub fn exec(reader: &mut dyn std::io::BufRead, ctx: vars::Context) { let tokens = tokenize(reader).unwrap(); dbg!(&tokens); - println!("Building tree"); let expressions = build_tree(tokens); dbg!(&expressions); @@ -22,4 +17,4 @@ pub fn exec(reader: &mut dyn std::io::BufRead, ctx: vars::Context) { pub fn escape(str: String) -> String { str -} \ No newline at end of file +} diff --git a/src/parser/tokens.rs b/src/parser/tokens.rs index 22f9bbf..ed43a3c 100644 --- a/src/parser/tokens.rs +++ b/src/parser/tokens.rs @@ -1,6 +1,4 @@ -use std::fmt::format; use std::io; -use crate::parser::vars; #[derive(Debug)] pub enum Tokens { @@ -12,7 +10,8 @@ pub enum Tokens { ArrayVariable(String, bool), ArrayFunction(String), StringFunction(String), - FunctionCallEnd, + ParenthesisStart, + ParenthesisEnd, CommandEnd(char), If, Else, @@ -21,10 +20,12 @@ pub enum Tokens { Function, End, SubStart, - SubEnd, RedirectInto, FileRead, FileWrite, + And, + Or, + JobCommandEnd } impl Tokens { @@ -38,7 +39,8 @@ impl Tokens { "else" => Tokens::Else, "end" => Tokens::End, "$(" => Tokens::SubStart, - ")" => Tokens::SubEnd, + "(" => Tokens::ParenthesisStart, + ")" => Tokens::ParenthesisEnd, ">" => Tokens::FileWrite, "<" => Tokens::FileRead, "|" => Tokens::RedirectInto, @@ -59,7 +61,6 @@ impl Tokens { Tokens::StringFunction(str) => format!("${}", str.as_str()), Tokens::CommandEnd(str) => str.to_string(), Tokens::ExportSet => "=".to_string(), - Tokens::FunctionCallEnd => ")".to_string(), Tokens::Function => "function".to_string(), Tokens::If => "if".to_string(), Tokens::Else => "else".to_string(), @@ -67,10 +68,14 @@ impl Tokens { Tokens::For => "for".to_string(), Tokens::End => "end".to_string(), Tokens::SubStart => "$(".to_string(), - Tokens::SubEnd => ")".to_string(), + Tokens::ParenthesisStart => "(".to_string(), + Tokens::ParenthesisEnd => ")".to_string(), Tokens::RedirectInto => "|".to_string(), Tokens::FileRead => "<".to_string(), - Tokens::FileWrite => ">".to_string() + Tokens::FileWrite => ">".to_string(), + Tokens::And => "&&".to_string(), + Tokens::Or => "||".to_string(), + Tokens::JobCommandEnd => "&".to_string() } } } @@ -84,7 +89,7 @@ fn read_var_ahead(i: usize, text: &String) -> (usize, Tokens) { x += 1; let letter: char = text.chars().nth(x).unwrap(); match letter { - 'a'...'z' | 'A'...'Z' | '0'...'9' | ':' => { + 'a'..='z' | 'A'..='Z' | '0'..='9' | ':' | '_' => { buf.push(letter.clone()); } '}' => { @@ -93,7 +98,7 @@ fn read_var_ahead(i: usize, text: &String) -> (usize, Tokens) { } break; } - _ => { if !parens_mode { break } } + _ => { if !parens_mode { break } else { panic!("Invalid variable name") } } } } let token = match text.chars().nth(i).unwrap() { @@ -174,15 +179,23 @@ pub fn tokenize(reader: &mut dyn std::io::BufRead) -> io::Result> { skipper = x - i - 1; buf_add = false; }, - ')' => if !quote_active && !double_quote_active { + '(' => if !quote_active && !double_quote_active && !escape_active { if buf.len() > 0 { tokens.push(Tokens::Literal(buf)); buf = String::new(); } - tokens.push(Tokens::FunctionCallEnd); + tokens.push(Tokens::ParenthesisStart); + buf_add = false; + } + ')' => if !quote_active && !double_quote_active && !escape_active { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + tokens.push(Tokens::ParenthesisEnd); buf_add = false; }, - 'i' => if !quote_active && !double_quote_active { + 'i' => if !quote_active && !double_quote_active && !escape_active { if check_keyword(&text, i, "if") { if buf.len() > 0 { tokens.push(Tokens::Literal(buf)); @@ -193,7 +206,7 @@ pub fn tokenize(reader: &mut dyn std::io::BufRead) -> io::Result> { buf_add = false; } }, - 'e' => if !quote_active && !double_quote_active { + 'e' => if !quote_active && !double_quote_active && !escape_active { if check_keyword(&text, i, "else") { if buf.len() > 0 { tokens.push(Tokens::Literal(buf)); @@ -212,7 +225,18 @@ pub fn tokenize(reader: &mut dyn std::io::BufRead) -> io::Result> { buf_add = false; } }, - 'w' => if !quote_active && !double_quote_active { + 'l' => if !quote_active && !double_quote_active && !escape_active { + if check_keyword(&text, i, "let") { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + skipper = 3; + tokens.push(Tokens::Let); + buf_add = false; + } + }, + 'w' => if !quote_active && !double_quote_active && !escape_active { if check_keyword(&text, i, "while") { if buf.len() > 0 { tokens.push(Tokens::Literal(buf)); @@ -223,7 +247,7 @@ pub fn tokenize(reader: &mut dyn std::io::BufRead) -> io::Result> { buf_add = false; } }, - 'f' => if !quote_active && !double_quote_active { + 'f' => if !quote_active && !double_quote_active && !escape_active { if check_keyword(&text, i, "for") { if buf.len() > 0 { tokens.push(Tokens::Literal(buf)); @@ -239,7 +263,25 @@ pub fn tokenize(reader: &mut dyn std::io::BufRead) -> io::Result> { tokens.push(Tokens::Literal(buf)); buf = String::new(); } - tokens.push(Tokens::RedirectInto); + if check_keyword(&text, i, "||") { + skipper = 1; + tokens.push(Tokens::Or); + } else { + tokens.push(Tokens::RedirectInto); + } + buf_add = false; + }, + '&' => if !escape_active && !quote_active && !double_quote_active { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + if check_keyword(&text, i, "&&") { + skipper = 1; + tokens.push(Tokens::And); + } else { + tokens.push(Tokens::JobCommandEnd); + } buf_add = false; }, '>' => if !escape_active && !quote_active && !double_quote_active { @@ -292,4 +334,4 @@ pub fn tokenize(reader: &mut dyn std::io::BufRead) -> io::Result> { } Ok(tokens) -} \ No newline at end of file +} diff --git a/src/parser/vars.rs b/src/parser/vars.rs index 236dc81..9611a23 100644 --- a/src/parser/vars.rs +++ b/src/parser/vars.rs @@ -1,7 +1,5 @@ use std::collections::HashMap; -use std::ops::{Add, Deref}; use crate::parser::ast::FunctionDefinitionExpression; -use crate::parser::escape; #[derive(Debug)] pub enum Variable { @@ -30,7 +28,7 @@ impl Variable { Variable::U128(num) => num.to_string(), Variable::F32(num) => num.to_string(), Variable::F64(num) => num.to_string(), - Variable::HMap(map) => { + Variable::HMap(_map) => { String::from("[Object object]") }, Variable::Array(vars) => { @@ -122,4 +120,4 @@ impl Context { let mut func = &mut self.scopes.last_mut().unwrap().func; func.insert(key, val); } -} \ No newline at end of file +}