Conditions to support function calls #116

This commit is contained in:
sagie gur ari 2020-07-02 18:20:20 +00:00
parent b893622ebb
commit 8383eb0f3c
9 changed files with 227 additions and 110 deletions

View file

@ -1,6 +1,6 @@
## CHANGELOG
### v0.5.1
### v0.6.0
* New ftp_put command.
* New fpt_put_in_memory command.
@ -8,6 +8,7 @@
* New ftp_get_in_memory command.
* New ftp_list command.
* New ftp_nlst command.
* Conditions to support function calls #116
### v0.5.0 (2020-06-06)

View file

@ -524,8 +524,14 @@ impl Command for ReturnCommand {
None => (),
};
let output = if arguments.is_empty() {
None
} else {
Some(arguments[0].clone())
};
let next_line = call_info.call_line + 1;
CommandResult::GoTo(None, GoToValue::Line(next_line))
CommandResult::GoTo(output, GoToValue::Line(next_line))
} else {
push_to_call_stack(state, &call_info);
CommandResult::Continue(None)

View file

@ -344,7 +344,13 @@ impl Command for IfCommand {
self.package.clone(),
) {
Ok(if_else_info) => {
match condition::eval_condition(arguments, state, variables, commands) {
match condition::eval_condition(
arguments,
instructions,
state,
variables,
commands,
) {
Ok(passed) => {
if passed {
let next_line = if if_else_info.else_lines.is_empty() {
@ -422,7 +428,7 @@ impl Command for ElseIfCommand {
state: &mut HashMap<String, StateValue>,
variables: &mut HashMap<String, String>,
_output_variable: Option<String>,
_instructions: &Vec<Instruction>,
instructions: &Vec<Instruction>,
commands: &mut Commands,
line: usize,
) -> CommandResult {
@ -436,7 +442,7 @@ impl Command for ElseIfCommand {
CommandResult::GoTo(None, GoToValue::Line(next_line))
} else {
let if_else_info = call_info.meta_info.clone();
match condition::eval_condition(arguments, state, variables, commands) {
match condition::eval_condition(arguments, instructions, state, variables, commands) {
Ok(passed) => {
if passed {
let next_line = if call_info.else_line_index + 1 < if_else_info.else_lines.len() {

View file

@ -40,14 +40,14 @@ impl Command for CommandImpl {
state: &mut HashMap<String, StateValue>,
variables: &mut HashMap<String, String>,
_output_variable: Option<String>,
_instructions: &Vec<Instruction>,
instructions: &Vec<Instruction>,
commands: &mut Commands,
_line: usize,
) -> CommandResult {
if arguments.is_empty() {
CommandResult::Error("Missing condition".to_string())
} else {
match condition::eval_condition(arguments, state, variables, commands) {
match condition::eval_condition(arguments, instructions, state, variables, commands) {
Ok(passed) => {
let output = !passed;
CommandResult::Continue(Some(output.to_string()))

View file

@ -1,11 +1,12 @@
use crate::types::scope::clear;
use crate::types::scope::set_line_context_name;
use crate::utils::eval;
use crate::utils::state::{get_handles_sub_state, put_handle};
use duckscript::types::command::{Command, CommandResult, Commands, GoToValue};
use duckscript::parser;
use duckscript::types::command::{Command, CommandResult, Commands};
use duckscript::types::error::ScriptError;
use duckscript::types::instruction::{Instruction, InstructionType};
use duckscript::types::instruction::Instruction;
use duckscript::types::runtime::StateValue;
use duckscript::{parser, runner};
use std::collections::HashMap;
#[derive(Clone)]
@ -119,77 +120,8 @@ impl Command for AliasCommand {
handle_option = Some(handle);
}
let mut line = 0;
let mut flow_output = None;
let mut flow_result = None;
loop {
let instruction = if self.instructions.len() > line {
self.instructions[line].clone()
} else {
break;
};
match instruction.instruction_type {
InstructionType::Script(ref script_instruction) => {
let (command_result, _) = runner::run_instruction(
commands,
variables,
state,
&self.instructions,
instruction.clone(),
line,
);
match command_result {
CommandResult::Exit(output) => {
flow_result = Some(CommandResult::Exit(output));
break;
}
CommandResult::Error(error) => {
flow_result = Some(CommandResult::Error(error));
break;
}
CommandResult::Crash(error) => {
flow_result = Some(CommandResult::Crash(error));
break;
}
CommandResult::GoTo(output, goto_value) => {
flow_output = output.clone();
match goto_value {
GoToValue::Label(_) => {
flow_result = Some(CommandResult::Error(
"goto label result not supported in alias command flow.".to_string(),
));
break;
}
GoToValue::Line(line_number) => line = line_number,
}
}
CommandResult::Continue(output) => {
flow_output = output.clone();
match script_instruction.output {
Some(ref output_variable) => {
match output {
Some(value) => {
variables.insert(output_variable.to_string(), value)
}
None => variables.remove(output_variable),
};
}
None => (),
};
line = line + 1;
}
};
}
_ => {
line = line + 1;
}
};
}
let (flow_result, flow_output) =
eval::eval_instructions(&self.instructions, commands, state, variables, 0);
match handle_option {
Some(handle) => {

View file

@ -1,5 +1,6 @@
use crate::utils::eval;
use duckscript::types::command::{CommandResult, Commands};
use duckscript::types::instruction::Instruction;
use duckscript::types::runtime::StateValue;
use std::collections::HashMap;
@ -28,6 +29,7 @@ pub(crate) fn is_true(value: Option<String>) -> bool {
pub(crate) fn eval_condition(
arguments: Vec<String>,
instructions: &Vec<Instruction>,
state: &mut HashMap<String, StateValue>,
variables: &mut HashMap<String, String>,
commands: &mut Commands,
@ -38,7 +40,8 @@ pub(crate) fn eval_condition(
let eval_statement = commands.exists(&arguments[0]);
if eval_statement {
match eval::eval_with_error(&arguments, state, variables, commands) {
match eval::eval_with_instructions(&arguments, instructions, state, variables, commands)
{
CommandResult::Continue(value) => {
let passed = is_true(value);

View file

@ -61,6 +61,7 @@ fn is_true_valid() {
fn eval_condition_empty() {
let result = eval_condition(
vec![],
&vec![],
&mut HashMap::new(),
&mut HashMap::new(),
&mut Commands::new(),
@ -77,6 +78,7 @@ fn eval_condition_empty() {
fn eval_condition_value_true() {
let result = eval_condition(
vec!["true".to_string()],
&vec![],
&mut HashMap::new(),
&mut HashMap::new(),
&mut Commands::new(),
@ -93,6 +95,7 @@ fn eval_condition_value_true() {
fn eval_condition_value_false() {
let result = eval_condition(
vec!["false".to_string()],
&vec![],
&mut HashMap::new(),
&mut HashMap::new(),
&mut Commands::new(),
@ -115,6 +118,7 @@ fn eval_condition_command_true() {
let result = eval_condition(
vec!["test_set".to_string(), "true".to_string()],
&vec![],
&mut HashMap::new(),
&mut HashMap::new(),
&mut commands,
@ -137,6 +141,7 @@ fn eval_condition_command_false() {
let result = eval_condition(
vec!["test_set".to_string(), "false".to_string()],
&vec![],
&mut HashMap::new(),
&mut HashMap::new(),
&mut commands,
@ -159,6 +164,7 @@ fn eval_condition_command_error() {
let result = eval_condition(
vec!["test_error".to_string()],
&vec![],
&mut HashMap::new(),
&mut HashMap::new(),
&mut commands,

View file

@ -1,6 +1,7 @@
use duckscript::parser;
use duckscript::runner;
use duckscript::types::command::{CommandResult, Commands};
use duckscript::types::command::{CommandResult, Commands, GoToValue};
use duckscript::types::instruction::{Instruction, InstructionType};
use duckscript::types::runtime::StateValue;
use std::collections::HashMap;
@ -8,6 +9,30 @@ use std::collections::HashMap;
#[path = "./eval_test.rs"]
mod eval_test;
fn parse(arguments: &Vec<String>) -> Result<Instruction, String> {
let mut line_buffer = String::new();
for argument in arguments {
if argument.contains(" ") {
line_buffer.push('"');
}
line_buffer.push_str(argument);
if argument.contains(" ") {
line_buffer.push('"');
}
line_buffer.push(' ');
}
let line_str = line_buffer
.replace("\r", "")
.replace("\n", "")
.replace("\\", "\\\\");
match parser::parse_text(&line_str) {
Ok(instructions) => Ok(instructions[0].clone()),
Err(error) => Err(error.to_string()),
}
}
pub(crate) fn eval(
arguments: &Vec<String>,
state: &mut HashMap<String, StateValue>,
@ -17,33 +42,10 @@ pub(crate) fn eval(
if arguments.is_empty() {
Ok(CommandResult::Continue(None))
} else {
let mut line_buffer = String::new();
for argument in arguments {
if argument.contains(" ") {
line_buffer.push('"');
}
line_buffer.push_str(argument);
if argument.contains(" ") {
line_buffer.push('"');
}
line_buffer.push(' ');
}
let line_str = line_buffer
.replace("\r", "")
.replace("\n", "")
.replace("\\", "\\\\");
match parser::parse_text(&line_str) {
Ok(instructions) => {
let (command_result, _) = runner::run_instruction(
commands,
variables,
state,
&vec![],
instructions[0].clone(),
0,
);
match parse(arguments) {
Ok(instruction) => {
let (command_result, _) =
runner::run_instruction(commands, variables, state, &vec![], instruction, 0);
Ok(command_result)
}
@ -66,3 +68,121 @@ pub(crate) fn eval_with_error(
Err(error) => CommandResult::Error(error.to_string()),
}
}
pub(crate) fn eval_with_instructions(
arguments: &Vec<String>,
instructions: &Vec<Instruction>,
state: &mut HashMap<String, StateValue>,
variables: &mut HashMap<String, String>,
commands: &mut Commands,
) -> CommandResult {
if arguments.is_empty() {
CommandResult::Continue(None)
} else {
match parse(arguments) {
Ok(instruction) => {
let mut all_instructions = instructions.clone();
all_instructions.push(instruction);
let (flow_result, flow_output) = eval_instructions(
&all_instructions,
commands,
state,
variables,
all_instructions.len() - 1,
);
match flow_result {
Some(result) => match result.clone() {
CommandResult::Crash(error) => CommandResult::Error(error),
_ => result,
},
None => CommandResult::Continue(flow_output),
}
}
Err(error) => CommandResult::Error(error),
}
}
}
pub(crate) fn eval_instructions(
instructions: &Vec<Instruction>,
commands: &mut Commands,
state: &mut HashMap<String, StateValue>,
variables: &mut HashMap<String, String>,
start_line: usize,
) -> (Option<CommandResult>, Option<String>) {
let mut line = start_line;
let mut flow_output = None;
let mut flow_result = None;
loop {
let instruction = if instructions.len() > line {
instructions[line].clone()
} else {
break;
};
match instruction.instruction_type {
InstructionType::Script(ref script_instruction) => {
let (command_result, _) = runner::run_instruction(
commands,
variables,
state,
&instructions,
instruction.clone(),
line,
);
match command_result {
CommandResult::Exit(output) => {
flow_result = Some(CommandResult::Exit(output));
break;
}
CommandResult::Error(error) => {
flow_result = Some(CommandResult::Error(error));
break;
}
CommandResult::Crash(error) => {
flow_result = Some(CommandResult::Crash(error));
break;
}
CommandResult::GoTo(output, goto_value) => {
flow_output = output.clone();
match goto_value {
GoToValue::Label(_) => {
flow_result = Some(CommandResult::Error(
"goto label result not supported in alias command flow."
.to_string(),
));
break;
}
GoToValue::Line(line_number) => line = line_number,
}
}
CommandResult::Continue(output) => {
flow_output = output.clone();
match script_instruction.output {
Some(ref output_variable) => {
match output {
Some(value) => {
variables.insert(output_variable.to_string(), value)
}
None => variables.remove(output_variable),
};
}
None => (),
};
line = line + 1;
}
};
}
_ => {
line = line + 1;
}
};
}
(flow_result, flow_output)
}

View file

@ -117,3 +117,46 @@ fn test_nested_if2
assert ${valid}
end
fn test_if_call_to_functions
value = _test_return_true
valid = set false
if ${value}
valid = set true
end
assert ${valid}
value = _test_return_false
valid = set true
if ${value}
valid = set false
end
assert ${valid}
valid = set false
if _test_return_true
valid = set true
end
assert ${valid}
valid = set true
if _test_return_false
valid = set false
end
assert ${valid}
valid = set false
if not _test_return_false
valid = set true
end
assert ${valid}
end
fn _test_return_true
return true
end
fn _test_return_false
return false
end