From 845f00df0be4792c6a780764fa63d155317ee056 Mon Sep 17 00:00:00 2001 From: Daniel Bulant Date: Sat, 4 Dec 2021 19:50:15 +0100 Subject: [PATCH] feat: some progress --- .idea/.gitignore | 5 ++ .idea/discord.xml | 7 +++ .idea/fileColors.xml | 7 +++ .idea/modules.xml | 8 +++ .idea/rush.iml | 14 +++++ .idea/vagrant.xml | 7 +++ .idea/vcs.xml | 6 ++ src/main.rs | 130 ++++++++++++++++++++++++++++++++----------- src/parser/ast.rs | 59 ++++++++++++++++++++ src/parser/mod.rs | 88 ++++++++++++++++++++++++++++- src/parser/vars.rs | 77 ++++++++++++++++++++++--- test/simple.rush | 39 +++++++++++++ 12 files changed, 404 insertions(+), 43 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/discord.xml create mode 100644 .idea/fileColors.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/rush.iml create mode 100644 .idea/vagrant.xml create mode 100644 .idea/vcs.xml create mode 100644 src/parser/ast.rs create mode 100644 test/simple.rush diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/discord.xml b/.idea/discord.xml new file mode 100644 index 0000000..8cf359d --- /dev/null +++ b/.idea/discord.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/fileColors.xml b/.idea/fileColors.xml new file mode 100644 index 0000000..6c33d23 --- /dev/null +++ b/.idea/fileColors.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..5966e0f --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/rush.iml b/.idea/rush.iml new file mode 100644 index 0000000..b4691a7 --- /dev/null +++ b/.idea/rush.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vagrant.xml b/.idea/vagrant.xml new file mode 100644 index 0000000..a5aa786 --- /dev/null +++ b/.idea/vagrant.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index ab92897..4e43920 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,73 +1,139 @@ mod parser; -use std::io::{self, Read, Write}; +use std::io::{self, Read, Stdout, Write}; use std::cmp; use std::convert::TryInto; +use std::ops::Add; +use std::process; -use termion::raw::IntoRawMode; +use termion::raw::{IntoRawMode, RawTerminal}; use termion::input::TermRead; use termion::cursor::{self, DetectCursorPos}; use termion::event::*; use termion::input::{MouseTerminal}; +struct Term { + input: String, + idx: usize, +} + +impl Term { + fn new() -> Term { + return Term { + input: String::new(), + idx: 0, + }; + } + + fn print(self: &Self, stdout: &mut RawTerminal) { + print!("{}", self.format(stdout)); + } + fn format(self: &Self, stdout: &mut RawTerminal) -> String { + let (_, y) = stdout.cursor_pos().unwrap(); + format!( + "{}{}{}{}{}", + termion::clear::CurrentLine, + termion::cursor::Goto(1, y), + &self.input, + termion::cursor::Left((self.input.len() - self.idx).try_into().unwrap()), + termion::cursor::Right(if self.input.len() > 0 { 1 } else { 0 } ) + ) + } + + fn insert(self: &mut Self, idx: usize, char: char) { + self.input.insert(idx, char); + } + fn insert_str(self: &mut Self, idx: usize, str: &str) { + self.input.insert_str(idx, str); + } + fn remove(self: &mut Self, idx: usize) { + self.input.remove(idx); + } +} + +struct Shell { + term: Term, + ctx: parser::vars::Context, +} + +impl Shell { + fn new() -> Shell { + return Shell { + term: Term::new(), + ctx: parser::vars::Context { + scopes: Vec::new(), + parent_context: None, + }, + }; + } +} + fn main() { + loop { + let mut shell = editor(); + shell.term.input += "\n"; + parser::exec(&mut shell.term.input.as_bytes(), shell.ctx); + } +} + +fn editor() -> Shell { let stdin = io::stdin(); let mut stdout = io::stdout().into_raw_mode().unwrap(); - let ctx = parser::vars::Context { - scopes: Vec::new(), - parent_context: None - }; - let mut output = String::new(); - let mut idx = 0; + let mut shell = Shell::new(); + let mut should_execute = true; for c in stdin.keys() { let c = c.unwrap(); match c { Key::Char('\n') => { - break; + if shell.term.input.chars().nth(shell.term.idx).unwrap_or(' ') == '\\' { + shell.term.insert_str(shell.term.idx, "\\\n"); + } else { + break; + } } Key::Backspace => { - if output.len() > 0 && idx > 0 { - output.remove(idx - 1); - idx -= 1; - print!("{}{} ", cursor::Left((output.len() - idx + 2).try_into().unwrap()), &output[idx..]); + if shell.term.input.len() > 0 && shell.term.idx > 0 { + if shell.term.idx == shell.term.input.len() - 1 { + shell.term.input.pop(); + } else { + shell.term.remove(shell.term.idx - 1); + } + shell.term.idx -= 1; } } Key::Delete => { - if idx < output.len() { - output.remove(idx); - print!("{} {}", &output[idx..], cursor::Left((output.len() - idx + 1).try_into().unwrap())); + if shell.term.idx < shell.term.input.len() { + shell.term.remove(shell.term.idx); } } Key::End => { - let start = idx; - idx = cmp::max(output.len(), 1) - 1; - print!("{}", cursor::Right((idx - start).try_into().unwrap())); + shell.term.idx = cmp::max(shell.term.input.len(), 1) - 1; } Key::Home => { - let start = idx; - idx = 0; - print!("{}", cursor::Left(start.try_into().unwrap())); + shell.term.idx = 0; } Key::Left => { - if idx > 0 { - idx -= 1; + if shell.term.idx > 0 { + shell.term.idx -= 1; } - print!("{}", cursor::Left(1)); } Key::Right => { - if idx < output.len() - 1 { - idx += 1; + if shell.term.idx < shell.term.input.len() - 1 { + shell.term.idx += 1; } - print!("{}", cursor::Right(1)); + } + Key::Ctrl('c') => { + process::exit(1); } Key::Char(char) => { - output.insert(idx, char); - idx += 1; - print!("{}", char); + shell.term.insert(shell.term.idx, char); + shell.term.idx += 1; } _ => {} } + shell.term.print(&mut stdout); stdout.flush().unwrap(); } - parser::read_parse(&mut output.as_bytes(), ctx).unwrap(); + stdout.suspend_raw_mode(); + shell } diff --git a/src/parser/ast.rs b/src/parser/ast.rs new file mode 100644 index 0000000..ff22cac --- /dev/null +++ b/src/parser/ast.rs @@ -0,0 +1,59 @@ +use crate::parser::vars::Variable; + +#[derive(Debug)] +pub struct LetExpression { + pub key: String, + pub value: Value +} + +#[derive(Debug)] +pub struct IfExpression { + pub condition: Expression, + pub contents: Vec +} + +#[derive(Debug)] +pub struct WhileExpression { + pub condition: Expression, + pub contents: Vec +} + +#[derive(Debug)] +pub struct ForExpression { + pub key: String, + pub list: Value, + pub contents: Vec +} + +#[derive(Debug)] +pub enum ForValue { + Value(Value), + Range(Some(u32), Some(u32)) +} + +#[derive(Debug)] +pub enum Value { + Literal(String), + Variable(Variable), + Expression(Expression) +} + +#[derive(Debug)] +pub struct FunctionExpression { + pub args: Vec, + pub body: Expression +} + +pub struct RedirectTargetExpression { + +} + +#[derive(Debug)] +pub enum Expression { + LetExpression(LetExpression), + Command(Vec), + Function(FunctionExpression), + IfExpression(IfExpression), + WhileExpression(WhileExpression), + ForExpression(ForExpression) +} \ No newline at end of file diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 4c27926..2bd790c 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,12 +1,96 @@ pub mod vars; +pub mod ast; use std::io; //use std::io::prelude::*; use utf8_chars::BufReadCharsExt; -pub fn read_parse(reader: &mut dyn std::io::BufRead, ctx: vars::Context) -> io::Result<()> { +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; + for c in reader.chars().map(|x| x.unwrap()) { - println!("char {}\r", c); + 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; + + 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(()) } + +pub fn escape(str: String) -> String { + str +} \ No newline at end of file diff --git a/src/parser/vars.rs b/src/parser/vars.rs index b9bac1c..7d9b95a 100644 --- a/src/parser/vars.rs +++ b/src/parser/vars.rs @@ -1,4 +1,7 @@ use std::collections::HashMap; +use std::ops::{Add, Deref}; +use crate::parser::ast::FunctionExpression; +use crate::parser::escape; #[derive(Debug)] pub enum Variable { @@ -11,34 +14,90 @@ pub enum Variable { U128(u128), F32(f32), F64(f64), - hmap(HashMap), - array(Vec) + HMap(HashMap), + Array(Vec) +} + +impl Variable { + pub fn to_string(self: &Self) -> String { + match self { + Variable::String(var) => String::from(var), + Variable::I32(num) => num.to_string(), + Variable::I64(num) => num.to_string(), + Variable::I128(num) => num.to_string(), + Variable::U32(num) => num.to_string(), + Variable::U64(num) => num.to_string(), + Variable::U128(num) => num.to_string(), + Variable::F32(num) => num.to_string(), + Variable::F64(num) => num.to_string(), + Variable::HMap(map) => { + String::from("[Object object]") + }, + Variable::Array(vars) => { + let mut str = String::new(); + for var in vars { + str += &*var.clone().to_string(); + str += " "; + } + str + } + } + } + + pub fn index(self: &Self) -> &Variable { + match self { + _ => panic!("Cannot index unsupported types") + } + } } #[derive(Debug)] pub struct Scope { - active: bool, - vars: HashMap + pub active: bool, + pub vars: HashMap, + pub func: HashMap } #[derive(Debug)] pub struct Context { pub scopes: Vec, - pub parent_context: Option> + pub exports: HashMap } impl Context { pub fn new() -> Context { Context { - scopes: Vec::new(), - parent_context: None + scopes: Vec::new() } } - pub fn add_scope(self: &mut Context, active: bool) { + pub fn pop_scope(self: &mut Self) -> Option { + self.scopes.pop() + } + pub fn add_scope(self: &mut Self, active: bool) { let scope = Scope { - active: active, + active, + func: HashMap::new(), vars: HashMap::new() }; self.scopes.push(scope); } + + pub fn get_var(self: &mut Self, var: &str) -> Option<&mut Variable> { + for mut scope in self.scopes.iter_mut().rev() { + let mut vars = &mut scope.vars; + let val = vars.get_mut(var); + match val { + None => {}, + Some(val) => { + return Some(val); + } + } + } + None + } + + pub fn set_var(&mut self, key: String, val: Variable) { + let mut vars = &mut self.scopes.last_mut().unwrap().vars; + vars.insert(key, val); + } } \ No newline at end of file diff --git a/test/simple.rush b/test/simple.rush new file mode 100644 index 0000000..e78de8a --- /dev/null +++ b/test/simple.rush @@ -0,0 +1,39 @@ + +echo test + +echo single; echo line + +echo test > cat + +grep echo < simple.rush + +let test = val +echo var $test +let test = $test +echo var2 ${test} +echo last exit code $? + +if true + echo condition true +end + +if false + echo condition false +end + +if true + echo condition true +else + echo else +end + +if false + echo condition false +else if true + echo condition else true +end + +while true + echo loop + break +end \ No newline at end of file