mirror of
https://github.com/danbulant/rush
synced 2026-05-19 04:18:35 +00:00
implement string and array native functions
This commit is contained in:
parent
b0a774994f
commit
2b5d7d52cd
8 changed files with 257 additions and 87 deletions
13
src/env.rs
Normal file
13
src/env.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
pub fn os_env_hashmap() -> HashMap<String, String> {
|
||||
let mut map = HashMap::new();
|
||||
use std::env;
|
||||
for (key, val) in env::vars_os() {
|
||||
// Use pattern bindings instead of testing .is_some() followed by .unwrap()
|
||||
if let (Ok(k), Ok(v)) = (key.into_string(), val.into_string()) {
|
||||
map.insert(k, v);
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
30
src/main.rs
30
src/main.rs
|
|
@ -1,4 +1,6 @@
|
|||
mod parser;
|
||||
mod env;
|
||||
mod nativeFunctions;
|
||||
|
||||
use std::io::{self, BufRead, Stdout, Write};
|
||||
use std::cmp;
|
||||
|
|
@ -13,6 +15,8 @@ use termion::event::*;
|
|||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use anyhow::Result;
|
||||
use crate::nativeFunctions::get_native_functions;
|
||||
use crate::parser::vars::Variable;
|
||||
|
||||
struct Term {
|
||||
input: String,
|
||||
|
|
@ -21,10 +25,10 @@ struct Term {
|
|||
|
||||
impl Term {
|
||||
fn new() -> Term {
|
||||
return Term {
|
||||
Term {
|
||||
input: String::new(),
|
||||
idx: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn print(self: &Self, stdout: &mut RawTerminal<Stdout>) {
|
||||
|
|
@ -61,10 +65,10 @@ struct Shell {
|
|||
|
||||
impl Shell {
|
||||
fn new() -> Shell {
|
||||
return Shell {
|
||||
Shell {
|
||||
term: Term::new(),
|
||||
ctx: parser::vars::Context::new()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn collect(&mut self) {
|
||||
|
|
@ -88,7 +92,7 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
Key::Backspace => {
|
||||
if self.term.input.len() > 0 && self.term.idx > 0 {
|
||||
if !self.term.input.is_empty() && self.term.idx > 0 {
|
||||
if self.term.idx == self.term.input.len() - 1 {
|
||||
self.term.input.pop();
|
||||
} else {
|
||||
|
|
@ -138,6 +142,7 @@ impl Shell {
|
|||
|
||||
fn start() {
|
||||
let mut shell = Shell::new();
|
||||
shell.ctx.native_func = get_native_functions();
|
||||
loop {
|
||||
print!("$: ");
|
||||
io::stdout().flush().unwrap();
|
||||
|
|
@ -146,11 +151,9 @@ impl Shell {
|
|||
break;
|
||||
}
|
||||
shell.term.input += "\n";
|
||||
shell.ctx.exports = env::os_env_hashmap().into_iter().map(|(k, v)| (k, Variable::String(v))).collect();
|
||||
let res = parser::exec(&mut shell.term.input.as_bytes(), &mut shell.ctx);
|
||||
match res {
|
||||
Err(err) => eprintln!("rush: {}", err),
|
||||
Ok(_) => {}
|
||||
}
|
||||
if let Err(err) = res { eprintln!("rush: {}", err) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -202,15 +205,8 @@ mod test {
|
|||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::Path;
|
||||
use crate::parser;
|
||||
use crate::{load_and_run, 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")
|
||||
|
|
|
|||
60
src/nativeFunctions.rs
Normal file
60
src/nativeFunctions.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
use std::collections::HashMap;
|
||||
use crate::parser::vars::{Context, NativeFunction, Variable, variables_to_string};
|
||||
use anyhow::{Result, bail};
|
||||
|
||||
pub fn get_native_functions() -> HashMap<String, NativeFunction> {
|
||||
let mut map = HashMap::new();
|
||||
|
||||
fn rush_trim(_ctx: &mut Context, args: Vec<Variable>) -> Result<Variable> {
|
||||
let text = variables_to_string(args);
|
||||
let trimmed = text.trim();
|
||||
Ok(Variable::String(trimmed.to_string()))
|
||||
}
|
||||
map.insert("$trim".to_string(), NativeFunction {
|
||||
name: "$trim".to_string(),
|
||||
description: "Removes leading and trailing whitespaces from a string".to_string(),
|
||||
args: vec![String::from("str")],
|
||||
func: rush_trim
|
||||
});
|
||||
|
||||
fn rush_test(_ctx: &mut Context, args: Vec<Variable>) -> Result<Variable> {
|
||||
if args.len() != 3 {
|
||||
bail!("Expected 3 arguments (source, operand, target), got {}", args.len());
|
||||
}
|
||||
let source = args.get(0).unwrap();
|
||||
let operand = args.get(1).unwrap();
|
||||
let target = args.get(2).unwrap();
|
||||
|
||||
let res = match operand {
|
||||
Variable::String(operand) => {
|
||||
match operand.as_str() {
|
||||
"=" => {
|
||||
if source.to_string() == target.to_string() {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
"!=" => {
|
||||
if source.to_string() != target.to_string() {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
_ => bail!("Unsupported operand: {}", operand)
|
||||
}
|
||||
}
|
||||
_ => bail!("Unsupported operand: {}", operand)
|
||||
};
|
||||
Ok(Variable::I32(res))
|
||||
}
|
||||
map.insert("test".to_string(), NativeFunction {
|
||||
name: "test".to_string(),
|
||||
description: "Compares values. Supported operands are = != > < >= <=".to_string(),
|
||||
args: vec![String::from("source"), String::from("operand"), String::from("target")],
|
||||
func: rush_test
|
||||
});
|
||||
|
||||
map
|
||||
}
|
||||
|
|
@ -47,10 +47,9 @@ pub enum ForValue {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DefinedFunction {
|
||||
name: String,
|
||||
args: Vec<Value>,
|
||||
body: Vec<Expression>
|
||||
pub struct DefinedFunctionCall {
|
||||
pub name: String,
|
||||
pub args: Vec<Value>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -59,8 +58,7 @@ pub enum Value {
|
|||
Variable(String),
|
||||
ArrayVariable(String),
|
||||
ArrayDefinition(Vec<Value>),
|
||||
ArrayFunction(DefinedFunction),
|
||||
StringFunction(DefinedFunction),
|
||||
ValueFunction(DefinedFunctionCall),
|
||||
Expressions(Vec<Expression>),
|
||||
Values(Vec<Value>)
|
||||
}
|
||||
|
|
@ -74,6 +72,8 @@ pub struct FunctionVariable {
|
|||
#[derive(Debug)]
|
||||
pub struct FunctionDefinitionExpression {
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub on_event: Option<String>,
|
||||
pub args: Vec<FunctionVariable>,
|
||||
pub body: Box<Expression>
|
||||
}
|
||||
|
|
@ -139,7 +139,11 @@ impl Tree {
|
|||
loop {
|
||||
if matches!(token, Tokens::Space) {
|
||||
if !buf.is_empty() {
|
||||
if buf.len() == 1 {
|
||||
values.push(CommandValue::Value(buf.pop().unwrap()));
|
||||
} else {
|
||||
values.push(CommandValue::Value(Value::Values(buf)));
|
||||
}
|
||||
buf = Vec::new();
|
||||
}
|
||||
if self.i >= end - 1 { break }
|
||||
|
|
@ -171,6 +175,11 @@ impl Tree {
|
|||
}
|
||||
Value::Literal(token.to_str())
|
||||
}
|
||||
Tokens::StringFunction(_) | Tokens::ArrayFunction(_) => {
|
||||
let val = self.get_value(end, false)?;
|
||||
token = self.get_current_token();
|
||||
val
|
||||
}
|
||||
_ => {
|
||||
Value::Literal(token.to_str())
|
||||
}
|
||||
|
|
@ -187,8 +196,12 @@ impl Tree {
|
|||
}
|
||||
// self.next();
|
||||
if !buf.is_empty() {
|
||||
if buf.len() == 1 {
|
||||
values.push(CommandValue::Value(buf.pop().unwrap()));
|
||||
} else {
|
||||
values.push(CommandValue::Value(Value::Values(buf)));
|
||||
}
|
||||
}
|
||||
Ok(Expression::Command(values))
|
||||
}
|
||||
|
||||
|
|
@ -255,12 +268,26 @@ impl Tree {
|
|||
bail!("Functions not yet implemented")
|
||||
}
|
||||
|
||||
fn parse_array_func(&mut self, _str: String, _end: usize) -> Result<DefinedFunction> {
|
||||
bail!("Array functions not yet implemented");
|
||||
fn parse_string_or_array_func_call(&mut self, end: usize) -> Result<DefinedFunctionCall> {
|
||||
let token = self.get_current_token();
|
||||
let name = match token {
|
||||
Tokens::ArrayFunction(str) => {
|
||||
String::from("@") + str
|
||||
}
|
||||
Tokens::StringFunction(str) => {
|
||||
String::from("$") + str
|
||||
}
|
||||
_ => bail!("Expected string or array function - internal error")
|
||||
}.clone();
|
||||
let mut args = Vec::new();
|
||||
self.inc();
|
||||
loop {
|
||||
args.push(self.get_value(end, false)?);
|
||||
self.inc();
|
||||
if self.i >= end { break }
|
||||
}
|
||||
|
||||
fn parse_string_func(&mut self, _str: String, _end: usize) -> Result<DefinedFunction> {
|
||||
bail!("Array functions not yet implemented");
|
||||
Ok(DefinedFunctionCall { name, args })
|
||||
}
|
||||
|
||||
fn parse_for(&mut self, _end: usize) -> Result<ForExpression> {
|
||||
|
|
@ -360,6 +387,27 @@ impl Tree {
|
|||
Ok(values)
|
||||
}
|
||||
|
||||
fn get_parens_vals(&self, end: usize) -> (usize, usize) {
|
||||
let mut len = 0;
|
||||
let mut lvl = 1;
|
||||
for token in &self.tokens[self.i..end] {
|
||||
match token.token {
|
||||
Tokens::SubStart => lvl += 1,
|
||||
Tokens::StringFunction(_) => lvl += 1,
|
||||
Tokens::ArrayFunction(_) => lvl += 1,
|
||||
Tokens::ParenthesisStart => lvl += 1,
|
||||
Tokens::ParenthesisEnd => lvl -= 1,
|
||||
_ => {}
|
||||
}
|
||||
if lvl == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
len += 1;
|
||||
}
|
||||
(len, lvl)
|
||||
}
|
||||
|
||||
fn get_value(&mut self, end: usize, stop_on_space: bool) -> Result<Value> {
|
||||
let mut token = self.get_current_token();
|
||||
let mut values: Vec<Value> = Vec::new();
|
||||
|
|
@ -381,8 +429,16 @@ impl Tree {
|
|||
Tokens::FileWrite => buf.push(Value::Literal(token.to_str())),
|
||||
Tokens::RedirectInto => bail!("Unexpected token REDIRECT (|)"),
|
||||
Tokens::ParenthesisEnd => bail!("Unexpected token FUNCTION CALL END ())"),
|
||||
Tokens::ArrayFunction(_) => bail!("Unexpected array function"),
|
||||
Tokens::StringFunction(_) => bail!("Unexpected string function"),
|
||||
Tokens::StringFunction(_) | Tokens::ArrayFunction(_) => {
|
||||
self.inc();
|
||||
let (len, lvl) = self.get_parens_vals(end);
|
||||
self.i -= 1;
|
||||
if lvl != 0 {
|
||||
bail!("Parenthesis do not match");
|
||||
}
|
||||
let val = self.parse_string_or_array_func_call(self.i + len)?;
|
||||
return Ok(Value::ValueFunction(val));
|
||||
},
|
||||
Tokens::ParenthesisStart => bail!("Parenthesis not yet implemented"),
|
||||
Tokens::ArrayStart => {
|
||||
let mut len = 0;
|
||||
|
|
@ -410,26 +466,8 @@ impl Tree {
|
|||
},
|
||||
Tokens::ArrayEnd => bail!("Unexpected token ARRAY END (])"),
|
||||
Tokens::SubStart => {
|
||||
let mut len = 0;
|
||||
let mut lvl = 1;
|
||||
let (len, lvl) = self.get_parens_vals(end);
|
||||
self.inc();
|
||||
for token in &self.tokens[self.i..] {
|
||||
match token.token {
|
||||
Tokens::SubStart => lvl += 1,
|
||||
Tokens::StringFunction(_) => lvl += 1,
|
||||
Tokens::ArrayFunction(_) => lvl += 1,
|
||||
Tokens::ParenthesisStart => lvl += 1,
|
||||
Tokens::ParenthesisEnd => lvl -= 1,
|
||||
_ => {}
|
||||
}
|
||||
if lvl == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
if len + self.i == end { break }
|
||||
len += 1;
|
||||
}
|
||||
// self.inc();
|
||||
if lvl != 0 {
|
||||
bail!("Parenthesis do not match");
|
||||
}
|
||||
|
|
@ -466,8 +504,12 @@ impl Tree {
|
|||
token = self.inc().get_current_token();
|
||||
}
|
||||
if !buf.is_empty() {
|
||||
if buf.len() == 1 {
|
||||
values.push(buf.into_iter().next().unwrap());
|
||||
} else {
|
||||
values.push(Value::Values(buf));
|
||||
}
|
||||
}
|
||||
if values.len() == 1 {
|
||||
return Ok(values.into_iter().next().unwrap());
|
||||
}
|
||||
|
|
@ -500,22 +542,8 @@ impl Tree {
|
|||
Tokens::ParenthesisStart => if matches!(expr, Some(_)) {
|
||||
bail!("Unexpected parenthesis. After file redirect, you need to use a semicolon or newline.");
|
||||
} else {
|
||||
let mut len = 1;
|
||||
let mut lvl = 1;
|
||||
self.inc();
|
||||
for token in &self.tokens[self.i..] {
|
||||
match token.token {
|
||||
Tokens::ParenthesisStart => lvl += 1,
|
||||
Tokens::ParenthesisEnd => lvl -= 1,
|
||||
_ => {}
|
||||
}
|
||||
if lvl == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
if len + self.i == end { break }
|
||||
len += 1;
|
||||
}
|
||||
let (len, lvl) = self.get_parens_vals(end);
|
||||
if lvl != 0 {
|
||||
bail!("Parenthesis not ended properly.");
|
||||
}
|
||||
|
|
@ -590,7 +618,7 @@ impl Tree {
|
|||
}
|
||||
|
||||
pub fn build_tree(tokens: Vec<Token>) -> Result<Vec<Expression>> {
|
||||
dbg!(&tokens);
|
||||
// dbg!(&tokens);
|
||||
let mut expressions: Vec<Expression> = Vec::new();
|
||||
let mut tree = Tree { tokens, i: 0 };
|
||||
loop {
|
||||
|
|
@ -604,5 +632,6 @@ pub fn build_tree(tokens: Vec<Token>) -> Result<Vec<Expression>> {
|
|||
}
|
||||
}
|
||||
}
|
||||
// dbg!(&expressions);
|
||||
Ok(expressions)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::process::{Child, Command, Stdio};
|
|||
use std::io;
|
||||
use crate::parser::ast::{AndExpression, BreakExpression, CommandValue, Expression, FileSourceExpression, FileTargetExpression, IfExpression, LetExpression, OrExpression, RedirectTargetExpression, Value, WhileExpression};
|
||||
use crate::parser::vars;
|
||||
use crate::parser::vars::{Context, Variable};
|
||||
use crate::parser::vars::{AnyFunction, Context, Variable};
|
||||
use anyhow::{Result, bail, Context as AnyhowContext};
|
||||
|
||||
trait ExecExpression {
|
||||
|
|
@ -112,10 +112,8 @@ impl GetValue for Value {
|
|||
Value::Literal(str) => {
|
||||
Ok(Variable::String(str.clone()))
|
||||
},
|
||||
Value::Variable(str) => Ok(ctx.get_var(&str).unwrap_or(&mut Variable::String(String::from(""))).clone()),
|
||||
Value::ArrayVariable(str) => Ok(ctx.get_var(&str).unwrap_or(&mut Variable::Array(Vec::new())).clone()),
|
||||
Value::ArrayFunction(_) => todo!("Not implemented yet"),
|
||||
Value::StringFunction(_) => todo!("Not implemented yet"),
|
||||
Value::Variable(str) => Ok(ctx.get_var(str).unwrap_or(&mut Variable::String(String::from(""))).clone()),
|
||||
Value::ArrayVariable(str) => Ok(ctx.get_var(str).unwrap_or(&mut Variable::Array(Vec::new())).clone()),
|
||||
Value::Expressions(expressions) => {
|
||||
let mut out = String::new();
|
||||
ctx.add_scope();
|
||||
|
|
@ -138,8 +136,26 @@ impl GetValue for Value {
|
|||
}
|
||||
Ok(Variable::Array(out))
|
||||
}
|
||||
Value::ValueFunction(call) => {
|
||||
let args = get_variables(ctx, &mut call.args)?;
|
||||
let func = ctx.get_func(call.name.as_str()).with_context(|| format!("Function {} not found", call.name))?;
|
||||
match func {
|
||||
AnyFunction::Native(func) => {
|
||||
(func.func)(ctx, args)
|
||||
}
|
||||
AnyFunction::UserDefined(_) => todo!("User defined functions are not yet supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_variables(ctx: &mut vars::Context, args: &mut Vec<Value>) -> Result<Vec<Variable>> {
|
||||
let mut out = Vec::new();
|
||||
for arg in args {
|
||||
out.push(arg.get(ctx)?);
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
impl ExecExpression for Expression {
|
||||
|
|
@ -167,7 +183,7 @@ 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)?.to_string();
|
||||
let num: u16 = if val.len() > 0 { val.parse()? } else { 1 };
|
||||
let num: u16 = if !val.is_empty() { val.parse()? } else { 1 };
|
||||
ctx.break_num = if num == 0 { 1 } else { num };
|
||||
Ok(None)
|
||||
}
|
||||
|
|
@ -247,7 +263,7 @@ impl ExecExpression for LetExpression {
|
|||
impl ExecExpression for Vec<CommandValue> {
|
||||
fn exec(self: &mut Vec<CommandValue>, ctx: &mut vars::Context) -> Result<Option<Command>> {
|
||||
if ctx.break_num > 0 { return Ok(None) }
|
||||
if self.len() == 0 { bail!("Command with 0 length"); }
|
||||
if self.is_empty() { bail!("Command with 0 length"); }
|
||||
let mut first = self.remove(0);
|
||||
let command_name = first.get(ctx)?.to_string();
|
||||
let mut cmd = Command::new(command_name);
|
||||
|
|
@ -286,8 +302,7 @@ impl ExecExpression for FileTargetExpression {
|
|||
todo!("Redirect without target file");
|
||||
}
|
||||
};
|
||||
let command;
|
||||
match src {
|
||||
let command = match src {
|
||||
Some(mut cmd) => {
|
||||
cmd.stdout(Stdio::piped());
|
||||
let file = File::create(target.to_string());
|
||||
|
|
@ -304,7 +319,7 @@ impl ExecExpression for FileTargetExpression {
|
|||
}
|
||||
}
|
||||
}
|
||||
command = cmd;
|
||||
cmd
|
||||
},
|
||||
None => { bail!("Invalid command provided for file target"); }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -166,7 +166,8 @@ pub fn tokenize(reader: &mut dyn std::io::BufRead) -> Result<Vec<Token>> {
|
|||
skipper = 1;
|
||||
buf_add = false;
|
||||
} else {
|
||||
let (skippers, mut token) = read_var_ahead(i, &text)?;
|
||||
let (mut skippers, mut token) = read_var_ahead(i, &text)?;
|
||||
skippers += 1;
|
||||
match token.token {
|
||||
Tokens::StringVariable(ref str, bool) => if !bool && !double_quote_active && text.len() > i + skippers && text.chars().nth(i + skippers).unwrap() == '(' {
|
||||
token = Token { token: Tokens::StringFunction(str.clone()), end: i + skippers, start: i };
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use anyhow::{bail, Result};
|
||||
use crate::parser::ast::FunctionDefinitionExpression;
|
||||
|
||||
|
|
@ -56,6 +56,17 @@ impl Display for Variable {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn variables_to_string(vars: Vec<Variable>) -> String {
|
||||
let mut str = String::new();
|
||||
for (i, var) in vars.iter().enumerate() {
|
||||
str += &*var.clone().to_string();
|
||||
if i < vars.len() - 1 {
|
||||
str += " ";
|
||||
}
|
||||
}
|
||||
str
|
||||
}
|
||||
|
||||
impl Variable {
|
||||
pub fn index(&self, index: &Variable) -> Result<&Variable> {
|
||||
match self {
|
||||
|
|
@ -134,6 +145,24 @@ impl Variable {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct NativeFunction {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub args: Vec<String>,
|
||||
pub func: fn(&mut Context, Vec<Variable>) -> Result<Variable>
|
||||
}
|
||||
|
||||
impl Debug for NativeFunction {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "NativeFunction {{ name: {}, description: {} }}", self.name, self.description)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AnyFunction<'a> {
|
||||
Native(&'a mut NativeFunction),
|
||||
UserDefined(&'a mut FunctionDefinitionExpression)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Scope {
|
||||
/// list of variables
|
||||
|
|
@ -147,8 +176,13 @@ pub struct Scope {
|
|||
#[derive(Debug)]
|
||||
pub struct Context {
|
||||
pub scopes: Vec<Scope>,
|
||||
pub exports: HashMap<String, String>,
|
||||
/// env variables
|
||||
pub exports: HashMap<String, Variable>,
|
||||
/// list of native functions (Rust functions)
|
||||
pub native_func: HashMap<String, NativeFunction>,
|
||||
/// number of break statements called
|
||||
pub break_num: u16,
|
||||
/// number of continue statements called
|
||||
pub continue_num: u16
|
||||
}
|
||||
|
||||
|
|
@ -157,16 +191,17 @@ impl Context {
|
|||
let mut res = Context {
|
||||
scopes: Vec::new(),
|
||||
exports: HashMap::new(),
|
||||
native_func: HashMap::new(),
|
||||
break_num: 0,
|
||||
continue_num: 0
|
||||
};
|
||||
res.add_scope();
|
||||
res
|
||||
}
|
||||
pub fn pop_scope(self: &mut Self) -> Option<Scope> {
|
||||
pub fn pop_scope(&mut self) -> Option<Scope> {
|
||||
self.scopes.pop()
|
||||
}
|
||||
pub fn add_scope(self: &mut Self) {
|
||||
pub fn add_scope(&mut self) {
|
||||
let scope = Scope {
|
||||
func: HashMap::new(),
|
||||
vars: HashMap::new(),
|
||||
|
|
@ -175,7 +210,16 @@ impl Context {
|
|||
self.scopes.push(scope);
|
||||
}
|
||||
|
||||
pub fn get_var(self: &mut Self, var: &str) -> Option<&mut Variable> {
|
||||
pub fn get_var(&mut self, var: &str) -> Option<&mut Variable> {
|
||||
if var.starts_with("env::") {
|
||||
let key = var.replace("env::", "");
|
||||
return match self.exports.get_mut(&key) {
|
||||
Some(val) => {
|
||||
return Some(val);
|
||||
},
|
||||
None => None
|
||||
}
|
||||
}
|
||||
for scope in self.scopes.iter_mut().rev() {
|
||||
let vars = &mut scope.vars;
|
||||
let val = vars.get_mut(var);
|
||||
|
|
@ -191,25 +235,36 @@ impl Context {
|
|||
|
||||
pub fn set_var(&mut self, key: String, val: Variable) {
|
||||
let vars = &mut self.scopes.last_mut().unwrap().vars;
|
||||
if key.starts_with("env::") {
|
||||
let key = key.replace("env::", "");
|
||||
self.exports.insert(key, Variable::String(val.to_string()));
|
||||
}
|
||||
vars.insert(key, val);
|
||||
}
|
||||
|
||||
pub fn get_func(self: &mut Self, key: &str) -> Option<&mut FunctionDefinitionExpression> {
|
||||
pub fn get_func(&mut self, key: &str) -> Option<AnyFunction> {
|
||||
for scope in self.scopes.iter_mut().rev() {
|
||||
let funcs = &mut scope.func;
|
||||
let val = funcs.get_mut(key);
|
||||
match val {
|
||||
None => {}
|
||||
Some(val) => {
|
||||
return Some(val);
|
||||
return Some(AnyFunction::UserDefined(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
let val = self.native_func.get_mut(key);
|
||||
match val {
|
||||
None => {}
|
||||
Some(val) => {
|
||||
return Some(AnyFunction::Native(val));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn set_func(&mut self, key: String, val: FunctionDefinitionExpression) {
|
||||
let mut func = &mut self.scopes.last_mut().unwrap().func;
|
||||
let func = &mut self.scopes.last_mut().unwrap().func;
|
||||
func.insert(key, val);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
test/builtins.rush
Normal file
1
test/builtins.rush
Normal file
|
|
@ -0,0 +1 @@
|
|||
test $trim("test ") = "test"
|
||||
Loading…
Reference in a new issue