mirror of
https://github.com/danbulant/rush
synced 2026-07-04 02:30:44 +00:00
various improvements, basic (currently failing) tests, improved lexer
This commit is contained in:
parent
b43959fa79
commit
aa33fb90ea
12 changed files with 439 additions and 300 deletions
88
Cargo.lock
generated
88
Cargo.lock
generated
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
42
src/main.rs
42
src/main.rs
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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
20
test/if.rush
Normal 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
|
||||||
|
|
@ -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
7
test/var.rush
Normal 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
4
test/while.rush
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
while true
|
||||||
|
echo loop
|
||||||
|
break
|
||||||
|
end
|
||||||
Loading…
Reference in a new issue