feat: some progress

This commit is contained in:
Daniel Bulant 2021-12-04 19:50:15 +01:00
parent d04d10cb9a
commit 845f00df0b
12 changed files with 404 additions and 43 deletions

5
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/

7
.idea/discord.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT" />
<option name="description" value="" />
</component>
</project>

7
.idea/fileColors.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SharedFileColors">
<fileColor scope="Non-Project Files" color="Yellow" />
<fileColor scope="Tests" color="Green" />
</component>
</project>

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/rush.iml" filepath="$PROJECT_DIR$/.idea/rush.iml" />
</modules>
</component>
</project>

14
.idea/rush.iml Normal file
View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

7
.idea/vagrant.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VagrantProjectSettings">
<option name="instanceFolder" value="" />
<option name="provider" value="" />
</component>
</project>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View file

@ -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<Stdout>) {
print!("{}", self.format(stdout));
}
fn format(self: &Self, stdout: &mut RawTerminal<Stdout>) -> 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
}

59
src/parser/ast.rs Normal file
View file

@ -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<Expression>
}
#[derive(Debug)]
pub struct WhileExpression {
pub condition: Expression,
pub contents: Vec<Expression>
}
#[derive(Debug)]
pub struct ForExpression {
pub key: String,
pub list: Value,
pub contents: Vec<Expression>
}
#[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<Value>,
pub body: Expression
}
pub struct RedirectTargetExpression {
}
#[derive(Debug)]
pub enum Expression {
LetExpression(LetExpression),
Command(Vec<Value>),
Function(FunctionExpression),
IfExpression(IfExpression),
WhileExpression(WhileExpression),
ForExpression(ForExpression)
}

View file

@ -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<String> = Vec::new();
let mut var_name = String::new();
let mut prev_char: Option<char> = 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
}

View file

@ -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<String, Variable>),
array(Vec<Variable>)
HMap(HashMap<String, Variable>),
Array(Vec<Variable>)
}
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<String, Variable>
pub active: bool,
pub vars: HashMap<String, Variable>,
pub func: HashMap<String, FunctionExpression>
}
#[derive(Debug)]
pub struct Context {
pub scopes: Vec<Scope>,
pub parent_context: Option<Box<Context>>
pub exports: HashMap<String, String>
}
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<Scope> {
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);
}
}

39
test/simple.rush Normal file
View file

@ -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