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