diff --git a/.idea/git_toolbox_prj.xml b/.idea/git_toolbox_prj.xml new file mode 100644 index 0000000..8fb785b --- /dev/null +++ b/.idea/git_toolbox_prj.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 8332f5d..7fed5f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,17 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "filedescriptor" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed3d8a5e20435ff00469e51a0d82049bae66504b5c429920dadf9bb54d47b3f" +dependencies = [ + "libc", + "thiserror", + "winapi", +] + [[package]] name = "libc" version = "0.2.107" @@ -26,6 +37,24 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" +[[package]] +name = "proc-macro2" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + [[package]] name = "redox_syscall" version = "0.2.10" @@ -48,10 +77,22 @@ dependencies = [ name = "rush" version = "0.1.0" dependencies = [ + "filedescriptor", "termion", "utf8-chars", ] +[[package]] +name = "syn" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "termion" version = "1.5.6" @@ -64,6 +105,32 @@ dependencies = [ "redox_termios", ] +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + [[package]] name = "utf8-chars" version = "1.0.2" @@ -72,3 +139,25 @@ checksum = "c1348d8face79d019be7cbc0198e36bf93e160ddbfaa7bb54c9592627b9ec841" dependencies = [ "arrayvec", ] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 576cd1f..9df853f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,5 @@ edition = "2018" [dependencies] utf8-chars = "1.0.0" -termion = "1.5.6" \ No newline at end of file +termion = "1.5.6" +filedescriptor = "0.8.1" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 4e43920..8624fe9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ mod parser; -use std::io::{self, Read, Stdout, Write}; +use std::io::{self, BufRead, Read, Stdout, Write}; use std::cmp; +use std::collections::HashMap; use std::convert::TryInto; use std::ops::Add; use std::process; @@ -62,7 +63,8 @@ impl Shell { term: Term::new(), ctx: parser::vars::Context { scopes: Vec::new(), - parent_context: None, + fd: Vec::new(), + exports: HashMap::new() }, }; } @@ -70,17 +72,24 @@ impl Shell { fn main() { loop { - let mut shell = editor(); + let mut shell = collect(); shell.term.input += "\n"; parser::exec(&mut shell.term.input.as_bytes(), shell.ctx); } } +fn collect() -> Shell { + let mut shell = Shell::new(); + let stdin = std::io::stdin(); + let v = stdin.lock().lines().next().unwrap().unwrap(); + shell.term.input = v; + shell +} + fn editor() -> Shell { let stdin = io::stdin(); let mut stdout = io::stdout().into_raw_mode().unwrap(); let mut shell = Shell::new(); - let mut should_execute = true; for c in stdin.keys() { let c = c.unwrap(); match c { @@ -134,6 +143,6 @@ fn editor() -> Shell { shell.term.print(&mut stdout); stdout.flush().unwrap(); } - stdout.suspend_raw_mode(); + stdout.suspend_raw_mode().unwrap(); shell } diff --git a/src/parser/ast.rs b/src/parser/ast.rs index ff22cac..c07bf4c 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -1,59 +1,266 @@ +use crate::parser::tokens::Tokens; use crate::parser::vars::Variable; #[derive(Debug)] pub struct LetExpression { pub key: String, - pub value: Value + pub value: Box } #[derive(Debug)] pub struct IfExpression { - pub condition: Expression, + pub condition: Box, pub contents: Vec } #[derive(Debug)] pub struct WhileExpression { - pub condition: Expression, + pub condition: Box, pub contents: Vec } #[derive(Debug)] pub struct ForExpression { pub key: String, - pub list: Value, + pub list: Box, pub contents: Vec } #[derive(Debug)] pub enum ForValue { Value(Value), - Range(Some(u32), Some(u32)) + Range(Option, Option) +} + +#[derive(Debug)] +pub struct DefinedFunction { + name: String, + args: Vec, + body: Vec } #[derive(Debug)] pub enum Value { Literal(String), - Variable(Variable), - Expression(Expression) + Variable(String), + ArrayVariable(String), + ArrayFunction(DefinedFunction), + StringFunction(DefinedFunction), + Expressions(Vec), + Values(Vec) } #[derive(Debug)] -pub struct FunctionExpression { - pub args: Vec, - pub body: Expression +pub struct FunctionDefinitionExpression { + pub args: Vec, + pub body: Box } +#[derive(Debug)] pub struct RedirectTargetExpression { + pub source: Box, + pub target: Box +} +#[derive(Debug)] +pub struct FileTargetExpression { + pub source: Option>, + pub target: Box +} + +#[derive(Debug)] +pub struct FileSourceExpression { + pub source: Box, + pub target: Option> +} + +#[derive(Debug)] +pub enum CommandValue { + Value(Value), + Var(String, Value) } #[derive(Debug)] pub enum Expression { LetExpression(LetExpression), - Command(Vec), - Function(FunctionExpression), + Command(Vec), + Function(FunctionDefinitionExpression), IfExpression(IfExpression), WhileExpression(WhileExpression), - ForExpression(ForExpression) + ForExpression(ForExpression), + RedirectTargetExpression(RedirectTargetExpression), + FileTargetExpression(FileTargetExpression), + FileSourceExpression(FileSourceExpression) +} + +struct Tree { + tokens: Vec, + i: usize +} + +impl Tree { + fn parse_call(&mut self) -> Vec { + let mut values: Vec = Vec::new(); + let mut token = self.get_current_token(); + let mut buf: Vec = Vec::new(); + + loop { + if matches!(token, Tokens::Space) { + if buf.len() > 0 { + values.push(CommandValue::Value(Value::Values(buf))); + buf = Vec::new(); + } + token = self.next(); + continue; + } + let val = match token { + Tokens::Literal(str) => Value::Literal(str.clone()), + Tokens::SubStart => Value::Expressions(self.parse_sub()), + Tokens::StringVariable(str, _) => Value::Variable(str.clone()), + Tokens::ArrayVariable(str, _) => Value::ArrayVariable(str.clone()), + Tokens::FileWrite => { + + } + _ => { + Value::Literal(token.to_str()) + } + }; + buf.push(val); + if self.i == self.tokens.len() { break } + token = self.next(); + if matches!(token, Tokens::CommandEnd(_)) { break } + } + if buf.len() > 0 { + values.push(CommandValue::Value(Value::Values(buf))); + } + values + } + + fn parse_let(&self) -> LetExpression { + panic!("Let not yet implemented"); + } + + fn parse_read(&self) -> FileSourceExpression { + panic!("Read not yet implemented"); + } + + fn parse_write(&self, source: Option) -> FileTargetExpression { + let source = match source { + Some(source) => Some(Box::new(source)), + None => None + }; + + } + + fn parse_function(&self) -> FunctionDefinitionExpression { + panic!("Functions not yet implemented") + } + + fn parse_array_func(&self, str: String) -> DefinedFunction { + panic!("Array functions not yet implemented"); + } + + fn parse_for(&self) -> ForExpression { + panic!("For loop not yet implemented"); + } + + fn parse_if(&self) -> IfExpression { + panic!("If not yet implemented"); + } + + fn parse_while(&self) -> WhileExpression { + panic!("While not yet implemented"); + } + + fn parse_sub(&self) -> Vec { + panic!("Sub not yet implemented") + } + + fn get_value(&mut self) -> 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; } + values.push(Value::Values(buf)); + buf = Vec::new(); + }, + Tokens::CommandEnd(_) => break, + Tokens::Literal(str) => buf.push(Value::Literal(str.clone())), + Tokens::ExportSet => panic!("Unexpected token EXPORT_SET (=)"), + Tokens::FileRead => buf.push(Value::Literal(token.to_str())), + 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::ArrayFunction(_) => panic!("Unexpected array function"), + Tokens::StringFunction(_) => panic!("Unexpected string function"), + Tokens::SubStart => /* SUB START PROCESS */, + Tokens::SubEnd => panic!("Unexpected token SUB END ())"), + 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") + } + token = self.next(); + } + } + + fn get_expression(&mut self, is_sub: bool) -> Expression { + let mut token = self.get_current_token(); + loop { + match token { + Tokens::Space => continue, + Tokens::CommandEnd(_) => continue, + Tokens::Literal(_) => return Expression::Command(self.parse_call()), + 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::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::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") + } + token = self.next(); + } + } + + fn next(&mut self) -> &Tokens { + self.i += 1; + self.get_current_token() + } + fn get_current_token(&self) -> &Tokens { + self.tokens.get(self.i).unwrap() + } + fn get_next_token(&self) -> &Tokens { + self.tokens.get(self.i + 1).unwrap() + } +} + +pub fn build_tree(tokens: Vec) -> Vec { + 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()); + } + + expressions } \ No newline at end of file diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 2bd790c..a1f8df1 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,94 +1,23 @@ 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}; -pub fn exec(reader: &mut dyn std::io::BufRead, ctx: vars::Context) -> io::Result<()> { - println!("got result"); - let mut word = String::new(); - let mut if_active = false; - let mut quote_active = false; - let mut double_quote_active = false; - let mut var_active = false; - let mut var_parens_active = false; - let mut command = String::new(); - let mut args: Vec = Vec::new(); - let mut var_name = String::new(); - let mut prev_char: Option = None; +pub fn exec(reader: &mut dyn std::io::BufRead, ctx: vars::Context) { + let tokens = tokenize(reader).unwrap(); - for c in reader.chars().map(|x| x.unwrap()) { - print!("{}\r", c); - // loop runs once, allows early exit - loop { - match c { - '$' => { - if quote_active { - word.push('$'); - } - var_active = true; - } - '{' => { - if var_active { - var_parens_active = true; - } else { - word.push('{'); - } - } - '}' => { - if !var_parens_active { - word.push('}'); - } - } - ' ' => { - if quote_active || double_quote_active { - word.push(' '); - break; - } - if var_parens_active { break; } - if var_active {} - if command.len() == 0 { - if word == "if" { - if_active = true; - } else if word == "end" { - ctx.pop_scope(); - } else { - command = word; - word = String::new(); - } - } else { - args.push(word.clone()); - word = String::new(); - } - } - ';' | '\n' => { - let mut arg_strings: Vec<&str> = Vec::new(); - for arg in &args { - arg_strings.push(&*arg); - } - println!("Will execute {} with {}", command, arg_strings.join(" ")); - if if_active { - println!("Will execute below only if the previous command succeeded"); - } - if_active = false; + dbg!(&tokens); - let exit_code = 0; - ctx.add_scope(exit_code == 0); - } - char => { - if var_active { - var_name.push(char); - } else { - word.push(char); - } - } - }; - break; - } - prev_char = Some(c); - } - Ok(()) + println!("Building tree"); + let expressions = build_tree(tokens); + + dbg!(&expressions); } pub fn escape(str: String) -> String { diff --git a/src/parser/tokens.rs b/src/parser/tokens.rs new file mode 100644 index 0000000..22f9bbf --- /dev/null +++ b/src/parser/tokens.rs @@ -0,0 +1,295 @@ +use std::fmt::format; +use std::io; +use crate::parser::vars; + +#[derive(Debug)] +pub enum Tokens { + Space, + Literal(String), + Let, + ExportSet, + StringVariable(String, bool), + ArrayVariable(String, bool), + ArrayFunction(String), + StringFunction(String), + FunctionCallEnd, + CommandEnd(char), + If, + Else, + While, + For, + Function, + End, + SubStart, + SubEnd, + RedirectInto, + FileRead, + FileWrite, +} + +impl Tokens { + fn detect(str: String) -> Tokens { + match str.as_str() { + "if" => Tokens::If, + "while" => Tokens::While, + "for" => Tokens::For, + "let" => Tokens::Let, + " " => Tokens::Space, + "else" => Tokens::Else, + "end" => Tokens::End, + "$(" => Tokens::SubStart, + ")" => Tokens::SubEnd, + ">" => Tokens::FileWrite, + "<" => Tokens::FileRead, + "|" => Tokens::RedirectInto, + "\n" | ";" => Tokens::CommandEnd(str.chars().nth(0).unwrap()), + "=" => Tokens::ExportSet, + _ => Tokens::Literal(str) + } + } + + pub(crate) fn to_str(&self) -> String { + match self { + Tokens::Space => " ".to_string(), + Tokens::Literal(str) => str.clone(), + Tokens::Let => "let".to_string(), + Tokens::StringVariable(str, bool) => format!("${}{}{}", match bool { true => "{", false => ""}, str.as_str(), match bool { true => "{", false => "" }), + Tokens::ArrayVariable(str, bool) => format!("@{}{}{}", match bool { true => "{", false => ""}, str.as_str(), match bool { true => "{", false => "" }), + Tokens::ArrayFunction(str) => format!("@{}", str.as_str()), + 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(), + Tokens::While => "while".to_string(), + Tokens::For => "for".to_string(), + Tokens::End => "end".to_string(), + Tokens::SubStart => "$(".to_string(), + Tokens::SubEnd => ")".to_string(), + Tokens::RedirectInto => "|".to_string(), + Tokens::FileRead => "<".to_string(), + Tokens::FileWrite => ">".to_string() + } + } +} + + +fn read_var_ahead(i: usize, text: &String) -> (usize, Tokens) { + let mut x = i; + let mut buf = String::new(); + let parens_mode = text.chars().nth(x + 1).unwrap() == '{'; + loop { + x += 1; + let letter: char = text.chars().nth(x).unwrap(); + match letter { + 'a'...'z' | 'A'...'Z' | '0'...'9' | ':' => { + buf.push(letter.clone()); + } + '}' => { + if parens_mode { + x += 1; + } + break; + } + _ => { if !parens_mode { break } } + } + } + let token = match text.chars().nth(i).unwrap() { + '$' => Tokens::StringVariable(buf, parens_mode), + '@' => Tokens::ArrayVariable(buf, parens_mode), + a => panic!("Invalid value {}", a) + }; + (x - i - 1, token) +} + +fn check_keyword(text: &String, i: usize, keyword: &str) -> bool { + let text_length = text.len(); + if text_length < i + keyword.len() + 1 { return false; } + text.chars().skip(i).take(keyword.len()).collect::() == keyword +} + +pub fn tokenize(reader: &mut dyn std::io::BufRead) -> io::Result> { + let mut quote_active = false; + let mut double_quote_active = false; + let mut escape_active = false; + let mut text = String::new(); + reader.read_to_string(&mut text); + let mut text_length = text.len(); + + let mut tokens: Vec = Vec::new(); + + let mut buf = String::new(); + let mut skipper = 0; + for i in 0..text_length { + if skipper > 0 { + skipper -= 1; + continue; + } + let letter: &char = &text.chars().nth(i).unwrap(); + let mut buf_add = true; + match letter { + '"' => if !escape_active && !quote_active { double_quote_active = !double_quote_active; buf_add = false }, + '\'' => if !escape_active && !double_quote_active { quote_active = !quote_active; buf_add = false }, + '$' => if !escape_active && !quote_active { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + if text_length > i && text.chars().nth(i + 1).unwrap() == '(' { + tokens.push(Tokens::SubStart); + skipper = 1; + buf_add = false; + } else { + let (skippers, mut token) = read_var_ahead(i, &text); + match token { + Tokens::StringVariable(ref str, bool) => if !bool && !double_quote_active { + if text.len() > i + skippers && text.chars().nth(i + skippers).unwrap() == '(' { + token = Tokens::StringFunction(str.clone()); + } + }, + Tokens::ArrayVariable(ref str, bool) => if !bool && !double_quote_active { + if text.len() > i + skippers && text.chars().nth(i + skippers).unwrap() == '(' { + token = Tokens::ArrayFunction(str.clone()); + } + } + _ => panic!("Cannot happen") + } + tokens.push(token); + skipper = skippers; + buf_add = false; + } + }, + ' ' => if !escape_active && !quote_active && !double_quote_active { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + tokens.push(Tokens::Space); + let mut x = i; + while text.chars().nth(x).unwrap() == ' ' { + x += 1; + } + skipper = x - i - 1; + buf_add = false; + }, + ')' => if !quote_active && !double_quote_active { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + tokens.push(Tokens::FunctionCallEnd); + buf_add = false; + }, + 'i' => if !quote_active && !double_quote_active { + if check_keyword(&text, i, "if") { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + skipper = 2; + tokens.push(Tokens::If); + buf_add = false; + } + }, + 'e' => if !quote_active && !double_quote_active { + if check_keyword(&text, i, "else") { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + skipper = 4; + tokens.push(Tokens::Else); + buf_add = false; + } else if check_keyword(&text, i , "end") { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + skipper = 3; + tokens.push(Tokens::End); + buf_add = false; + } + }, + 'w' => if !quote_active && !double_quote_active { + if check_keyword(&text, i, "while") { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + skipper = 5; + tokens.push(Tokens::While); + buf_add = false; + } + }, + 'f' => if !quote_active && !double_quote_active { + if check_keyword(&text, i, "for") { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + skipper = 3; + tokens.push(Tokens::If); + buf_add = false; + } + }, + '|' => if !escape_active && !quote_active && !double_quote_active { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + 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(); + } + tokens.push(Tokens::FileWrite); + buf_add = false; + }, + '<' => if !escape_active && !quote_active && !double_quote_active { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + tokens.push(Tokens::FileRead); + buf_add = false; + }, + ';' | '\n' => if !escape_active && !quote_active && !double_quote_active { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + tokens.push(Tokens::CommandEnd(letter.clone())); + buf_add = false; + }, + '\\' => if !escape_active { + escape_active = true; + buf_add = false; + } else { + escape_active = false; + }, + '=' => if !escape_active && !quote_active && !double_quote_active { + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + buf = String::new(); + } + tokens.push(Tokens::ExportSet); + buf_add = false; + }, + _ => {} + } + if letter.clone() != '\\' { escape_active = false; } + if buf_add { + buf.push(*letter); + } + } + if buf.len() > 0 { + tokens.push(Tokens::Literal(buf)); + } + + Ok(tokens) +} \ No newline at end of file diff --git a/src/parser/vars.rs b/src/parser/vars.rs index 7d9b95a..236dc81 100644 --- a/src/parser/vars.rs +++ b/src/parser/vars.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use std::ops::{Add, Deref}; -use crate::parser::ast::FunctionExpression; +use crate::parser::ast::FunctionDefinitionExpression; use crate::parser::escape; #[derive(Debug)] @@ -55,19 +55,22 @@ impl Variable { pub struct Scope { pub active: bool, pub vars: HashMap, - pub func: HashMap + pub func: HashMap } #[derive(Debug)] pub struct Context { pub scopes: Vec, - pub exports: HashMap + pub exports: HashMap, + pub fd: Vec } impl Context { pub fn new() -> Context { Context { - scopes: Vec::new() + scopes: Vec::new(), + exports: HashMap::new(), + fd: Vec::new() } } pub fn pop_scope(self: &mut Self) -> Option { @@ -100,4 +103,23 @@ impl Context { let mut vars = &mut self.scopes.last_mut().unwrap().vars; vars.insert(key, val); } + + pub fn get_func(self: &mut Self, key: &str) -> Option<&mut FunctionDefinitionExpression> { + for mut scope in self.scopes.iter_mut().rev() { + let mut funcs = &mut scope.func; + let val = funcs.get_mut(key); + match val { + None => {}, + Some(val) => { + return Some(val); + } + } + } + None + } + + pub fn set_func(&mut self, key: String, val: FunctionDefinitionExpression) { + let mut func = &mut self.scopes.last_mut().unwrap().func; + func.insert(key, val); + } } \ No newline at end of file