feat: some new progress

This commit is contained in:
Daniel Bulant 2021-12-05 14:45:54 +01:00
parent 845f00df0b
commit 78d4cea4fe
8 changed files with 677 additions and 105 deletions

20
.idea/git_toolbox_prj.xml Normal file
View 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
View file

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

View file

@ -9,3 +9,4 @@ edition = "2018"
[dependencies]
utf8-chars = "1.0.0"
termion = "1.5.6"
filedescriptor = "0.8.1"

View file

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

View file

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

View file

@ -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
View 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)
}

View file

@ -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);
}
}