mirror of
https://github.com/sagiegurari/duckscript
synced 2024-10-14 11:53:05 +00:00
Conditions to support function calls #116
This commit is contained in:
parent
b893622ebb
commit
8383eb0f3c
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,15 +9,7 @@ use std::collections::HashMap;
|
|||
#[path = "./eval_test.rs"]
|
||||
mod eval_test;
|
||||
|
||||
pub(crate) fn eval(
|
||||
arguments: &Vec<String>,
|
||||
state: &mut HashMap<String, StateValue>,
|
||||
variables: &mut HashMap<String, String>,
|
||||
commands: &mut Commands,
|
||||
) -> Result<CommandResult, String> {
|
||||
if arguments.is_empty() {
|
||||
Ok(CommandResult::Continue(None))
|
||||
} else {
|
||||
fn parse(arguments: &Vec<String>) -> Result<Instruction, String> {
|
||||
let mut line_buffer = String::new();
|
||||
for argument in arguments {
|
||||
if argument.contains(" ") {
|
||||
|
@ -35,15 +28,24 @@ pub(crate) fn eval(
|
|||
.replace("\\", "\\\\");
|
||||
|
||||
match parser::parse_text(&line_str) {
|
||||
Ok(instructions) => {
|
||||
let (command_result, _) = runner::run_instruction(
|
||||
commands,
|
||||
variables,
|
||||
state,
|
||||
&vec![],
|
||||
instructions[0].clone(),
|
||||
0,
|
||||
);
|
||||
Ok(instructions) => Ok(instructions[0].clone()),
|
||||
Err(error) => Err(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn eval(
|
||||
arguments: &Vec<String>,
|
||||
state: &mut HashMap<String, StateValue>,
|
||||
variables: &mut HashMap<String, String>,
|
||||
commands: &mut Commands,
|
||||
) -> Result<CommandResult, String> {
|
||||
if arguments.is_empty() {
|
||||
Ok(CommandResult::Continue(None))
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue