<p align="center"> <img src="../docs/phlow.svg" alt="Phlow logo" width="140"/> </p> <h1 align="center">PHS β Phlow Script</h1>
**PHS** is a lightweight scripting format for [Phlow](https://github.com/lowcarboncode/phlow), built on top of [Rhai](https://rhai.rs/). It enables simple, dynamic behavior scripting using `.phs` files while deeply integrating with the Phlow runtime and module system.
## β¨ Overview
PHS (Phlow Script) brings the power of embedded scripting to YAML-based workflows. It's designed to let you inject dynamic logic through readable scripts, while preserving Phlow's declarative style.
You can inject modules directly into your PHS context via the `modules` section of your `.yaml` configuration. Each module declared becomes globally accessible in the `.phs` script, making it easy to mix scripting with orchestrated steps.
## π Summary
- [β¨ Overview](#-overview)
- [π Module Injection via YAML](#-module-injection-via-yaml)
- [π§ͺ Example](#-example)
- [main.yaml](#mainyaml)
- [script.phs](#scriptphs)
- [π‘Output](#output)
- [π File Extensions](#-file-extensions)
- [π Modules Supported in PHS](#-modules-supported-in-phs)
- [π§ Variables in PHS](#-variables-in-phs)
- [π€ Declaring Variables](#-declaring-variables)
- [βοΈ Reassigning Values](#οΈ-reassigning-values)
- [π Using Function Results](#-using-function-results)
- [π§± Arrays and Objects (Maps)](#-arrays-and-objects-maps)
- [π Arrays](#-arrays)
- [π Looping Through Arrays](#-looping-through-arrays)
- [π§³ Objects (Maps)](#-objects-maps)
- [π¦ Nesting](#-nesting)
- [π§ Conditionals in PHS](#-conditionals-in-phs)
- [β
Basic If](#-basic-if)
- [π If...Else](#-ifelse)
- [π Else If](#-else-if)
- [π Nested Conditions](#-nested-conditions)
- [π Loops in PHS](#-loops-in-phs)
- [π Looping Through an Array](#-looping-through-an-array)
- [π’ Looping with a Range](#-looping-with-a-range)
- [π Nested Loops](#-nested-loops)
- [π Breaking a Loop (not supported yet)](#-breaking-a-loop-not-supported-yet)
- [π§© Functions in PHS](#-functions-in-phs)
- [π Defining a Function](#-defining-a-function)
- [βΆοΈ Calling a Function](#οΈ-calling-a-function)
- [β©οΈ Returning Values](#οΈ-returning-values)
- [π§ Functions with Logic](#-functions-with-logic)
- [β οΈ Scope](#οΈ-scope)
- [𧬠PHS Syntax and Language Features](#-phs-syntax-and-language-features)
- [π Data Types in PHS](#-data-types-in-phs)
- [β Operators](#-operators)
- [π Global Scope](#-global-scope)
- [π§ͺ Expressions & Statements](#-expressions--statements)
- [π Ternary Expressions](#-ternary-expressions)
- [π Type Conversion Helpers](#-type-conversion-helpers)
- [π Working with Maps & Arrays](#-working-with-maps--arrays)
- [π§― Error Handling](#-error-handling)
- [πͺ Debugging Tools](#-debugging-tools)
- [𧬠Nested Access in YAML](#-nested-access-in-yaml)
- [πFuture Support Notes](#future-support-notes)
## π Module Injection via YAML
All modules declared in the YAML under `modules:` are automatically available inside your `.phs` script. For example, when you load the `log` module, its functions can be used directly in the script.
## π§ͺ Example
#### main.yaml
```yaml
main: cli
name: Example Cli
version: 1.0.0
description: Example CLI module
author: Your Name
modules:
- module: cli
version: latest
with:
additional_args: false
args:
- name: name
description: Name of the user
index: 1
type: string
required: false
- module: log
version: latest
steps:
- return: !import script.phs
```
#### script.phs
```rust
log("warn", `Hello, ${main.name}`);
```
### π‘Output
If the user runs:
```bash
phlow run main.yaml --name Philippe
```
The script will log:
```bash
[warn] Hello, Philippe
```
## π File Extensions
Phlow automatically loads `.phs` scripts when referenced in the flow via `!import`. These scripts are parsed and executed using the internal Rhai engine extended with Phlow modules.
### π Modules Supported in PHS
Any module that exposes scripting bindings can be used. Example modules:
- log
- cli
- http_server
- (and any custom Rust module registered with bindings)
## π§ Variables in PHS
You can declare and use variables in `.phs` scripts using the `let` keyword. These variables help you store temporary values, compose strings, perform calculations, or reuse values throughout your script.
### π€ Declaring Variables
```rust
let name = main.name;
let greeting = "Hello";
let message = `${greeting}, ${name}!`;
log("info", message);
```
### βοΈ Reassigning Values
Variables can be reassigned at any point:
```rust
let count = 1;
count = count + 1;
```
### π Using Function Results
You can assign the result of a function to a variable:
```rust
let status = "warn";
let msg = "Something happened";
log(status, msg);
```
## π§± Arrays and objects (maps)
PHS allows you to work with arrays and objects (maps) natively. These are useful when handling lists of items, grouping values, or building dynamic data structures.
### π Arrays
You can create arrays using square brackets []:
```rust
let fruits = ["apple", "banana", "orange"];
log("info", `First fruit: ${fruits[0]}`);
β Adding Items
fruits.push("grape");
```
### π Looping Through Arrays
```rust
for fruit in fruits {
log("debug", `Fruit: ${fruit}`);
}
```
### π§³ Objects (Maps)
You can define key-value objects using curly braces {}:
```rust
let user = #{
name: main.name,
age: 30,
active: true
};
log("info", `User: ${user.name} (age: ${user.age})`);
π§ Updating Properties
user.age = 31;
user.status = "online";
```
### π¦ Nesting
Objects and arrays can be nested:
```rust
let config = #{
tags: ["dev", "backend"],
options: #{
retries: 3,
timeout: 1000
}
};
log("debug", `Retries: ${config.options.retries}`);
```
## π§ Conditionals in PHS
PHS supports conditional logic using if, else if, and else blocks. These let you define dynamic behaviors based on data or user input.
### β
Basic If
```rust
if main.name == "Philippe" {
log("info", "Welcome back, boss!");
}
```
### π If...Else
```rust
if main.name == "Alice" {
log("info", "Hi Alice!");
} else {
log("info", "Hello, guest!");
}
```
### π Else If
```rust
if main.name == "Bob" {
log("info", "Hello Bob!");
} else if main.name == "Charlie" {
log("info", "Hey Charlie!");
} else {
log("info", "Who are you?");
}
```
### π Nested Conditions
```rust
if main.name != "" {
if main.name.len > 5 {
log("debug", "That's a long name.");
} else {
log("debug", "Short and sweet.");
}
}
```
Conditionals are a great way to adapt the behavior of your script based on CLI arguments, environment values, or runtime results.
## π Loops in PHS
PHS supports looping structures to help you iterate over arrays or repeat actions multiple times. The most common loop you'll use is the for loop.
### π Looping Through an Array
```rust
let fruits = ["apple", "banana", "orange"];
for fruit in fruits {
log("info", `Fruit: ${fruit}`);
}
```
### π’ Looping with a Range
You can loop through a range of numbers:
```rust
for i in 0..5 {
log("debug", `Index: ${i}`);
}
```
This prints numbers from 0 to 4.
### π Nested Loops
Loops can be nested for handling multi-dimensional data:
```rust
let matrix = [
[1, 2],
[3, 4]
];
for row in matrix {
for value in row {
log("debug", `Value: ${value}`);
}
}
```
### π Breaking a Loop (not supported yet)
Currently, there's no support for break or continue in .phs. Keep your loops simple and controlled with conditions when needed.
Loops are powerful for automating repetitive tasks or handling collections of data. Combine them with conditionals and functions to build expressive scripts.
## π§© Functions in PHS
You can define your own functions in .phs to reuse logic, organize your code, and make scripts cleaner and more modular.
### π Defining a Function
Use the fn keyword:
```rust
fn greet(name) {
log("info", `Hello, ${name}!`);
}
```
### βΆοΈ Calling a Function
Once defined, just call it like this:
```rust
greet("Philippe");
```
This will log:
```bash
[info] Hello, Philippe!
```
### β©οΈ Returning Values
Functions can return values using return:
```rust
fn double(n) {
return n * 2;
}
let result = double(5);
log("debug", `Result: ${result}`);
```
### π§ Functions with Logic
You can include conditionals, loops, and other functions inside your custom function:
```rust
fn log_fruits(fruits) {
for fruit in fruits {
log("info", `Fruit: ${fruit}`);
}
}
let list = ["apple", "banana", "orange"];
log_fruits(list);
```
### β οΈ Scope
Variables declared inside a function are local to that function unless returned or passed back explicitly.
# 𧬠PHS Syntax and Language Features
This guide expands on PHS (Phlow Script)'s syntax, types, and scripting features.
## π Data Types in PHS
PHS supports common primitive types, plus arrays and maps (objects):
| `bool` | `true`, `false` |
| `string` | `"hello"`, `` `hi ${name}` `` |
| `int` | `42` |
| `float` | `3.14` *(if enabled)* |
| `array` | `[1, 2, 3]` |
| `map` | `{ key: "value" }` |
| `fn` | `fn name(x) { ... }` |
## β Operators
| `+` | Add / Concatenate | `2 + 3`, `"a" + "b"` |
| `-` | Subtract | `10 - 4` |
| `*` | Multiply | `5 * 6` |
| `/` | Divide | `9 / 3` |
| `%` | Modulo | `10 % 3` |
| `==` | Equals | `x == y` |
| `!=` | Not equal | `x != y` |
| `<`, `>`, `<=`, `>=` | Comparisons | `x >= 10` |
| `&&` | Logical AND | `x && y` |
| `||` | Logical OR | `x || y` |
| `!` | Logical NOT | `!x` |
## π Global Scope
- `main` β the full YAML input
- Declared `modules` β globally exposed
- Utility functions like `log(...)`
## π§ͺ Expressions & Statements
```rust
let upper = main.name.to_uppercase().trim();
```
## π Ternary Expressions
```rust
let msg = main.name == "" ? "Anonymous" : `Hello, ${main.name}`;
```
## π Type Conversion Helpers
```rust
let number = "42".to_int();
let flag = "true".to_bool();
```
## π Working with Maps & Arrays
```rust
let keys = user.keys();
let vals = user.values();
if fruits.contains("banana") {
log("info", "Found it!");
}
```
## π§― Error Handling
Structured try/catch is not supported.
## πͺ Debugging Tools
```rust
log("debug", `Debugging var: ${data}`);
```
## 𧬠Nested Access in YAML
```yaml
config:
retries: 3
labels:
- core
- beta
```
```rust
let retry = main.config.retries;
let tag = main.config.labels[0];
```
## πFuture Support Notes
- `break` / `continue` β *not supported yet*
- `match` / pattern matching β *planned*
- `try/catch` β *TBD*