various improvements, basic (currently failing) tests, improved lexer

This commit is contained in:
Daniel Bulant 2022-02-20 18:39:23 +01:00
parent b43959fa79
commit aa33fb90ea
12 changed files with 439 additions and 300 deletions

88
Cargo.lock generated
View file

@ -2,11 +2,29 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "addr2line"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.54" version = "1.0.54"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a99269dff3bc004caa411f38845c20303f1e393ca2bd6581576fa3a7f59577d" checksum = "7a99269dff3bc004caa411f38845c20303f1e393ca2bd6581576fa3a7f59577d"
dependencies = [
"backtrace",
]
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
@ -14,12 +32,45 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "filedescriptor" name = "filedescriptor"
version = "0.8.1" version = "0.8.1"
@ -31,18 +82,49 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "gimli"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.107" version = "0.2.107"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg",
]
[[package]] [[package]]
name = "numtoa" name = "numtoa"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
[[package]]
name = "object"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.32" version = "1.0.32"
@ -89,6 +171,12 @@ dependencies = [
"utf8-chars", "utf8-chars",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.82" version = "1.0.82"

View file

@ -10,4 +10,7 @@ edition = "2018"
utf8-chars = "1.0.0" utf8-chars = "1.0.0"
termion = "1.5.6" termion = "1.5.6"
filedescriptor = "0.8.1" filedescriptor = "0.8.1"
anyhow = "1.0.54"
[dependencies.anyhow]
version = "1.0.54"
features = ["backtrace"]

View file

@ -59,11 +59,7 @@ impl Shell {
fn new() -> Shell { fn new() -> Shell {
return Shell { return Shell {
term: Term::new(), term: Term::new(),
ctx: parser::vars::Context { ctx: parser::vars::Context::new()
scopes: Vec::new(),
fd: Vec::new(),
exports: HashMap::new()
},
}; };
} }
@ -76,7 +72,6 @@ impl Shell {
fn main() { fn main() {
let mut shell = Shell::new(); let mut shell = Shell::new();
shell.ctx.add_scope(true);
loop { loop {
print!("$: "); print!("$: ");
io::stdout().flush().unwrap(); io::stdout().flush().unwrap();
@ -90,6 +85,41 @@ fn main() {
} }
} }
#[cfg(test)]
mod test {
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use crate::parser;
use anyhow::Result;
fn load_and_run<P: AsRef<Path>>(path: P) -> Result<()> {
let mut ctx = parser::vars::Context::new();
let src = File::open(path).unwrap();
parser::exec(&mut BufReader::new(src), &mut ctx)
}
#[test]
fn simple() -> Result<()> {
load_and_run("test/simple.rush")
}
#[test]
fn var() -> Result<()> {
load_and_run("test/var.rush")
}
#[test]
fn if_expr() -> Result<()> {
load_and_run("test/if.rush")
}
#[test]
fn while_expr() -> Result<()> {
load_and_run("test/while.rush")
}
}
fn editor() -> Shell { fn editor() -> Shell {
let stdin = io::stdin(); let stdin = io::stdin();
let mut stdout = io::stdout().into_raw_mode().unwrap(); let mut stdout = io::stdout().into_raw_mode().unwrap();

View file

@ -1,9 +1,10 @@
use crate::parser::tokens::Tokens; use crate::parser::tokens::Tokens;
use anyhow::{bail, Result}; use anyhow::{bail, Context, Result};
#[derive(Debug)] #[derive(Debug)]
pub struct LetExpression { pub struct LetExpression {
pub key: Box<Value>, pub key: Box<Value>,
pub vartype: Option<String>,
pub value: Box<Value> pub value: Box<Value>
} }
@ -22,7 +23,8 @@ pub struct OrExpression {
#[derive(Debug)] #[derive(Debug)]
pub struct IfExpression { pub struct IfExpression {
pub condition: Box<Expression>, pub condition: Box<Expression>,
pub contents: Vec<Expression> pub contents: Vec<Expression>,
pub else_contents: Vec<Expression>
} }
#[derive(Debug)] #[derive(Debug)]
@ -62,9 +64,16 @@ pub enum Value {
Values(Vec<Value>) Values(Vec<Value>)
} }
#[derive(Debug)]
pub struct FunctionVariable {
pub name: String,
pub vartype: Option<String>
}
#[derive(Debug)] #[derive(Debug)]
pub struct FunctionDefinitionExpression { pub struct FunctionDefinitionExpression {
pub args: Vec<String>, pub name: String,
pub args: Vec<FunctionVariable>,
pub body: Box<Expression> pub body: Box<Expression>
} }
@ -92,6 +101,11 @@ pub enum CommandValue {
Var(String, Value) Var(String, Value)
} }
#[derive(Debug)]
pub struct BreakExpression {
pub num: Box<Value>
}
#[derive(Debug)] #[derive(Debug)]
pub enum Expression { pub enum Expression {
LetExpression(LetExpression), LetExpression(LetExpression),
@ -106,7 +120,8 @@ pub enum Expression {
FileSourceExpression(FileSourceExpression), FileSourceExpression(FileSourceExpression),
Expressions(Vec<Expression>), Expressions(Vec<Expression>),
OrExpression(OrExpression), OrExpression(OrExpression),
AndExpression(AndExpression) AndExpression(AndExpression),
BreakExpression(BreakExpression)
} }
#[derive(Debug)] #[derive(Debug)]
@ -190,7 +205,7 @@ impl Tree {
self.inc(); // ???? self.inc(); // ????
self.inc(); self.inc();
let value = Box::new(self.get_value(end)?); let value = Box::new(self.get_value(end)?);
Ok(Expression::LetExpression(LetExpression { key, value })) Ok(Expression::LetExpression(LetExpression { key, vartype: None, value }))
} }
fn parse_read(&mut self, target: Option<Expression>, _end: usize) -> Result<Expression> { fn parse_read(&mut self, target: Option<Expression>, _end: usize) -> Result<Expression> {
@ -241,28 +256,55 @@ impl Tree {
Ok(Expression::FileTargetExpression(FileTargetExpression { source, target })) Ok(Expression::FileTargetExpression(FileTargetExpression { source, target }))
} }
fn parse_function(&self, _end: usize) -> Result<FunctionDefinitionExpression> { fn parse_function(&mut self, _end: usize) -> Result<FunctionDefinitionExpression> {
bail!("Functions not yet implemented") bail!("Functions not yet implemented")
} }
fn parse_array_func(&self, _str: String, _end: usize) -> Result<DefinedFunction> { fn parse_array_func(&mut self, _str: String, _end: usize) -> Result<DefinedFunction> {
bail!("Array functions not yet implemented"); bail!("Array functions not yet implemented");
} }
fn parse_string_func(&self, _str: String, _end: usize) -> Result<DefinedFunction> { fn parse_string_func(&mut self, _str: String, _end: usize) -> Result<DefinedFunction> {
bail!("Array functions not yet implemented"); bail!("Array functions not yet implemented");
} }
fn parse_for(&self, _end: usize) -> Result<ForExpression> { fn parse_for(&mut self, _end: usize) -> Result<ForExpression> {
bail!("For loop not yet implemented"); bail!("For loop not yet implemented");
} }
fn parse_if(&self, _end: usize) -> Result<IfExpression> { fn parse_if(&mut self, end: usize) -> Result<IfExpression> {
bail!("If not yet implemented"); self.inc();
let condition = self.get_expression(end).with_context(|| "Error getting condition for if expression")?;
dbg!(&condition);
let mut contents = Vec::new();
loop {
match self.get_next_token() {
Tokens::End => break,
Tokens::Space => { self.inc(); },
_ => contents.push(self.get_expression(end).with_context(|| "Error getting contents for if expression")?)
};
}
let mut else_contents = Vec::new();
Ok(IfExpression { condition: Box::new(condition), contents, else_contents })
} }
fn parse_while(&self, _end: usize) -> Result<WhileExpression> { fn parse_while(&mut self, end: usize) -> Result<WhileExpression> {
bail!("While not yet implemented"); self.inc();
let condition = self.get_expression(end).with_context(|| "Error getting condition for while expression")?;
dbg!(&condition);
dbg!(self.i);
let mut contents = Vec::new();
loop {
let token = self.get_next_token();
dbg!(token);
match token {
Tokens::End => break,
Tokens::Space => { self.inc(); },
_ => contents.push(self.get_expression(end).with_context(|| "Error getting contents for while expression")?)
};
dbg!(&contents);
}
Ok(WhileExpression { condition: Box::new(condition), contents })
} }
fn parse_sub(&mut self, end: usize) -> Result<Vec<Expression>> { fn parse_sub(&mut self, end: usize) -> Result<Vec<Expression>> {
@ -319,7 +361,7 @@ impl Tree {
} }
// self.inc(); // self.inc();
if lvl != 0 { if lvl != 0 {
panic!("Parenthesis do not match"); bail!("Parenthesis do not match");
} }
dbg!(&self, len); dbg!(&self, len);
let val = Value::Expressions(self.parse_sub(self.i + len)?); let val = Value::Expressions(self.parse_sub(self.i + len)?);
@ -349,6 +391,7 @@ impl Tree {
}, },
Tokens::And => bail!("Unexpected AND (&&)"), Tokens::And => bail!("Unexpected AND (&&)"),
Tokens::Or => bail!("Unexpected OR (||)"), Tokens::Or => bail!("Unexpected OR (||)"),
Tokens::Break => buf.push(Value::Literal(token.to_str())),
Tokens::JobCommandEnd => bail!("Unexpected job command end (&)"), Tokens::JobCommandEnd => bail!("Unexpected job command end (&)"),
} }
if self.i >= end - 1 { break } if self.i >= end - 1 { break }
@ -367,7 +410,7 @@ impl Tree {
match token { match token {
Tokens::Space => {self.inc();}, Tokens::Space => {self.inc();},
Tokens::CommandEnd(_) => { if matches!(expr, Some(_)) { break }; self.inc();}, Tokens::CommandEnd(_) => { if matches!(expr, Some(_)) { break }; self.inc();},
Tokens::Literal(t) => if matches!(expr, Some(_)) { Tokens::Literal(_) => if matches!(expr, Some(_)) {
bail!("Unexpected literal. After file redirect, you need to use a semicolon or newline."); bail!("Unexpected literal. After file redirect, you need to use a semicolon or newline.");
} else { } else {
expr = Some(self.parse_call(end)?); expr = Some(self.parse_call(end)?);
@ -416,7 +459,7 @@ impl Tree {
_ => expr = Some(self.parse_call(end)?) _ => expr = Some(self.parse_call(end)?)
}, },
Tokens::Else => bail!("Unexpected token ELSE"), Tokens::Else => bail!("Unexpected token ELSE"),
Tokens::End => bail!("Unexpected token END"), Tokens::End => bail!("Unexpected token END\nCurrent expression:{:?}", expr),
Tokens::For => match expr { Tokens::For => match expr {
Some(_) => bail!("Commands must be ended properly"), Some(_) => bail!("Commands must be ended properly"),
None => expr = Some(Expression::ForExpression(self.parse_for(end)?)), None => expr = Some(Expression::ForExpression(self.parse_for(end)?)),
@ -447,6 +490,12 @@ impl Tree {
expr = Some(Expression::OrExpression(OrExpression { first: Box::new(expr.unwrap()), second: Box::new(self.get_expression(end)?) })); expr = Some(Expression::OrExpression(OrExpression { first: Box::new(expr.unwrap()), second: Box::new(self.get_expression(end)?) }));
} }
}, },
Tokens::Break => match expr {
None => {
expr = Some(Expression::BreakExpression(BreakExpression { num: Box::new(self.get_value(end)?)}));
},
Some(_) => bail!("Unexpected break")
}
Tokens::JobCommandEnd => bail!("Jobs not yet implemented") Tokens::JobCommandEnd => bail!("Jobs not yet implemented")
} }
if self.i >= end - 1 { break } if self.i >= end - 1 { break }
@ -462,21 +511,23 @@ impl Tree {
self.i += 1; self.i += 1;
self self
} }
fn get_current_token(&self) -> &Tokens { fn get_current_token(&self) -> &Tokens { self.tokens.get(self.i).unwrap() }
self.tokens.get(self.i).unwrap() fn get_next_token(&self) -> &Tokens { self.tokens.get(self.i + 1).unwrap() }
}
fn get_next_token(&self) -> &Tokens {
self.tokens.get(self.i + 1).unwrap()
}
} }
pub fn build_tree(tokens: Vec<Tokens>) -> Result<Vec<Expression>> { pub fn build_tree(tokens: Vec<Tokens>) -> Result<Vec<Expression>> {
let mut expressions: Vec<Expression> = Vec::new(); let mut expressions: Vec<Expression> = Vec::new();
let mut tree = Tree { tokens, i: 0 }; let mut tree = Tree { tokens, i: 0 };
loop { loop {
if tree.i == tree.tokens.len() - 1 { break; } if tree.i >= tree.tokens.len() - 1 { break; }
let val = tree.get_expression(tree.tokens.len()); let val = tree.get_expression(tree.tokens.len());
expressions.push(val?); match val {
Ok(val) => expressions.push(val),
Err(error) => {
dbg!(tree);
return Err(error);
}
}
} }
Ok(expressions) Ok(expressions)
} }

View file

@ -1,16 +1,17 @@
use std::fs::File; use std::fs::File;
use std::process::{Child, Command, Stdio}; use std::process::{Child, Command, Stdio};
use std::io; use std::io;
use crate::parser::ast::{AndExpression, CommandValue, Expression, FileSourceExpression, FileTargetExpression, LetExpression, OrExpression, RedirectTargetExpression, Value}; use crate::parser::ast::{AndExpression, BreakExpression, CommandValue, Expression, FileSourceExpression, FileTargetExpression, IfExpression, LetExpression, OrExpression, RedirectTargetExpression, Value, WhileExpression};
use crate::parser::vars; use crate::parser::vars;
use crate::parser::vars::{Context, Variable}; use crate::parser::vars::{Context, Variable};
use anyhow::{Result, bail, Context as AnyhowContext};
trait ExecExpression { trait ExecExpression {
fn exec(self, ctx: &mut vars::Context) -> Option<Command>; fn exec(&mut self, ctx: &mut vars::Context) -> Result<Option<Command>>;
} }
trait GetValue { trait GetValue {
fn get(self, ctx: &mut vars::Context) -> Variable; fn get(&mut self, ctx: &mut vars::Context) -> Result<Variable>;
} }
struct ExecResult { struct ExecResult {
@ -97,123 +98,200 @@ impl ExecResult {
} }
impl GetValue for CommandValue { impl GetValue for CommandValue {
fn get(self, ctx: &mut vars::Context) -> Variable { fn get(self: &mut CommandValue, ctx: &mut vars::Context) -> Result<Variable> {
match self { match self {
CommandValue::Value(val) => val.get(ctx), CommandValue::Value(val) => val.get(ctx),
CommandValue::Var(_, _) => panic!("Broken executor") CommandValue::Var(_, _) => bail!("Broken executor")
} }
} }
} }
impl GetValue for Value { impl GetValue for Value {
fn get(self, ctx: &mut vars::Context) -> Variable { fn get(self: &mut Value, ctx: &mut vars::Context) -> Result<Variable> {
match self { match self {
Value::Literal(str) => { Value::Literal(str) => {
Variable::String(str) Ok(Variable::String(str.clone()))
}, },
Value::Variable(str) => ctx.get_var(&str).unwrap_or(&mut Variable::String(String::from(""))).clone(), Value::Variable(str) => Ok(ctx.get_var(&str).unwrap_or(&mut Variable::String(String::from(""))).clone()),
Value::ArrayVariable(str) => ctx.get_var(&str).unwrap_or(&mut Variable::Array(Vec::new())).clone(), Value::ArrayVariable(str) => Ok(ctx.get_var(&str).unwrap_or(&mut Variable::Array(Vec::new())).clone()),
Value::ArrayFunction(_) => panic!("Not implemented yet"), Value::ArrayFunction(_) => todo!("Not implemented yet"),
Value::StringFunction(_) => panic!("Not implemented yet"), Value::StringFunction(_) => todo!("Not implemented yet"),
Value::Expressions(expressions) => { Value::Expressions(expressions) => {
let mut out = String::new(); let mut out = String::new();
ctx.add_scope(true); ctx.add_scope();
for mut expr in expressions { for expr in expressions {
let res = expr.exec(ctx); let res = expr.exec(ctx)?;
match res { match res {
None => {}, None => {},
Some(mut cmd) => { Some(mut cmd) => {
out += &*String::from_utf8_lossy(&cmd.output().expect("Failed to read output of command").stdout); out += &*String::from_utf8_lossy(&cmd.output().with_context(|| "Failed to read output of command")?.stdout);
} }
} }
} }
ctx.pop_scope(); ctx.pop_scope();
Variable::String(out) Ok(Variable::String(out))
}, },
Value::Values(vec) => { Value::Values(vec) => {
let mut out = Vec::new(); let mut out = Vec::new();
for mut val in vec { for val in vec {
out.push(val.get(ctx)); out.push(val.get(ctx)?);
} }
Variable::Array(out) Ok(Variable::Array(out))
} }
} }
} }
} }
impl ExecExpression for Expression { impl ExecExpression for Expression {
fn exec(self, ctx: &mut vars::Context) -> Option<Command> { fn exec(self: &mut Expression, ctx: &mut vars::Context) -> Result<Option<Command>> {
match self { match self {
Expression::LetExpression(expr) => expr.exec(ctx), Expression::LetExpression(expr) => expr.exec(ctx),
Expression::Command(expr) => expr.exec(ctx), Expression::Command(expr) => expr.exec(ctx),
Expression::JobCommand(_) => todo!(), Expression::JobCommand(_) => todo!("Jobs"),
Expression::Function(_) => todo!(), Expression::Function(_) => todo!("Function definition"),
Expression::IfExpression(_) => todo!(), Expression::IfExpression(expr) => expr.exec(ctx),
Expression::WhileExpression(_) => todo!(), Expression::WhileExpression(expr) => expr.exec(ctx),
Expression::ForExpression(_) => todo!(), Expression::ForExpression(_) => todo!("For expression"),
Expression::RedirectTargetExpression(expr) => expr.exec(ctx), Expression::RedirectTargetExpression(expr) => expr.exec(ctx),
Expression::FileTargetExpression(expr) => expr.exec(ctx), Expression::FileTargetExpression(expr) => expr.exec(ctx),
Expression::FileSourceExpression(expr) => expr.exec(ctx), Expression::FileSourceExpression(expr) => expr.exec(ctx),
Expression::Expressions(expr) => expr.exec(ctx), Expression::Expressions(expr) => expr.exec(ctx),
Expression::OrExpression(expr) => expr.exec(ctx), Expression::OrExpression(expr) => expr.exec(ctx),
Expression::AndExpression(expr) => expr.exec(ctx) Expression::AndExpression(expr) => expr.exec(ctx),
Expression::BreakExpression(expr) => expr.exec(ctx)
} }
} }
} }
impl ExecExpression for BreakExpression {
fn exec(self: &mut BreakExpression, ctx: &mut vars::Context) -> Result<Option<Command>> {
if ctx.break_num > 0 { ctx.break_num -= 1; return Ok(None) }
let val = self.num.get(ctx)?;
let num: u16 = val.to_string().parse()?;
ctx.break_num = num;
Ok(None)
}
}
impl ExecExpression for WhileExpression {
fn exec(self: &mut WhileExpression, ctx: &mut vars::Context) -> Result<Option<Command>> {
if ctx.break_num > 0 { ctx.break_num -= 1; return Ok(None) }
let mut condition = match self.condition.exec(ctx)? {
None => bail!("Invalid while expression"),
Some(cmd) => cmd
};
ctx.add_scope();
let mut res;
loop {
match condition.spawn() {
Result::Err(_) => {
res = Some(condition);
break;
},
Result::Ok(mut child) => {
if !child.wait()?.success() {
res = Some(condition);
break
} else {
res = self.contents.exec(ctx)?
}
}
};
if ctx.break_num > 0 {
ctx.break_num -= 1;
break;
}
}
ctx.pop_scope();
Ok(res)
}
}
impl ExecExpression for IfExpression {
fn exec(self: &mut IfExpression, ctx: &mut vars::Context) -> Result<Option<Command>> {
if ctx.break_num > 0 { return Ok(None) }
let mut condition = match self.condition.exec(ctx)? {
None => bail!("Invalid IF expression"),
Some(cmd) => cmd
};
let res = match condition.spawn() {
Result::Err(_) => {
Some(condition)
},
Result::Ok(mut res) => {
if !res.wait()?.success() {
Some(condition)
} else {
ctx.add_scope();
let res = self.contents.exec(ctx)?;
ctx.pop_scope();
res
}
}
};
Ok(res)
}
}
impl ExecExpression for LetExpression { impl ExecExpression for LetExpression {
fn exec(mut self, ctx: &mut vars::Context) -> Option<Command> { fn exec(self: &mut LetExpression, ctx: &mut vars::Context) -> Result<Option<Command>> {
let key = self.key.get(ctx); if ctx.break_num > 0 { return Ok(None) }
let val = self.value.get(ctx); let key = self.key.get(ctx)?;
let val = self.value.get(ctx)?;
ctx.set_var(key.to_string(), val); ctx.set_var(key.to_string(), val);
None Ok(None)
} }
} }
impl ExecExpression for Vec<CommandValue> { impl ExecExpression for Vec<CommandValue> {
fn exec(mut self, ctx: &mut vars::Context) -> Option<Command> { fn exec(self: &mut Vec<CommandValue>, ctx: &mut vars::Context) -> Result<Option<Command>> {
if self.len() == 0 { panic!("Command with 0 length"); } if ctx.break_num > 0 { return Ok(None) }
if self.len() == 0 { bail!("Command with 0 length"); }
let mut first = self.remove(0); let mut first = self.remove(0);
let command_name = first.get(ctx).to_string(); let command_name = first.get(ctx)?.to_string();
let mut cmd = Command::new(command_name); let mut cmd = Command::new(command_name);
for mut value in self { for value in self {
cmd.arg(value.get(ctx).to_string()); cmd.arg(value.get(ctx)?.to_string());
} }
Some(cmd) Ok(Some(cmd))
} }
} }
impl ExecExpression for RedirectTargetExpression { impl ExecExpression for RedirectTargetExpression {
fn exec(mut self, ctx: &mut vars::Context) -> Option<Command> { fn exec(self: &mut RedirectTargetExpression, ctx: &mut vars::Context) -> Result<Option<Command>> {
let mut src = self.source.exec(ctx).unwrap(); if ctx.break_num > 0 { return Ok(None) }
let mut target = self.target.exec(ctx).unwrap(); let mut src = self.source.exec(ctx)?.unwrap();
let mut target = self.target.exec(ctx)?.unwrap();
src.stdout(Stdio::piped()); src.stdout(Stdio::piped());
match src.spawn() { match src.spawn() {
Result::Err(e) => { println!("Error executing: {}", e)}, Result::Err(e) => { println!("Error executing: {}", e)},
Result::Ok(mut res) => { Result::Ok(res) => {
target.stdin(res.stdout.unwrap()); target.stdin(res.stdout.unwrap());
} }
} }
Some(target) Ok(Some(target))
} }
} }
impl ExecExpression for FileTargetExpression { impl ExecExpression for FileTargetExpression {
fn exec(mut self, ctx: &mut vars::Context) -> Option<Command> { fn exec(self: &mut FileTargetExpression, ctx: &mut vars::Context) -> Result<Option<Command>> {
let mut src = self.source; if ctx.break_num > 0 { return Ok(None) }
let mut target = self.target.get(ctx); let src = &mut self.source;
let mut src = match src { let target = self.target.get(ctx)?;
Some(expr) => expr.exec(ctx), let src = match src {
Some(expr) => expr.exec(ctx)?,
None => { None => {
todo!(); todo!("Redirect without target file");
} }
}; };
let command; let command;
match src { match src {
Some(mut cmd) => { Some(mut cmd) => {
cmd.stdout(Stdio::piped()); cmd.stdout(Stdio::piped());
let mut file = File::create(target.to_string()); let file = File::create(target.to_string());
match file { match file {
Result::Err(e) => println!("Error: {}", e), Result::Err(e) => println!("Error: {}", e),
Result::Ok(mut file) => { Result::Ok(mut file) => {
@ -222,102 +300,107 @@ impl ExecExpression for FileTargetExpression {
println!("Error executing command: {}", e); println!("Error executing command: {}", e);
}, },
Result::Ok(res) => { Result::Ok(res) => {
io::copy(&mut res.stdout.unwrap(), &mut file); io::copy(&mut res.stdout.unwrap(), &mut file)?;
} }
} }
} }
} }
command = cmd; command = cmd;
}, },
None => { panic!("Invalid command provided for file target"); } None => { bail!("Invalid command provided for file target"); }
}; };
Some(command) Ok(Some(command))
} }
} }
impl ExecExpression for FileSourceExpression { impl ExecExpression for FileSourceExpression {
fn exec(self, ctx: &mut Context) -> Option<Command> { fn exec(self: &mut FileSourceExpression, ctx: &mut Context) -> Result<Option<Command>> {
let mut source = self.source.get(ctx).to_string(); if ctx.break_num > 0 { return Ok(None) }
let mut target = self.target; let source = self.source.get(ctx)?.to_string();
let mut target = match target { let target = &mut self.target;
Some(expr) => expr.exec(ctx), let target = match target {
Some(expr) => expr.exec(ctx)?,
None => { None => {
Some(Command::new("less")) Some(Command::new("less"))
} }
}; };
let mut command = match target { let mut command = match target {
None => { panic!("Invalid command") }, None => { bail!("Invalid command") },
Some(cmd) => cmd Some(cmd) => cmd
}; };
let mut source = match File::open(source) { let source = match File::open(source) {
Result::Err(e) => panic!("Cannot open file: {}", e), Result::Err(e) => bail!("Cannot open file: {}", e),
Result::Ok(file) => file Result::Ok(file) => file
}; };
command.stdin(source); command.stdin(source);
Some(command) Ok(Some(command))
} }
} }
impl ExecExpression for Vec<Expression> { impl ExecExpression for Vec<Expression> {
fn exec(self, ctx: &mut Context) -> Option<Command> { fn exec(self: &mut Vec<Expression>, ctx: &mut Context) -> Result<Option<Command>> {
if ctx.break_num > 0 { return Ok(None) }
let mut last = None; let mut last = None;
for expr in self { for expr in self {
last = expr.exec(ctx); last = expr.exec(ctx)?;
if ctx.break_num > 0 { return Ok(last) }
} }
last Ok(last)
} }
} }
impl ExecExpression for OrExpression { impl ExecExpression for OrExpression {
fn exec(self, ctx: &mut Context) -> Option<Command> { fn exec(self: &mut OrExpression, ctx: &mut Context) -> Result<Option<Command>> {
let mut first = match self.first.exec(ctx) { if ctx.break_num > 0 { return Ok(None) }
None => panic!("Invalid OR expression"), let mut first = match self.first.exec(ctx)? {
None => bail!("Invalid OR expression"),
Some(cmd) => cmd Some(cmd) => cmd
}; };
let mut res = match first.spawn() { let res = match first.spawn() {
Result::Err(e) => { Result::Err(_) => {
self.second.exec(ctx) self.second.exec(ctx)?
}, },
Result::Ok(mut res) => { Result::Ok(mut res) => {
if res.wait().unwrap().success() { if res.wait()?.success() {
Some(first) Some(first)
} else { } else {
self.second.exec(ctx) self.second.exec(ctx)?
} }
} }
}; };
res Ok(res)
} }
} }
impl ExecExpression for AndExpression { impl ExecExpression for AndExpression {
fn exec(self, ctx: &mut Context) -> Option<Command> { fn exec(self: &mut AndExpression, ctx: &mut Context) -> Result<Option<Command>> {
let mut first = match self.first.exec(ctx) { if ctx.break_num > 0 { return Ok(None) }
None => panic!("Invalid AND expression"), let mut first = match self.first.exec(ctx)? {
None => bail!("Invalid AND expression"),
Some(cmd) => cmd Some(cmd) => cmd
}; };
let mut res = match first.spawn() { let res = match first.spawn() {
Result::Err(e) => { Result::Err(_) => {
Some(first) Some(first)
}, },
Result::Ok(mut res) => { Result::Ok(mut res) => {
if !res.wait().unwrap().success() { if !res.wait()?.success() {
Some(first) Some(first)
} else { } else {
self.second.exec(ctx) self.second.exec(ctx)?
} }
} }
}; };
res Ok(res)
} }
} }
pub fn exec_tree(tree: Vec<Expression>, ctx: &mut vars::Context) { pub fn exec_tree(tree: Vec<Expression>, ctx: &mut vars::Context) -> Result<()> {
for mut expression in tree { for mut expression in tree {
let mut cmd = expression.exec(ctx); let cmd = expression.exec(ctx)?;
match cmd { match cmd {
None => {}, None => {},
Some(mut cmd) => match cmd.spawn() { Some(mut cmd) => match cmd.spawn() {
@ -325,10 +408,12 @@ pub fn exec_tree(tree: Vec<Expression>, ctx: &mut vars::Context) {
println!("Error executing: {}", e); println!("Error executing: {}", e);
}, },
Result::Ok(mut res) => { Result::Ok(mut res) => {
let out = res.wait().unwrap(); let out = res.wait()?;
ctx.set_var(String::from("?"), Variable::I32(out.code().unwrap_or(1))); ctx.set_var(String::from("?"), Variable::I32(out.code().unwrap_or(1)));
} }
} }
} }
if ctx.break_num > 0 { bail!("Too many break statements") }
} }
Ok(())
} }

View file

@ -13,11 +13,11 @@ pub fn exec(reader: &mut dyn std::io::BufRead, ctx: &mut vars::Context) -> Resul
dbg!(&tokens); dbg!(&tokens);
let expressions = build_tree(tokens); let expressions = build_tree(tokens)?;
dbg!(&expressions); dbg!(&expressions);
exec_tree(expressions?, ctx); exec_tree(expressions, ctx)?;
Ok(()) Ok(())
} }

View file

@ -25,6 +25,7 @@ pub enum Tokens {
FileWrite, FileWrite,
And, And,
Or, Or,
Break,
JobCommandEnd JobCommandEnd
} }
@ -44,8 +45,9 @@ impl Tokens {
">" => Tokens::FileWrite, ">" => Tokens::FileWrite,
"<" => Tokens::FileRead, "<" => Tokens::FileRead,
"|" => Tokens::RedirectInto, "|" => Tokens::RedirectInto,
"\n" | ";" => Tokens::CommandEnd(str.chars().nth(0).unwrap()), "\r\n" | "\n" | ";" => Tokens::CommandEnd(str.chars().nth(0).unwrap()),
"=" => Tokens::ExportSet, "=" => Tokens::ExportSet,
"break" => Tokens::Break,
_ => Tokens::Literal(str) _ => Tokens::Literal(str)
} }
} }
@ -75,6 +77,7 @@ impl Tokens {
Tokens::FileWrite => ">".to_string(), Tokens::FileWrite => ">".to_string(),
Tokens::And => "&&".to_string(), Tokens::And => "&&".to_string(),
Tokens::Or => "||".to_string(), Tokens::Or => "||".to_string(),
Tokens::Break => "break".to_string(),
Tokens::JobCommandEnd => "&".to_string() Tokens::JobCommandEnd => "&".to_string()
} }
} }
@ -114,12 +117,6 @@ fn read_var_ahead(i: usize, text: &String) -> (usize, Tokens) {
(x - i - 1, token) (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::<String>() == keyword
}
pub fn tokenize(reader: &mut dyn std::io::BufRead) -> io::Result<Vec<Tokens>> { pub fn tokenize(reader: &mut dyn std::io::BufRead) -> io::Result<Vec<Tokens>> {
let mut quote_active = false; let mut quote_active = false;
let mut double_quote_active = false; let mut double_quote_active = false;
@ -130,6 +127,10 @@ pub fn tokenize(reader: &mut dyn std::io::BufRead) -> io::Result<Vec<Tokens>> {
let mut tokens: Vec<Tokens> = Vec::new(); let mut tokens: Vec<Tokens> = Vec::new();
fn save_buf(buf: &mut String, tokens: &mut Vec<Tokens>) {
if buf.len() > 0 { tokens.push(Tokens::detect(std::mem::take(buf))) }
}
let mut buf = String::new(); let mut buf = String::new();
let mut skipper = 0; let mut skipper = 0;
for i in 0..text_length { for i in 0..text_length {
@ -143,10 +144,7 @@ pub fn tokenize(reader: &mut dyn std::io::BufRead) -> io::Result<Vec<Tokens>> {
'"' => if !escape_active && !quote_active { double_quote_active = !double_quote_active; buf_add = false }, '"' => 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 && !double_quote_active { quote_active = !quote_active; buf_add = false },
'$' => if !escape_active && !quote_active { '$' => if !escape_active && !quote_active {
if buf.len() > 0 { save_buf(&mut buf, &mut tokens);
tokens.push(Tokens::Literal(buf));
buf = String::new();
}
if text_length > i && text.chars().nth(i + 1).unwrap() == '(' { if text_length > i && text.chars().nth(i + 1).unwrap() == '(' {
tokens.push(Tokens::SubStart); tokens.push(Tokens::SubStart);
skipper = 1; skipper = 1;
@ -172,10 +170,7 @@ pub fn tokenize(reader: &mut dyn std::io::BufRead) -> io::Result<Vec<Tokens>> {
} }
}, },
' ' => if !escape_active && !quote_active && !double_quote_active { ' ' => if !escape_active && !quote_active && !double_quote_active {
if buf.len() > 0 { save_buf(&mut buf, &mut tokens);
tokens.push(Tokens::Literal(buf));
buf = String::new();
}
tokens.push(Tokens::Space); tokens.push(Tokens::Space);
let mut x = i; let mut x = i;
while text.chars().nth(x).unwrap() == ' ' { while text.chars().nth(x).unwrap() == ' ' {
@ -185,134 +180,15 @@ pub fn tokenize(reader: &mut dyn std::io::BufRead) -> io::Result<Vec<Tokens>> {
buf_add = false; buf_add = false;
}, },
'(' => if !quote_active && !double_quote_active && !escape_active { '(' => if !quote_active && !double_quote_active && !escape_active {
if buf.len() > 0 { save_buf(&mut buf, &mut tokens);
tokens.push(Tokens::Literal(buf));
buf = String::new();
}
tokens.push(Tokens::ParenthesisStart); tokens.push(Tokens::ParenthesisStart);
buf_add = false; buf_add = false;
} }
')' => if !quote_active && !double_quote_active && !escape_active { ')' => if !quote_active && !double_quote_active && !escape_active {
if buf.len() > 0 { save_buf(&mut buf, &mut tokens);
tokens.push(Tokens::Literal(buf));
buf = String::new();
}
tokens.push(Tokens::ParenthesisEnd); tokens.push(Tokens::ParenthesisEnd);
buf_add = false; buf_add = false;
}, },
'i' => if !quote_active && !double_quote_active && !escape_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 && !escape_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;
}
},
'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));
buf = String::new();
}
skipper = 5;
tokens.push(Tokens::While);
buf_add = false;
}
},
'f' => if !quote_active && !double_quote_active && !escape_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();
}
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 {
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 { '\\' => if !escape_active {
escape_active = true; escape_active = true;
buf_add = false; buf_add = false;
@ -320,10 +196,7 @@ pub fn tokenize(reader: &mut dyn std::io::BufRead) -> io::Result<Vec<Tokens>> {
escape_active = false; escape_active = false;
}, },
'=' => if !escape_active && !quote_active && !double_quote_active { '=' => if !escape_active && !quote_active && !double_quote_active {
if buf.len() > 0 { save_buf(&mut buf, &mut tokens);
tokens.push(Tokens::Literal(buf));
buf = String::new();
}
tokens.push(Tokens::ExportSet); tokens.push(Tokens::ExportSet);
buf_add = false; buf_add = false;
}, },

View file

@ -1,4 +1,5 @@
use std::collections::HashMap; use std::collections::HashMap;
use anyhow::{bail, Result};
use crate::parser::ast::FunctionDefinitionExpression; use crate::parser::ast::FunctionDefinitionExpression;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -50,43 +51,50 @@ impl Variable {
} }
} }
pub fn index(self: &Self) -> &Variable { pub fn index(self: &Self, index: &Variable) -> Result<&Variable> {
match self { match self {
_ => panic!("Cannot index unsupported types") _ => bail!("Cannot index unsupported types")
} }
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Scope { pub struct Scope {
pub active: bool, /// list of variables
pub vars: HashMap<String, Variable>, pub vars: HashMap<String, Variable>,
pub func: HashMap<String, FunctionDefinitionExpression> /// list of functions
pub func: HashMap<String, FunctionDefinitionExpression>,
/// list of file descriptors, to be closed when the scope is left
pub fd: Vec<usize>
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Context { pub struct Context {
pub scopes: Vec<Scope>, pub scopes: Vec<Scope>,
pub exports: HashMap<String, String>, pub exports: HashMap<String, String>,
pub fd: Vec<usize> pub break_num: u16,
pub continue_num: u16
} }
impl Context { impl Context {
pub fn new() -> Context { pub fn new() -> Context {
Context { let mut res = Context {
scopes: Vec::new(), scopes: Vec::new(),
exports: HashMap::new(), exports: HashMap::new(),
fd: Vec::new() break_num: 0,
} continue_num: 0
};
res.add_scope();
res
} }
pub fn pop_scope(self: &mut Self) -> Option<Scope> { pub fn pop_scope(self: &mut Self) -> Option<Scope> {
self.scopes.pop() self.scopes.pop()
} }
pub fn add_scope(self: &mut Self, active: bool) { pub fn add_scope(self: &mut Self) {
let scope = Scope { let scope = Scope {
active,
func: HashMap::new(), func: HashMap::new(),
vars: HashMap::new() vars: HashMap::new(),
fd: Vec::new()
}; };
self.scopes.push(scope); self.scopes.push(scope);
} }

20
test/if.rush Normal file
View file

@ -0,0 +1,20 @@
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

View file

@ -3,37 +3,7 @@ echo test
echo single; echo line echo single; echo line
echo test > cat echo test > cats
rm cats
grep echo < simple.rush grep echo < test/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

7
test/var.rush Normal file
View file

@ -0,0 +1,7 @@
let test = val
echo var $test
let test = $test
echo var2 ${test}
echo last exit code $?
false
echo last false exit code $?

4
test/while.rush Normal file
View file

@ -0,0 +1,4 @@
while true
echo loop
break
end