implement for each, fix while loop

This commit is contained in:
Daniel Bulant 2023-08-05 21:40:30 +02:00
parent 2b5d7d52cd
commit c25c604bc3
6 changed files with 265 additions and 56 deletions

View file

@ -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`. 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`. - `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 @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 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` - `while` - Runs in loop as long as the command returns `0`
### Functions ### Functions
@ -113,9 +113,11 @@ Use `source` to load external files with functions to be triggered.
* `let` for assigning variables (`let var = value`) * `let` for assigning variables (`let var = value`)
* `export` for exporting variables to env (`export var` to export var, or `export 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) * `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` * `true` returns `0`
* `false` returns `1` * `false` returns `1`
* `source` to run another file in the same file scope * `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. Some GNU standard utils may be overwritten by rush builtins, but must be made compatible.

View file

@ -56,5 +56,80 @@ pub fn get_native_functions() -> HashMap<String, NativeFunction> {
func: rush_test 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 map
} }

View file

@ -1,58 +1,60 @@
use crate::parser::tokens::{Token, Tokens}; use crate::parser::tokens::{Token, Tokens};
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct LetExpression { pub struct LetExpression {
pub key: Box<Value>, pub key: Box<Value>,
pub vartype: Option<String>, pub vartype: Option<String>,
pub value: Box<Value> pub value: Box<Value>
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct AndExpression { pub struct AndExpression {
pub first: Box<Expression>, pub first: Box<Expression>,
pub second: Box<Expression> pub second: Box<Expression>
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct OrExpression { pub struct OrExpression {
pub first: Box<Expression>, pub first: Box<Expression>,
pub second: Box<Expression> pub second: Box<Expression>
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct IfExpression { pub struct IfExpression {
pub condition: Box<Expression>, pub condition: Box<Expression>,
pub contents: Vec<Expression>, pub contents: Vec<Expression>,
pub else_contents: Vec<Expression> pub else_contents: Vec<Expression>
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct WhileExpression { pub struct WhileExpression {
pub condition: Box<Expression>, pub condition: Box<Expression>,
pub contents: Vec<Expression> pub contents: Vec<Expression>
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct ForExpression { pub struct ForExpression {
pub key: String, pub arg_value: Value,
pub list: Box<Value>, pub arg_key: Option<Value>,
pub contents: Vec<Expression> pub list: Value,
pub contents: Vec<Expression>,
pub else_contents: Vec<Expression>
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum ForValue { pub enum ForValue {
Value(Value), Value(Value),
Range(Option<u32>, Option<u32>) Range(Option<u32>, Option<u32>)
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct DefinedFunctionCall { pub struct DefinedFunctionCall {
pub name: String, pub name: String,
pub args: Vec<Value> pub args: Vec<Value>
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum Value { pub enum Value {
Literal(String), Literal(String),
Variable(String), Variable(String),
@ -63,13 +65,13 @@ pub enum Value {
Values(Vec<Value>) Values(Vec<Value>)
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct FunctionVariable { pub struct FunctionVariable {
pub name: String, pub name: String,
pub vartype: Option<String> pub vartype: Option<String>
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct FunctionDefinitionExpression { pub struct FunctionDefinitionExpression {
pub name: String, pub name: String,
pub description: Option<String>, pub description: Option<String>,
@ -78,36 +80,36 @@ pub struct FunctionDefinitionExpression {
pub body: Box<Expression> pub body: Box<Expression>
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct RedirectTargetExpression { pub struct RedirectTargetExpression {
pub source: Box<Expression>, pub source: Box<Expression>,
pub target: Box<Expression> pub target: Box<Expression>
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct FileTargetExpression { pub struct FileTargetExpression {
pub source: Option<Box<Expression>>, pub source: Option<Box<Expression>>,
pub target: Box<Value> pub target: Box<Value>
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct FileSourceExpression { pub struct FileSourceExpression {
pub source: Box<Value>, pub source: Box<Value>,
pub target: Option<Box<Expression>> pub target: Option<Box<Expression>>
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum CommandValue { pub enum CommandValue {
Value(Value), Value(Value),
Var(String, Value) Var(String, Value)
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct BreakExpression { pub struct BreakExpression {
pub num: Box<Value> pub num: Box<Value>
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum Expression { pub enum Expression {
LetExpression(LetExpression), LetExpression(LetExpression),
Command(Vec<CommandValue>), Command(Vec<CommandValue>),
@ -278,7 +280,7 @@ impl Tree {
String::from("$") + str String::from("$") + str
} }
_ => bail!("Expected string or array function - internal error") _ => bail!("Expected string or array function - internal error")
}.clone(); };
let mut args = Vec::new(); let mut args = Vec::new();
self.inc(); self.inc();
loop { loop {
@ -290,30 +292,48 @@ impl Tree {
Ok(DefinedFunctionCall { name, args }) Ok(DefinedFunctionCall { name, args })
} }
fn parse_for(&mut self, _end: usize) -> Result<ForExpression> { fn parse_for(&mut self, end: usize) -> Result<ForExpression> {
bail!("For loop not yet implemented");
}
fn parse_if(&mut self, end: usize) -> Result<IfExpression> {
self.inc(); 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(); let mut contents = Vec::new();
loop { loop {
match self.get_current_token() { match self.get_current_token() {
Tokens::End => break, Tokens::End => break,
Tokens::Space => {}, Tokens::Space => {},
Tokens::Else => break, Tokens::Else => break,
Tokens::CommandEnd(_) => {} 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(); 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 { loop {
match self.get_current_token() { match self.get_current_token() {
Tokens::CommandEnd(_) => { self.inc(); }, Tokens::CommandEnd(_) => { self.inc(); },
Tokens::Space => { self.inc(); }, Tokens::Space => { self.inc(); },
_ => break _ => break
} }
if self.i >= end - 1 { return Ok(Vec::new()) }
} }
let mut else_contents = Vec::new(); let mut else_contents = Vec::new();
if matches!(self.get_current_token(), Tokens::Else) { 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")?) _ => else_contents.push(self.get_expression(end).with_context(|| "Error getting contents for if expression")?)
}; };
self.inc(); self.inc();
if self.i >= end { break }
} }
} }
if self.i < self.tokens.len() { if self.i < end {
loop { loop {
match self.get_current_token() { match self.get_current_token() {
Tokens::CommandEnd(_) => { self.inc(); }, Tokens::CommandEnd(_) => { self.inc(); },
Tokens::Space => { self.inc(); }, Tokens::Space => { self.inc(); },
_ => break _ => break
} }
if self.i >= self.tokens.len() - 1 { break } if self.i >= end - 1 { break }
} }
self.inc(); 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 }) 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>> { fn parse_array_definition(&mut self, end: usize) -> Result<Vec<Value>> {
let mut values: Vec<Value> = Vec::new(); let mut values: Vec<Value> = Vec::new();
if matches!(self.get_current_token(), Tokens::Space) { self.inc(); }
loop { loop {
if self.i >= end - 1 { break; } if self.i >= end { break; }
let val = self.get_value(end, true)?; let val = self.get_value(end, true)?;
self.inc();
values.push(val); values.push(val);
if matches!(self.get_current_token(), Tokens::Space) { self.inc(); } if matches!(self.get_current_token(), Tokens::Space) { self.inc(); }
} }
@ -415,8 +455,8 @@ impl Tree {
loop { loop {
match token { match token {
Tokens::Space => { Tokens::Space => {
if stop_on_space { break; }
if buf.is_empty() { token = self.inc().get_current_token(); continue; } if buf.is_empty() { token = self.inc().get_current_token(); continue; }
if stop_on_space { break; }
values.push(Value::Values(buf)); values.push(Value::Values(buf));
buf = Vec::new(); buf = Vec::new();
if self.i >= end - 1 { break } if self.i >= end - 1 { break }
@ -453,16 +493,15 @@ impl Tree {
if lvl == 0 { if lvl == 0 {
break; break;
} }
len += 1;
if len + self.i == end { break } if len + self.i == end { break }
len += 1;
} }
if lvl != 0 { if lvl != 0 {
bail!("Parenthesis do not match"); bail!("Parenthesis do not match");
} }
let val = Value::ArrayDefinition(self.parse_array_definition(self.i + len)?); let val = Value::ArrayDefinition(self.parse_array_definition(self.i + len)?);
values.push(val); values.push(val);
self.inc();
}, },
Tokens::ArrayEnd => bail!("Unexpected token ARRAY END (])"), Tokens::ArrayEnd => bail!("Unexpected token ARRAY END (])"),
Tokens::SubStart => { Tokens::SubStart => {
@ -599,7 +638,7 @@ impl Tree {
Some(_) => bail!("Unexpected break") Some(_) => bail!("Unexpected break")
} }
Tokens::JobCommandEnd => bail!("Jobs not yet implemented") Tokens::JobCommandEnd => bail!("Jobs not yet implemented")
} };
if self.i >= end - 1 { break } if self.i >= end - 1 { break }
token = self.get_current_token(); token = self.get_current_token();
} }
@ -614,7 +653,6 @@ impl Tree {
self self
} }
fn get_current_token(&self) -> &Tokens { &self.tokens.get(self.i).unwrap().token } 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>> { pub fn build_tree(tokens: Vec<Token>) -> Result<Vec<Expression>> {

View file

@ -1,7 +1,7 @@
use std::fs::File; use std::fs::File;
use std::process::{Child, Command, Stdio}; use std::process::{Child, Command, Stdio};
use std::io; use std::io;
use crate::parser::ast::{AndExpression, 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;
use crate::parser::vars::{AnyFunction, Context, Variable}; use crate::parser::vars::{AnyFunction, Context, Variable};
use anyhow::{Result, bail, Context as AnyhowContext}; use anyhow::{Result, bail, Context as AnyhowContext};
@ -50,7 +50,7 @@ impl ExecResult {
self.spawn(); self.spawn();
let child = self.child.unwrap(); let child = self.child.unwrap();
let mut stdout = child.stdout.unwrap(); let mut stdout = child.stdout.unwrap();
io::copy(&mut stdout, into); io::copy(&mut stdout, into).unwrap();
} }
} }
into into
@ -59,7 +59,7 @@ impl ExecResult {
/// ///
/// Uses `redirect_from_result` of the next result. Spawns this result, but not the next one. /// 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 { fn redirect_into_result(&mut self, into: &mut ExecResult) -> &mut Self {
into.redirect_from_result(self); into.redirect_from_result(self).unwrap();
self self
} }
/// Redirects the `from` into the current pending result /// Redirects the `from` into the current pending result
@ -167,7 +167,7 @@ impl ExecExpression for Expression {
Expression::Function(_) => todo!("Function definition"), Expression::Function(_) => todo!("Function definition"),
Expression::IfExpression(expr) => expr.exec(ctx), Expression::IfExpression(expr) => expr.exec(ctx),
Expression::WhileExpression(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::RedirectTargetExpression(expr) => expr.exec(ctx),
Expression::FileTargetExpression(expr) => expr.exec(ctx), Expression::FileTargetExpression(expr) => expr.exec(ctx),
Expression::FileSourceExpression(expr) => expr.exec(ctx), Expression::FileSourceExpression(expr) => expr.exec(ctx),
@ -197,18 +197,24 @@ impl ExecExpression for WhileExpression {
Some(cmd) => cmd Some(cmd) => cmd
}; };
ctx.add_scope(); ctx.add_scope();
let mut res; let mut res = None;
loop { loop {
match condition.spawn() { match condition.spawn() {
Result::Err(_) => { Err(_) => {
res = Some(condition); res = Some(condition);
break; break;
}, },
Result::Ok(mut child) => { Ok(mut child) => {
if !child.wait()?.success() { if !child.wait()?.success() {
res = Some(condition); res = Some(condition);
break break
} else { } else {
match res {
None => {},
Some(mut cmd) => {
wait_child(cmd.spawn()?, ctx)?;
}
}
res = self.contents.exec(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 { impl ExecExpression for IfExpression {
fn exec(self: &mut IfExpression, ctx: &mut vars::Context) -> Result<Option<Command>> { fn exec(self: &mut IfExpression, ctx: &mut vars::Context) -> Result<Option<Command>> {
if ctx.break_num > 0 { return Ok(None) } 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>> { fn exec(self: &mut Vec<CommandValue>, ctx: &mut vars::Context) -> Result<Option<Command>> {
if ctx.break_num > 0 { return Ok(None) } if ctx.break_num > 0 { return Ok(None) }
if self.is_empty() { bail!("Command with 0 length"); } 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 command_name = first.get(ctx)?.to_string();
let mut cmd = Command::new(command_name); let mut cmd = Command::new(command_name);
for value in self { for value in &mut self[1..] {
cmd.arg(value.get(ctx)?.to_string()); cmd.arg(value.get(ctx)?.to_string());
} }
Ok(Some(cmd)) 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<()> { pub fn exec_tree(tree: Vec<Expression>, ctx: &mut vars::Context) -> Result<()> {
for mut expression in tree { for mut expression in tree {
let cmd = expression.exec(ctx)?; 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); println!("Error executing: {}", e);
}, },
Result::Ok(mut res) => { Result::Ok(mut res) => {
let out = res.wait()?; wait_child(res, ctx)?;
ctx.set_var(String::from("?"), Variable::I32(out.code().unwrap_or(1)));
} }
} }
} }

View file

@ -167,12 +167,13 @@ pub fn tokenize(reader: &mut dyn std::io::BufRead) -> Result<Vec<Token>> {
buf_add = false; buf_add = false;
} else { } else {
let (mut skippers, mut token) = read_var_ahead(i, &text)?; let (mut skippers, mut token) = read_var_ahead(i, &text)?;
skippers += 1;
match token.token { 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 }; 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 }; token = Token { token: Tokens::ArrayFunction(str.clone()), end: i+skippers, start: i };
} }
_ => bail!("Cannot happen") _ => bail!("Cannot happen")

View file

@ -15,7 +15,8 @@ pub enum Variable {
F32(f32), F32(f32),
F64(f64), F64(f64),
HMap(HashMap<String, Variable>), HMap(HashMap<String, Variable>),
Array(Vec<Variable>) Array(Vec<Variable>),
Bool(bool)
} }
impl Display for Variable { impl Display for Variable {
@ -32,6 +33,13 @@ impl Display for Variable {
Variable::U128(num) => num.to_string(), Variable::U128(num) => num.to_string(),
Variable::F32(num) => num.to_string(), Variable::F32(num) => num.to_string(),
Variable::F64(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) => { Variable::HMap(_map) => {
String::from("[Object object]") String::from("[Object object]")
}, },