Duckscript is a simple, extendable and embeddable scripting language.<br>
The language itself has only few rules and most common language features are implemented as commands rather than part of the language itself.
<aname="lang-goals"></a>
### Language Goals
Duckscript scripting language goals are:
* Simple - This is probably the simplest language you will ever see.
* Extendable - Instead of having common features such as functions and conditional blocks be a part of the language, they are actually part of the API. So they can easily be replaced/modified or you can add more 'feature' like commands on your own.
* Embeddable - One of the main purposes of this language is to allow other libraries/executables/apps have scripting capability by embedding duckscript. Embedding is easy (for [rust](https://www.rust-lang.org/)) and requires only few lines of code.
<aname="installation"></a>
## Installation
If you have [rust](https://www.rust-lang.org/), just run the following command
```sh
cargo install --force duckscript_cli
```
This will install duckscript script runner, the standard duckscript SDK and the duckscript CLI.<br>
You should then have a **duckscript** executable in your ~/.cargo/bin directory.<br>
Make sure to add ~/.cargo/bin directory to your PATH variable.
<aname="installation-binary-release"></a>
### Binary Release
Binary releases are available in the [github releases page](https://github.com/sagiegurari/duckscript/releases).<br>
The following binaries are available for each release:
* x86_64-unknown-linux-musl
* x86_64-apple-darwin
* x86_64-pc-windows-msvc
<aname="tutorial"></a>
## Duckscript Tutorial
The following sections will teach you how to write and run duck scripts.
<aname="tutorial-hello-world"></a>
### Hello World Example
Let's take a really simple example (all examples are located in the [examples](https://github.com/sagiegurari/duckscript/tree/master/examples) directory:
```sh
# print the text "hello world"
echo hello world
```
Running this script is done using the **duckscript** executable as follows:
```sh
duckscript ./examples/hello_world.ds
```
We will understand more and breakdown this down in the following sections.
<aname="tutorial-commands"></a>
### Commands
Commands are the basis of everything in duckscript.<br>
Commands may execute some action (like printing "hello world" to the console) or serve as flow control (such as functions or if/else conditions).<br>
In order to invoke an action, simply write the action name:
Commands may accept arguments, for example the command **echo** may accept any number of arguments and it will print all of them.<br>
Arguments are separated with the space character.<br>
So in the example:
```sh
# print the text "hello world"
echo hello world
```
The **echo** command got 2 arguments: "hello" and "world".<br>
If your argument contains a space, you can wrap the entire argument with the ```"``` character as follows:
```sh
# print the text "hello world"
echo "hello world"
```
In which case the **echo** command got only one argument: "hello world" and prints it.<br>
You can escape the ```"``` character using the ```"\"``` character, for example:
```sh
# print the text 'hello "world"'
echo "hello \"world\""
```
In the above example, the **echo** command got one argument: 'hello "world"' and prints it.<br>
The ```"\"``` is also used to escape the following:
* \n - End of line
* \r - Carriage return
* \t - Tab character
<aname="tutorial-commands-storing-output"></a>
#### Storing Output
Commands may return an output which can be stored in a variable.<br>
Variables in duckscript have no strict type.<br>
In the following example, the **set** command takes one argument and stores it in the **out** variable.
```sh
out = set "hello world"
```
Duckscript has only global scope, so once you have stored a value in a variable, you may use it anywhere in your script.
<aname="tutorial-commands-using-variables"></a>
#### Using Variables
Stored variables can be later on used as arguments for other commands.<br>
In order to use a variable, we need to wrap it as follows: ```${variable}```.<br>
<br>
The following example uses the **set** command to store a value in the **out** variable and then prints it:
```sh
out = set "hello world"
# This will print: "The out variable holds the value: hello world"
echo The out variable holds the value: ${out}
# This will print: "To use the out variable just write: ${out}"
echo To use the out variable just write: \${out}
```
In this example, although **out** holds the value **hello world** which contains a space, it is still considered as a single input argument to the **echo** command.<br>
In the second echo command we prevented the variable name from being replaced by escaping it using the ```\``` character.
<aname="tutorial-labels"></a>
### Labels
Labels are simple textual names you can give to a specific line.<br>
Commands like **goto** can then be used to make the script execution jump from its current position to the label position.<br>
For example:
```sh
goto :good
echo error!!!!
:good echo yay!!!!
```
<aname="tutorial-comments"></a>
### Comments
Comments are not executed and are simply in the code for documentation purposes.<br>
A document line must start with the ```#``` character.<br>
You can also have a comment after the command and the command will ignore it.<br>
For example:
```sh
# This is just a comment
echo This will print # But this will not
```
<aname="tutorial-pre-processing"></a>
### Pre Processing
Pre processing is the phase that duckscript is parsing the script content.<br>
It is possible to run specific commands at that phase to modify the script during the parsing phase.<br>
The basic syntax of a pre processing command line is:
The print pre processing command allows to print any number of arguments, which could be useful for debugging.<br>
In the following example, although the print command comes after the echo command, it will execute first as it is invoked in the parsing phase and not in the script execution phase which comes later:
```sh
# this will print "hello world during script execution"
echo hello world during script execution
# this will print "hello world during parsing"
!print hello world during parsing
```
<aname="tutorial-standard-api"></a>
### Standard API
Duckscript is split to several modules and while the script runner does not require it, by default it will come with the standard duckscript API called the duckscript SDK.<br>
This SDK holds the most common commands, some which execute actions (such as echo) and some which serve as flow control (such as function).<br>
The SDK enables users to develop their scripts and have a good starting point without the need to develop the commands on their own (as that is a bit more complex).
As mentioned before, duckscript is really simple and only has few basic rules.<br>
In order to provide a more richer development experience, common language features such as functions and conditional blocks have been implemented as commands.<br>
This is an example of the [function command](https://github.com/sagiegurari/duckscript/blob/master/docs/sdk.md#sdk__Function):
```sh
function print_first_and_second_argument
echo ${1} ${2}
return printed
end_function
function run_flow
status = print_first_and_second_argument hello world
echo The printout status is: ${status}
end_function
run_flow
```
This example demonstrates how functions as a concept do not need to be part of the language and can be implemented by anyone as a command.<br>
This also means that other developers can replace the function command with their implementation to provide additional/different functionality.
Below an example of [if/else command](https://github.com/sagiegurari/duckscript/blob/master/docs/sdk.md#sdk__If):
```sh
if set false
echo should not be here
elseif set true
echo in else if but not done yet
if set true
echo nested if
end_if
else
echo should not be here
end_if
```
Below an example of loops using the [for/in command](https://github.com/sagiegurari/duckscript/blob/master/docs/sdk.md#sdk__ForIn):
```sh
range = array 1 2 3
for i in range
for j in range
echo i: ${i} j: ${j}
end_for
end_for
release range
```
<aname="tutorial-standard-api-full-sdk-docs"></a>
#### Full SDK Docs
The full SDK docs can be found [here](https://github.com/sagiegurari/duckscript/blob/master/docs/sdk.md)
Keep in mind that the command names (such as **sdk::Echo**) can be used to invoke the commands, however for simplicity, the documentation
examples uses the alias form of the commands (for example: **echo**).<br>
Each command may have multiple aliases which can be used to invoke it.
<aname="tutorial-final-notes"></a>
### Final Notes
That's It!!!!<br>
That is all the language.<br>
Short, simple, only few rules to follow and you mastered duckscript.<br>
If you want to know what more you can do with it, look at the [SDK docs](https://github.com/sagiegurari/duckscript/blob/master/docs/sdk.md).<br>
If you want to know how to write your own commands or embed the duckscript runtime in your application, continue reading.
<aname="sdk-tutorial"></a>
## Duckscript Command Implementation Tutorial
Want to write new custom commands so you can use them in your duckscripts? great!<br>
Hopefully the following sections will help you gain the basic knowledge on how to write them.<br>
First of all it is important to understand that there are two types of commands:
* Commands which execute some action like copying files, printing some text to the console or returning an environment variable.
* Commands which provide flow control or some more complex action and require modifying the internal context in runtime.
<aname="sdk-tutorial-standard-commands"></a>
## Standard Commands
Commands are structs that must implement the Command trait.<br>
They must have a name, which is used to invoke the command.<br>
They optionally may have aliases which can also be used too invoke the command.<br>
They should return help documentation in markdown in order to generate SDK documentation (must for PRs to duckscript SDK).<br>
And they must implement the **run** function which holds the command logic.<br>
The run function accepts the command arguments (variables already replaced to actual values) and returns the command result.<br>
The command result can be one of the following:
* Continue(Option<String>) - Tells the runner to continue to the next command and optionally set the output variable the given value.
* GoTo(Option<String>, GoToValue) - Tells the runner to jump to the requested line or label and optionally set the output variable the given value.
* Error(String) - Tells the runner to stop the exection and return the error message.
* Exit(Option<String>) - Tells the runner to stop the execution and optionally set the output variable the given value.
Let's implement a simple **set** command which accepts a single argument and sets the output variable to that value.<br>
And if no argument was provided, return a None which will tell the runner to delete the output variable.<br>
Afterwards the runner should continue to the next line.<br>
So we need to use a Continue(Option<String>) result.<br>