diff --git a/CHANGELOG.md b/CHANGELOG.md index 19297ee..f938c21 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/duckscript_sdk/src/sdk/std/flowcontrol/function/mod.rs b/duckscript_sdk/src/sdk/std/flowcontrol/function/mod.rs index 61784c5..3b81bd8 100755 --- a/duckscript_sdk/src/sdk/std/flowcontrol/function/mod.rs +++ b/duckscript_sdk/src/sdk/std/flowcontrol/function/mod.rs @@ -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) diff --git a/duckscript_sdk/src/sdk/std/flowcontrol/ifelse/mod.rs b/duckscript_sdk/src/sdk/std/flowcontrol/ifelse/mod.rs index 9e4df08..438cf08 100755 --- a/duckscript_sdk/src/sdk/std/flowcontrol/ifelse/mod.rs +++ b/duckscript_sdk/src/sdk/std/flowcontrol/ifelse/mod.rs @@ -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, variables: &mut HashMap, _output_variable: Option, - _instructions: &Vec, + instructions: &Vec, 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() { diff --git a/duckscript_sdk/src/sdk/std/not/mod.rs b/duckscript_sdk/src/sdk/std/not/mod.rs index 6fda432..518df0f 100755 --- a/duckscript_sdk/src/sdk/std/not/mod.rs +++ b/duckscript_sdk/src/sdk/std/not/mod.rs @@ -40,14 +40,14 @@ impl Command for CommandImpl { state: &mut HashMap, variables: &mut HashMap, _output_variable: Option, - _instructions: &Vec, + instructions: &Vec, 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())) diff --git a/duckscript_sdk/src/types/command.rs b/duckscript_sdk/src/types/command.rs index 0718661..67822ba 100644 --- a/duckscript_sdk/src/types/command.rs +++ b/duckscript_sdk/src/types/command.rs @@ -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) => { diff --git a/duckscript_sdk/src/utils/condition.rs b/duckscript_sdk/src/utils/condition.rs index 6dac475..69088d2 100644 --- a/duckscript_sdk/src/utils/condition.rs +++ b/duckscript_sdk/src/utils/condition.rs @@ -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) -> bool { pub(crate) fn eval_condition( arguments: Vec, + instructions: &Vec, state: &mut HashMap, variables: &mut HashMap, 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); diff --git a/duckscript_sdk/src/utils/condition_test.rs b/duckscript_sdk/src/utils/condition_test.rs index 8b69835..4787125 100644 --- a/duckscript_sdk/src/utils/condition_test.rs +++ b/duckscript_sdk/src/utils/condition_test.rs @@ -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, diff --git a/duckscript_sdk/src/utils/eval.rs b/duckscript_sdk/src/utils/eval.rs index 625f8e1..9193b2c 100644 --- a/duckscript_sdk/src/utils/eval.rs +++ b/duckscript_sdk/src/utils/eval.rs @@ -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) -> Result { + 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, state: &mut HashMap, @@ -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, + instructions: &Vec, + state: &mut HashMap, + variables: &mut HashMap, + 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, + commands: &mut Commands, + state: &mut HashMap, + variables: &mut HashMap, + start_line: usize, +) -> (Option, Option) { + 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) +} diff --git a/test/std/flowcontrol/if_else_test.ds b/test/std/flowcontrol/if_else_test.ds index ce7e339..475885c 100644 --- a/test/std/flowcontrol/if_else_test.ds +++ b/test/std/flowcontrol/if_else_test.ds @@ -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 +