mirror of
https://github.com/danbulant/rush
synced 2026-05-19 04:18:35 +00:00
implement for each, fix while loop
This commit is contained in:
parent
2b5d7d52cd
commit
c25c604bc3
6 changed files with 265 additions and 56 deletions
|
|
@ -91,8 +91,8 @@ Slices: `[x..y]` gets a substring (or subarray) of the variable. When `x` ommite
|
|||
Bracketless. Scopes are ended by the keyword `end`.
|
||||
|
||||
- `if` - Runs it's scope if the command returns `0`. Useful in pair with `test` builtin. `else` supported. `else if` doesn't require another `end`.
|
||||
- `for $val of @arr` - Runs for each value of the array (or hashmap)
|
||||
- `for $val of X..Y` - Runs for each number in the range `X` and `Y` (both inclusive).
|
||||
- `for val of @arr` - Runs for each value of the array (or hashmap)
|
||||
- `for val of X..Y` - Runs for each number in the range `X` and `Y` (both inclusive).
|
||||
- `while` - Runs in loop as long as the command returns `0`
|
||||
|
||||
### Functions
|
||||
|
|
@ -113,9 +113,11 @@ Use `source` to load external files with functions to be triggered.
|
|||
* `let` for assigning variables (`let var = value`)
|
||||
* `export` for exporting variables to env (`export var` to export var, or `export var = value`)
|
||||
* `test` tests for evaluation (`=` for equality, `>`, `<`, `<=`, `>=` for number comparisons)
|
||||
* `exists` for existance of a given string, or if given a flag (`-F`unctions, `-v`ariables, `-e`nv, `-f`ile, `-d`irectory, `-r`eadable file, `-w`ritable file, e`-x`ecutable file), existence of the selected object
|
||||
* `exists` for existence of a given string, or if given a flag (`-F`unctions, `-v`ariables, `-e`nv, `-f`ile, `-d`irectory, `-r`eadable file, `-w`ritable file, e`-x`ecutable file), existence of the selected object
|
||||
* `true` returns `0`
|
||||
* `false` returns `1`
|
||||
* `source` to run another file in the same file scope
|
||||
* `typeof` returns the type of arguments passed
|
||||
* `inspect` shows the object in a debug output
|
||||
|
||||
Some GNU standard utils may be overwritten by rush builtins, but must be made compatible.
|
||||
|
|
|
|||
|
|
@ -56,5 +56,80 @@ pub fn get_native_functions() -> HashMap<String, NativeFunction> {
|
|||
func: rush_test
|
||||
});
|
||||
|
||||
fn rush_true(_ctx: &mut Context, _args: Vec<Variable>) -> Result<Variable> {
|
||||
Ok(Variable::I32(0))
|
||||
}
|
||||
map.insert("true".to_string(), NativeFunction {
|
||||
name: "true".to_string(),
|
||||
description: "Returns 0".to_string(),
|
||||
args: vec![],
|
||||
func: rush_true
|
||||
});
|
||||
|
||||
fn rush_false(_ctx: &mut Context, _args: Vec<Variable>) -> Result<Variable> {
|
||||
Ok(Variable::I32(1))
|
||||
}
|
||||
map.insert("false".to_string(), NativeFunction {
|
||||
name: "false".to_string(),
|
||||
description: "Returns 1".to_string(),
|
||||
args: vec![],
|
||||
func: rush_false
|
||||
});
|
||||
|
||||
fn rush_export(ctx: &mut Context, args: Vec<Variable>) -> Result<Variable> {
|
||||
if args.len() != 1 && args.len() != 3 {
|
||||
bail!("Expected 1 (name) or 3 (name = value) arguments, got {}", args.len());
|
||||
}
|
||||
let name = args.get(0).unwrap();
|
||||
if args.len() == 1 {
|
||||
let value = ctx.get_var(&name.to_string());
|
||||
match value {
|
||||
Some(value) => {
|
||||
let val = value.clone();
|
||||
ctx.exports.insert(name.to_string(), val);
|
||||
}
|
||||
None => return Ok(Variable::I32(1))
|
||||
}
|
||||
} else {
|
||||
let value = args.get(2).unwrap();
|
||||
ctx.set_var(name.to_string(), value.clone());
|
||||
}
|
||||
Ok(Variable::I32(0))
|
||||
}
|
||||
map.insert("export".to_string(), NativeFunction {
|
||||
name: "export".to_string(),
|
||||
description: "Exports a variable to the environment".to_string(),
|
||||
args: vec![String::from("name"), String::from("="), String::from("value")],
|
||||
func: rush_export
|
||||
});
|
||||
|
||||
fn rush_typeof(_ctx: &mut Context, args: Vec<Variable>) -> Result<Variable> {
|
||||
if args.len() != 1 {
|
||||
bail!("Expected 1 argument, got {}", args.len());
|
||||
}
|
||||
let arg = args.get(0).unwrap();
|
||||
let res = match arg {
|
||||
Variable::String(_) => "string",
|
||||
Variable::I32(_) => "i32",
|
||||
Variable::I64(_) => "i64",
|
||||
Variable::I128(_) => "i128",
|
||||
Variable::U32(_) => "u32",
|
||||
Variable::U64(_) => "u64",
|
||||
Variable::U128(_) => "u128",
|
||||
Variable::F32(_) => "f32",
|
||||
Variable::F64(_) => "f64",
|
||||
Variable::Bool(_) => "bool",
|
||||
Variable::Array(_) => "array",
|
||||
Variable::HMap(_) => "HMap"
|
||||
};
|
||||
Ok(Variable::String(res.to_string()))
|
||||
}
|
||||
map.insert("typeof".to_string(), NativeFunction {
|
||||
name: "typeof".to_string(),
|
||||
description: "Returns the type of a variable".to_string(),
|
||||
args: vec![String::from("var")],
|
||||
func: rush_typeof
|
||||
});
|
||||
|
||||
map
|
||||
}
|
||||
|
|
@ -1,58 +1,60 @@
|
|||
use crate::parser::tokens::{Token, Tokens};
|
||||
use anyhow::{bail, Context, Result};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LetExpression {
|
||||
pub key: Box<Value>,
|
||||
pub vartype: Option<String>,
|
||||
pub value: Box<Value>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AndExpression {
|
||||
pub first: Box<Expression>,
|
||||
pub second: Box<Expression>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OrExpression {
|
||||
pub first: Box<Expression>,
|
||||
pub second: Box<Expression>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IfExpression {
|
||||
pub condition: Box<Expression>,
|
||||
pub contents: Vec<Expression>,
|
||||
pub else_contents: Vec<Expression>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WhileExpression {
|
||||
pub condition: Box<Expression>,
|
||||
pub contents: Vec<Expression>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ForExpression {
|
||||
pub key: String,
|
||||
pub list: Box<Value>,
|
||||
pub contents: Vec<Expression>
|
||||
pub arg_value: Value,
|
||||
pub arg_key: Option<Value>,
|
||||
pub list: Value,
|
||||
pub contents: Vec<Expression>,
|
||||
pub else_contents: Vec<Expression>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ForValue {
|
||||
Value(Value),
|
||||
Range(Option<u32>, Option<u32>)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DefinedFunctionCall {
|
||||
pub name: String,
|
||||
pub args: Vec<Value>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Value {
|
||||
Literal(String),
|
||||
Variable(String),
|
||||
|
|
@ -63,13 +65,13 @@ pub enum Value {
|
|||
Values(Vec<Value>)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FunctionVariable {
|
||||
pub name: String,
|
||||
pub vartype: Option<String>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FunctionDefinitionExpression {
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
|
|
@ -78,36 +80,36 @@ pub struct FunctionDefinitionExpression {
|
|||
pub body: Box<Expression>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RedirectTargetExpression {
|
||||
pub source: Box<Expression>,
|
||||
pub target: Box<Expression>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FileTargetExpression {
|
||||
pub source: Option<Box<Expression>>,
|
||||
pub target: Box<Value>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FileSourceExpression {
|
||||
pub source: Box<Value>,
|
||||
pub target: Option<Box<Expression>>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CommandValue {
|
||||
Value(Value),
|
||||
Var(String, Value)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BreakExpression {
|
||||
pub num: Box<Value>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Expression {
|
||||
LetExpression(LetExpression),
|
||||
Command(Vec<CommandValue>),
|
||||
|
|
@ -278,7 +280,7 @@ impl Tree {
|
|||
String::from("$") + str
|
||||
}
|
||||
_ => bail!("Expected string or array function - internal error")
|
||||
}.clone();
|
||||
};
|
||||
let mut args = Vec::new();
|
||||
self.inc();
|
||||
loop {
|
||||
|
|
@ -290,30 +292,48 @@ impl Tree {
|
|||
Ok(DefinedFunctionCall { name, args })
|
||||
}
|
||||
|
||||
fn parse_for(&mut self, _end: usize) -> Result<ForExpression> {
|
||||
bail!("For loop not yet implemented");
|
||||
}
|
||||
|
||||
fn parse_if(&mut self, end: usize) -> Result<IfExpression> {
|
||||
fn parse_for(&mut self, end: usize) -> Result<ForExpression> {
|
||||
self.inc();
|
||||
let condition = self.get_expression(end).with_context(|| "Error getting condition for if expression")?;
|
||||
let arg_value = self.get_value(end, true)?;
|
||||
let arg_key = match self.get_value(end, true)? {
|
||||
Value::Literal(k) if k == "in" => None,
|
||||
any => Some(any)
|
||||
};
|
||||
if matches!(arg_key, Some(_)) {
|
||||
match self.get_value(end, true)? {
|
||||
Value::Literal(k) if k == "in" => {},
|
||||
_ => bail!("Expected 'in' after for key")
|
||||
}
|
||||
self.inc();
|
||||
}
|
||||
let list = self.get_value(end, false)?;
|
||||
|
||||
let mut contents = Vec::new();
|
||||
|
||||
loop {
|
||||
match self.get_current_token() {
|
||||
Tokens::End => break,
|
||||
Tokens::Space => {},
|
||||
Tokens::Else => break,
|
||||
Tokens::CommandEnd(_) => {}
|
||||
_ => contents.push(self.get_expression(end).with_context(|| "Error getting contents for if expression")?)
|
||||
};
|
||||
_ => contents.push(self.get_expression(end).with_context(|| "Error getting contents for for expression")?)
|
||||
}
|
||||
if self.i >= end - 1 { break }
|
||||
self.inc();
|
||||
}
|
||||
let else_contents = self.parse_else(end)?;
|
||||
|
||||
Ok(ForExpression { arg_key, arg_value, contents, else_contents, list })
|
||||
}
|
||||
|
||||
fn parse_else(&mut self, end: usize) -> Result<Vec<Expression>> {
|
||||
loop {
|
||||
match self.get_current_token() {
|
||||
Tokens::CommandEnd(_) => { self.inc(); },
|
||||
Tokens::Space => { self.inc(); },
|
||||
_ => break
|
||||
}
|
||||
if self.i >= end - 1 { return Ok(Vec::new()) }
|
||||
}
|
||||
let mut else_contents = Vec::new();
|
||||
if matches!(self.get_current_token(), Tokens::Else) {
|
||||
|
|
@ -331,19 +351,39 @@ impl Tree {
|
|||
_ => else_contents.push(self.get_expression(end).with_context(|| "Error getting contents for if expression")?)
|
||||
};
|
||||
self.inc();
|
||||
if self.i >= end { break }
|
||||
}
|
||||
}
|
||||
if self.i < self.tokens.len() {
|
||||
if self.i < end {
|
||||
loop {
|
||||
match self.get_current_token() {
|
||||
Tokens::CommandEnd(_) => { self.inc(); },
|
||||
Tokens::Space => { self.inc(); },
|
||||
_ => break
|
||||
}
|
||||
if self.i >= self.tokens.len() - 1 { break }
|
||||
if self.i >= end - 1 { break }
|
||||
}
|
||||
self.inc();
|
||||
}
|
||||
Ok(else_contents)
|
||||
}
|
||||
|
||||
fn parse_if(&mut self, end: usize) -> Result<IfExpression> {
|
||||
self.inc();
|
||||
let condition = self.get_expression(end).with_context(|| "Error getting condition for if expression")?;
|
||||
let mut contents = Vec::new();
|
||||
loop {
|
||||
match self.get_current_token() {
|
||||
Tokens::End => break,
|
||||
Tokens::Space => {},
|
||||
Tokens::Else => break,
|
||||
Tokens::CommandEnd(_) => {}
|
||||
_ => contents.push(self.get_expression(end).with_context(|| "Error getting contents for if expression")?)
|
||||
};
|
||||
self.inc();
|
||||
if self.i >= end { break }
|
||||
}
|
||||
let else_contents = self.parse_else(end)?;
|
||||
Ok(IfExpression { condition: Box::new(condition), contents, else_contents })
|
||||
}
|
||||
|
||||
|
|
@ -377,10 +417,10 @@ impl Tree {
|
|||
|
||||
fn parse_array_definition(&mut self, end: usize) -> Result<Vec<Value>> {
|
||||
let mut values: Vec<Value> = Vec::new();
|
||||
if matches!(self.get_current_token(), Tokens::Space) { self.inc(); }
|
||||
loop {
|
||||
if self.i >= end - 1 { break; }
|
||||
if self.i >= end { break; }
|
||||
let val = self.get_value(end, true)?;
|
||||
self.inc();
|
||||
values.push(val);
|
||||
if matches!(self.get_current_token(), Tokens::Space) { self.inc(); }
|
||||
}
|
||||
|
|
@ -415,8 +455,8 @@ impl Tree {
|
|||
loop {
|
||||
match token {
|
||||
Tokens::Space => {
|
||||
if stop_on_space { break; }
|
||||
if buf.is_empty() { token = self.inc().get_current_token(); continue; }
|
||||
if stop_on_space { break; }
|
||||
values.push(Value::Values(buf));
|
||||
buf = Vec::new();
|
||||
if self.i >= end - 1 { break }
|
||||
|
|
@ -453,16 +493,15 @@ impl Tree {
|
|||
if lvl == 0 {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
|
||||
if len + self.i == end { break }
|
||||
len += 1;
|
||||
}
|
||||
if lvl != 0 {
|
||||
bail!("Parenthesis do not match");
|
||||
}
|
||||
let val = Value::ArrayDefinition(self.parse_array_definition(self.i + len)?);
|
||||
values.push(val);
|
||||
self.inc();
|
||||
},
|
||||
Tokens::ArrayEnd => bail!("Unexpected token ARRAY END (])"),
|
||||
Tokens::SubStart => {
|
||||
|
|
@ -599,7 +638,7 @@ impl Tree {
|
|||
Some(_) => bail!("Unexpected break")
|
||||
}
|
||||
Tokens::JobCommandEnd => bail!("Jobs not yet implemented")
|
||||
}
|
||||
};
|
||||
if self.i >= end - 1 { break }
|
||||
token = self.get_current_token();
|
||||
}
|
||||
|
|
@ -614,7 +653,6 @@ impl Tree {
|
|||
self
|
||||
}
|
||||
fn get_current_token(&self) -> &Tokens { &self.tokens.get(self.i).unwrap().token }
|
||||
fn get_next_token(&self) -> &Tokens { &self.tokens.get(self.i + 1).unwrap().token }
|
||||
}
|
||||
|
||||
pub fn build_tree(tokens: Vec<Token>) -> Result<Vec<Expression>> {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::fs::File;
|
||||
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::ast::{AndExpression, BreakExpression, CommandValue, Expression, FileSourceExpression, FileTargetExpression, ForExpression, IfExpression, LetExpression, OrExpression, RedirectTargetExpression, Value, WhileExpression};
|
||||
use crate::parser::vars;
|
||||
use crate::parser::vars::{AnyFunction, Context, Variable};
|
||||
use anyhow::{Result, bail, Context as AnyhowContext};
|
||||
|
|
@ -50,7 +50,7 @@ impl ExecResult {
|
|||
self.spawn();
|
||||
let child = self.child.unwrap();
|
||||
let mut stdout = child.stdout.unwrap();
|
||||
io::copy(&mut stdout, into);
|
||||
io::copy(&mut stdout, into).unwrap();
|
||||
}
|
||||
}
|
||||
into
|
||||
|
|
@ -59,7 +59,7 @@ impl ExecResult {
|
|||
///
|
||||
/// Uses `redirect_from_result` of the next result. Spawns this result, but not the next one.
|
||||
fn redirect_into_result(&mut self, into: &mut ExecResult) -> &mut Self {
|
||||
into.redirect_from_result(self);
|
||||
into.redirect_from_result(self).unwrap();
|
||||
self
|
||||
}
|
||||
/// Redirects the `from` into the current pending result
|
||||
|
|
@ -167,7 +167,7 @@ impl ExecExpression for Expression {
|
|||
Expression::Function(_) => todo!("Function definition"),
|
||||
Expression::IfExpression(expr) => expr.exec(ctx),
|
||||
Expression::WhileExpression(expr) => expr.exec(ctx),
|
||||
Expression::ForExpression(_) => todo!("For expression"),
|
||||
Expression::ForExpression(expr) => expr.exec(ctx),
|
||||
Expression::RedirectTargetExpression(expr) => expr.exec(ctx),
|
||||
Expression::FileTargetExpression(expr) => expr.exec(ctx),
|
||||
Expression::FileSourceExpression(expr) => expr.exec(ctx),
|
||||
|
|
@ -197,18 +197,24 @@ impl ExecExpression for WhileExpression {
|
|||
Some(cmd) => cmd
|
||||
};
|
||||
ctx.add_scope();
|
||||
let mut res;
|
||||
let mut res = None;
|
||||
loop {
|
||||
match condition.spawn() {
|
||||
Result::Err(_) => {
|
||||
Err(_) => {
|
||||
res = Some(condition);
|
||||
break;
|
||||
},
|
||||
Result::Ok(mut child) => {
|
||||
Ok(mut child) => {
|
||||
if !child.wait()?.success() {
|
||||
res = Some(condition);
|
||||
break
|
||||
} else {
|
||||
match res {
|
||||
None => {},
|
||||
Some(mut cmd) => {
|
||||
wait_child(cmd.spawn()?, ctx)?;
|
||||
}
|
||||
}
|
||||
res = self.contents.exec(ctx)?
|
||||
}
|
||||
}
|
||||
|
|
@ -224,6 +230,80 @@ impl ExecExpression for WhileExpression {
|
|||
}
|
||||
}
|
||||
|
||||
impl ExecExpression for ForExpression {
|
||||
fn exec<'a>(&mut self, ctx: &mut Context) -> Result<Option<Command>> {
|
||||
if ctx.break_num > 0 { ctx.break_num -= 1; return Ok(None) }
|
||||
let arg_value = self.arg_value.get(ctx)?;
|
||||
let arg_key = match &self.arg_key {
|
||||
None => None,
|
||||
Some(key) => {
|
||||
let mut lkey: Value = key.clone();
|
||||
let res = lkey.get(ctx)?;
|
||||
Some(res)
|
||||
}
|
||||
};
|
||||
let mut res: Option<Command> = None;
|
||||
let list = self.list.get(ctx)?;
|
||||
|
||||
fn process(i: usize, val: Variable, ctx: &mut Context, arg_key: &Option<Variable>, arg_value: &Variable) -> Result<()> {
|
||||
ctx.add_scope();
|
||||
if let Some(key) = &arg_key {
|
||||
ctx.set_var(key.to_string(), Variable::U64(i as u64));
|
||||
}
|
||||
ctx.set_var(arg_value.to_string(), val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
match list {
|
||||
Variable::Array(arr) => {
|
||||
if arr.is_empty() {
|
||||
self.else_contents.exec(ctx)?;
|
||||
} else {
|
||||
for (i, val) in arr.iter().enumerate() {
|
||||
process(i, val.clone(), ctx, &arg_key, &arg_value)?;
|
||||
match res {
|
||||
None => {},
|
||||
Some(mut cmd) => {
|
||||
wait_child(cmd.spawn()?, ctx)?;
|
||||
}
|
||||
}
|
||||
res = self.contents.exec(ctx)?;
|
||||
ctx.pop_scope();
|
||||
if ctx.break_num > 0 {
|
||||
ctx.break_num -= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Variable::String(str) => {
|
||||
if str.is_empty() {
|
||||
self.else_contents.exec(ctx)?;
|
||||
} else {
|
||||
for (i, char) in str.chars().enumerate() {
|
||||
process(i, Variable::String(char.to_string()), ctx, &arg_key, &arg_value)?;
|
||||
match res {
|
||||
None => {},
|
||||
Some(mut cmd) => {
|
||||
wait_child(cmd.spawn()?, ctx)?;
|
||||
}
|
||||
}
|
||||
res = self.contents.exec(ctx)?;
|
||||
ctx.pop_scope();
|
||||
if ctx.break_num > 0 {
|
||||
ctx.break_num -= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => bail!("Invalid for expression")
|
||||
};
|
||||
|
||||
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) }
|
||||
|
|
@ -264,10 +344,10 @@ 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.is_empty() { bail!("Command with 0 length"); }
|
||||
let mut first = self.remove(0);
|
||||
let first = self.get_mut(0).unwrap();
|
||||
let command_name = first.get(ctx)?.to_string();
|
||||
let mut cmd = Command::new(command_name);
|
||||
for value in self {
|
||||
for value in &mut self[1..] {
|
||||
cmd.arg(value.get(ctx)?.to_string());
|
||||
}
|
||||
Ok(Some(cmd))
|
||||
|
|
@ -412,6 +492,12 @@ impl ExecExpression for AndExpression {
|
|||
}
|
||||
}
|
||||
|
||||
fn wait_child(mut child: Child, ctx: &mut Context) -> Result<()> {
|
||||
let out = child.wait()?;
|
||||
ctx.set_var(String::from("?"), Variable::I32(out.code().unwrap_or(1)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn exec_tree(tree: Vec<Expression>, ctx: &mut vars::Context) -> Result<()> {
|
||||
for mut expression in tree {
|
||||
let cmd = expression.exec(ctx)?;
|
||||
|
|
@ -422,8 +508,7 @@ pub fn exec_tree(tree: Vec<Expression>, ctx: &mut vars::Context) -> Result<()> {
|
|||
println!("Error executing: {}", e);
|
||||
},
|
||||
Result::Ok(mut res) => {
|
||||
let out = res.wait()?;
|
||||
ctx.set_var(String::from("?"), Variable::I32(out.code().unwrap_or(1)));
|
||||
wait_child(res, ctx)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,12 +167,13 @@ pub fn tokenize(reader: &mut dyn std::io::BufRead) -> Result<Vec<Token>> {
|
|||
buf_add = false;
|
||||
} else {
|
||||
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() == '(' {
|
||||
Tokens::StringVariable(ref str, bool) => if !bool && !double_quote_active && text.len() > i + skippers + 1 && text.chars().nth(i + skippers + 1).unwrap() == '(' {
|
||||
skippers += 1;
|
||||
token = Token { token: Tokens::StringFunction(str.clone()), end: i + skippers, start: i };
|
||||
},
|
||||
Tokens::ArrayVariable(ref str, bool) => if !bool && !double_quote_active && text.len() > i + skippers && text.chars().nth(i + skippers).unwrap() == '(' {
|
||||
Tokens::ArrayVariable(ref str, bool) => if !bool && !double_quote_active && text.len() > i + skippers + 1 && text.chars().nth(i + skippers + 1).unwrap() == '(' {
|
||||
skippers += 1;
|
||||
token = Token { token: Tokens::ArrayFunction(str.clone()), end: i+skippers, start: i };
|
||||
}
|
||||
_ => bail!("Cannot happen")
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ pub enum Variable {
|
|||
F32(f32),
|
||||
F64(f64),
|
||||
HMap(HashMap<String, Variable>),
|
||||
Array(Vec<Variable>)
|
||||
Array(Vec<Variable>),
|
||||
Bool(bool)
|
||||
}
|
||||
|
||||
impl Display for Variable {
|
||||
|
|
@ -32,6 +33,13 @@ impl Display for Variable {
|
|||
Variable::U128(num) => num.to_string(),
|
||||
Variable::F32(num) => num.to_string(),
|
||||
Variable::F64(num) => num.to_string(),
|
||||
Variable::Bool(val) => {
|
||||
if *val {
|
||||
String::from("true")
|
||||
} else {
|
||||
String::from("false")
|
||||
}
|
||||
},
|
||||
Variable::HMap(_map) => {
|
||||
String::from("[Object object]")
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue