Skip to content

Commit c5f2212

Browse files
authored
Merge pull request #451 from sagiegurari/0.10.0
0.10.0
2 parents 34c46ca + 1db4769 commit c5f2212

File tree

195 files changed

+1556
-2654
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

195 files changed

+1556
-2654
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
## CHANGELOG
22

3+
### v0.10.0
4+
5+
* Enhancement: Runtime - \[Breaking Change\] New Env struct enabling commands to redirect out/err to provided streams #440
6+
* Enhancement: Runtime - \[Breaking Change\] Commands now get new CommandArgs struct instead of multiple fields.
7+
* Enhancement: Runtime - Enable to halt execution via env.
8+
39
### v0.9.4 (2024-09-28)
410

511
* Enhancement: Runtime - Adding halt interrupt to env #448 (thanks @nickheyer)

docs/_includes/content.md

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -356,21 +356,16 @@ If you want to know how to write your own commands or embed the duckscript runti
356356
Want to write new custom commands so you can use them in your duckscripts? great!<br>
357357
Hopefully the following sections will help you gain the basic knowledge on how to write them.<br>
358358
359-
First of all it is important to understand that there are two types of commands:
360-
361-
* Commands which execute some action like copying files, printing some text to the console or returning an environment variable.
362-
* Commands which provide flow control or some more complex action and require modifying the internal context in runtime.
363-
364-
<a name="sdk-tutorial-standard-commands"></a>
365-
## Standard Commands
359+
<a name="sdk-tutorial-commands"></a>
360+
## Commands
366361
Commands are structs that must implement the Command trait.<br>
367362
368363
* They must have a name, which is used to invoke the command.<br>
369364
* They optionally may have aliases which can also be used to invoke the command.<br>
370365
* They should return help documentation in markdown format in order to generate SDK documentation (must for PRs to duckscript official SDK).<br>
371366
* They must implement the **run** function which holds the command logic.<br>
372367
373-
The run function accepts the command arguments (variables already replaced to actual values) and returns the command result.<br>
368+
The run function accepts the command arguments (args array contains actual values and not original variables) and returns the command result.<br>
374369
The command result can be one of the following:
375370
376371
* Continue(Option<String>) - Tells the runner to continue to the next command and optionally set the output variable the given value.
@@ -393,11 +388,15 @@ impl Command for SetCommand {
393388
"set".to_string()
394389
}
395390
396-
fn run(&self, arguments: Vec<String>) -> CommandResult {
397-
let output = if arguments.is_empty() {
391+
fn clone_and_box(&self) -> Box<dyn Command> {
392+
Box::new((*self).clone())
393+
}
394+
395+
fn run(&self, arguments: CommandArgs) -> CommandResult {
396+
let output = if arguments.args.is_empty() {
398397
None
399398
} else {
400-
Some(arguments[0].clone())
399+
Some(arguments.args[0].clone())
401400
};
402401
403402
CommandResult::Continue(output)
@@ -420,11 +419,15 @@ impl Command for GetEnvCommand {
420419
"get_env".to_string()
421420
}
422421
423-
fn run(&self, arguments: Vec<String>) -> CommandResult {
424-
if arguments.is_empty() {
422+
fn clone_and_box(&self) -> Box<dyn Command> {
423+
Box::new((*self).clone())
424+
}
425+
426+
fn run(&self, arguments: CommandArgs) -> CommandResult {
427+
if arguments.args.is_empty() {
425428
CommandResult::Error("Missing environment variable name.".to_string())
426429
} else {
427-
match env::var(&arguments[0]) {
430+
match env::var(&arguments.args[0]) {
428431
Ok(value) => CommandResult::Continue(Some(value)),
429432
Err(_) => CommandResult::Continue(None),
430433
}
@@ -435,38 +438,23 @@ impl Command for GetEnvCommand {
435438
436439
You can look at more examples in the duckscript_sdk folder.
437440
438-
<a name="sdk-tutorial-context-commands"></a>
439-
## Context Commands
440-
Context commands are exactly the same as standard commands except that they have access to the runtime context.<br>
441-
Therefore they implement the same Command trait but this time instead of implementing the run function, they need to implement the following:
442-
443-
* requires_context - Must return true
444-
* run_with_context - The same logic you would put in the run function but now you have access to a lot more of the runtime context.
445-
446-
The run_with_context signature is as follows:
441+
<a name="sdk-tutorial-commands-context"></a>
442+
## Access The Context
443+
The duckscript runtime context is available in the CommandArgs struc.<br>
447444
448445
```rust
449446
/// Run the instruction with access to the runtime context.
450447
///
451-
/// # Arguments
452-
///
453-
/// * `arguments` - The command arguments array
448+
/// The CommandArgs has the following members:
449+
/// * `args` - The command arguments array
454450
/// * `state` - Internal state which is only used by commands to store/pull data
455451
/// * `variables` - All script variables
456452
/// * `output_variable` - The output variable name (if defined)
457453
/// * `instructions` - The entire list of instructions which make up the currently running script
458454
/// * `commands` - The currently known commands
459455
/// * `line` - The current instruction line number (global line number after including all scripts into one global script)
460-
fn run_with_context(
461-
&self,
462-
arguments: Vec<String>,
463-
state: &mut HashMap<String, StateValue>,
464-
variables: &mut HashMap<String, String>,
465-
output_variable: Option<String>,
466-
instructions: &Vec<Instruction>,
467-
commands: &mut Commands,
468-
line: usize,
469-
) -> CommandResult;
456+
/// * `env` - The current runtime env with access to out/err writers, etc...
457+
fn run(&self, arguments: CommandArgs) -> CommandResult;
470458
```
471459
472460
With access to this context you can add/remove/switch commands in runtime, store/pull internal state, add/remove/change variables and so on...
@@ -479,7 +467,7 @@ The duckscript cli basically embeds duckscript so you can look at it as a refere
479467
```rust
480468
let mut context = Context::new();
481469
duckscriptsdk::load(&mut context.commands)?;
482-
runner::run_script_file(file, context)?;
470+
runner::run_script_file(file, context, None)?;
483471
```
484472
485473
That's it!<br>
@@ -502,10 +490,10 @@ The following public functions are available:
502490
503491
```rust
504492
/// Executes the provided script with the given context
505-
pub fn run_script(text: &str, context: Context) -> Result<Context, ScriptError>;
493+
pub fn run_script(text: &str, context: Context, env: Option<Env>) -> Result<Context, ScriptError>;
506494
507495
/// Executes the provided script file with the given context
508-
pub fn run_script_file(file: &str, context: Context) -> Result<Context, ScriptError>;
496+
pub fn run_script_file(file: &str, context: Context, env: Option<Env>) -> Result<Context, ScriptError>;
509497
```
510498
511499
<a name="editor-support"></a>

docs/_includes/nav.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
* [Full SDK Docs](https://github.com/sagiegurari/duckscript/blob/master/docs/sdk.md)
2222
* [Final Notes](#tutorial-final-notes)
2323
* [Duckscript Command Implementation Tutorial](#sdk-tutorial)
24-
* [Standard Commands](#sdk-tutorial-standard-commands)
25-
* [Context Commands](#sdk-tutorial-context-commands)
24+
* [Commands](#sdk-tutorial-commands)
25+
* [Access The Context](#sdk-tutorial-commands-context)
2626
* [Duckscript Embedding Tutorial](#embed-tutorial)
2727
* [Setting Up The Context](#embed-tutorial-setup-context)
2828
* [Running The Script](#embed-tutorial-running)

duckscript/src/runner.rs

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,41 @@ mod runner_test;
99

1010
use crate::expansion::{self, ExpandedValue};
1111
use crate::parser;
12-
use crate::types::command::{CommandResult, Commands, GoToValue};
12+
use crate::types::command::{CommandArgs, CommandResult, Commands, GoToValue};
13+
use crate::types::env::Env;
1314
use crate::types::error::ScriptError;
1415
use crate::types::instruction::{
1516
Instruction, InstructionMetaInfo, InstructionType, ScriptInstruction,
1617
};
1718
use crate::types::runtime::{Context, Runtime, StateValue};
1819
use std::collections::HashMap;
1920
use std::io::stdin;
21+
use std::sync::atomic::Ordering;
2022

2123
#[derive(Debug)]
2224
enum EndReason {
2325
ExitCalled,
2426
ReachedEnd,
2527
Crash(ScriptError),
28+
Halted,
2629
}
2730

2831
/// Executes the provided script with the given context
29-
pub fn run_script(text: &str, context: Context) -> Result<Context, ScriptError> {
32+
pub fn run_script(text: &str, context: Context, env: Option<Env>) -> Result<Context, ScriptError> {
3033
match parser::parse_text(text) {
31-
Ok(instructions) => run(instructions, context),
34+
Ok(instructions) => run(instructions, context, env),
3235
Err(error) => Err(error),
3336
}
3437
}
3538

3639
/// Executes the provided script file with the given context
37-
pub fn run_script_file(file: &str, context: Context) -> Result<Context, ScriptError> {
40+
pub fn run_script_file(
41+
file: &str,
42+
context: Context,
43+
env: Option<Env>,
44+
) -> Result<Context, ScriptError> {
3845
match parser::parse_file(file) {
39-
Ok(instructions) => run(instructions, context),
46+
Ok(instructions) => run(instructions, context, env),
4047
Err(error) => Err(error),
4148
}
4249
}
@@ -58,7 +65,7 @@ pub fn repl(mut context: Context) -> Result<Context, ScriptError> {
5865

5966
// add new instructions
6067
instructions.append(&mut new_instructions);
61-
let runtime = create_runtime(instructions.clone(), context);
68+
let runtime = create_runtime(instructions.clone(), context, None);
6269

6370
let (updated_context, end_reason) = run_instructions(runtime, start, true)?;
6471

@@ -83,17 +90,21 @@ pub fn repl(mut context: Context) -> Result<Context, ScriptError> {
8390
}
8491
}
8592

86-
fn run(instructions: Vec<Instruction>, context: Context) -> Result<Context, ScriptError> {
87-
let runtime = create_runtime(instructions, context);
93+
fn run(
94+
instructions: Vec<Instruction>,
95+
context: Context,
96+
env: Option<Env>,
97+
) -> Result<Context, ScriptError> {
98+
let runtime = create_runtime(instructions, context, env);
8899

89100
match run_instructions(runtime, 0, false) {
90101
Ok((context, _)) => Ok(context),
91102
Err(error) => Err(error),
92103
}
93104
}
94105

95-
fn create_runtime(instructions: Vec<Instruction>, context: Context) -> Runtime {
96-
let mut runtime = Runtime::new(context);
106+
fn create_runtime(instructions: Vec<Instruction>, context: Context, env: Option<Env>) -> Runtime {
107+
let mut runtime = Runtime::new(context, env);
97108

98109
let mut line = 0;
99110
for instruction in &instructions {
@@ -126,6 +137,11 @@ fn run_instructions(
126137

127138
let mut end_reason = EndReason::ReachedEnd;
128139
loop {
140+
if runtime.env.halt.load(Ordering::SeqCst) {
141+
end_reason = EndReason::Halted;
142+
break;
143+
}
144+
129145
let (instruction, meta_info) = if instructions.len() > line {
130146
let instruction = instructions[line].clone();
131147
let meta_info = instruction.meta_info.clone();
@@ -141,6 +157,7 @@ fn run_instructions(
141157
instructions,
142158
instruction,
143159
line,
160+
&mut runtime.env,
144161
);
145162

146163
match command_result {
@@ -185,6 +202,7 @@ fn run_instructions(
185202
instructions,
186203
error,
187204
meta_info.clone(),
205+
&mut runtime.env,
188206
) {
189207
return Err(ScriptError::Runtime(error, Some(meta_info.clone())));
190208
};
@@ -249,6 +267,7 @@ fn run_on_error_instruction(
249267
instructions: &Vec<Instruction>,
250268
error: String,
251269
meta_info: InstructionMetaInfo,
270+
env: &mut Env,
252271
) -> Result<(), String> {
253272
if commands.exists("on_error") {
254273
let mut script_instruction = ScriptInstruction::new();
@@ -263,8 +282,15 @@ fn run_on_error_instruction(
263282
instruction_type: InstructionType::Script(script_instruction),
264283
};
265284

266-
let (command_result, output_variable) =
267-
run_instruction(commands, variables, state, instructions, instruction, 0);
285+
let (command_result, output_variable) = run_instruction(
286+
commands,
287+
variables,
288+
state,
289+
instructions,
290+
instruction,
291+
0,
292+
env,
293+
);
268294

269295
match command_result {
270296
CommandResult::Exit(output) => {
@@ -288,6 +314,7 @@ pub fn run_instruction(
288314
instructions: &Vec<Instruction>,
289315
instruction: Instruction,
290316
line: usize,
317+
env: &mut Env,
291318
) -> (CommandResult, Option<String>) {
292319
let mut output_variable = None;
293320
let command_result = match instruction.instruction_type {
@@ -305,19 +332,17 @@ pub fn run_instruction(
305332
&instruction.meta_info,
306333
);
307334

308-
if command_instance.requires_context() {
309-
command_instance.run_with_context(
310-
command_arguments,
311-
state,
312-
variables,
313-
output_variable.clone(),
314-
instructions,
315-
commands,
316-
line,
317-
)
318-
} else {
319-
command_instance.run(command_arguments)
320-
}
335+
let command_args = CommandArgs {
336+
args: command_arguments,
337+
state,
338+
variables,
339+
output_variable: output_variable.clone(),
340+
instructions,
341+
commands,
342+
line,
343+
env,
344+
};
345+
command_instance.run(command_args)
321346
}
322347
None => CommandResult::Crash(format!("Command: {} not found.", &command)),
323348
},

0 commit comments

Comments
 (0)