4 releases
new 0.0.8 | Apr 23, 2025 |
---|---|
0.0.7 | Apr 23, 2025 |
0.0.6 | Apr 23, 2025 |
0.0.5 | Apr 23, 2025 |
#597 in Debugging
83 downloads per month
Used in 3 crates
(2 directly)
20KB
179 lines
PHS – Phlow Script
PHS is a lightweight scripting format for Phlow, built on top of Rhai. 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
- 🔌 Module Injection via YAML
- 🧪 Example
- 📁 File Extensions
- 🔐 Modules Supported in PHS
- 🧠 Variables in PHS
- 🧱 Arrays and Objects (Maps)
- 🧭 Conditionals in PHS
- 🔁 Loops in PHS
- 🧩 Functions in PHS
- 🧬 PHS Syntax and Language Features
🔌 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
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
log("warn", `Hello, ${main.name}`);
💡Output
If the user runs:
phlow run main.yaml --name Philippe
The script will log:
[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
let name = main.name;
let greeting = "Hello";
let message = `${greeting}, ${name}!`;
log("info", message);
✍️ Reassigning Values
Variables can be reassigned at any point:
let count = 1;
count = count + 1;
🔄 Using Function Results
You can assign the result of a function to a variable:
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 []:
let fruits = ["apple", "banana", "orange"];
log("info", `First fruit: ${fruits[0]}`);
➕ Adding Items
fruits.push("grape");
🔄 Looping Through Arrays
for fruit in fruits {
log("debug", `Fruit: ${fruit}`);
}
🧳 Objects (Maps)
You can define key-value objects using curly braces {}:
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:
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
if main.name == "Philippe" {
log("info", "Welcome back, boss!");
}
🔁 If...Else
if main.name == "Alice" {
log("info", "Hi Alice!");
} else {
log("info", "Hello, guest!");
}
🔀 Else If
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
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
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:
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:
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:
fn greet(name) {
log("info", `Hello, ${name}!`);
}
▶️ Calling a Function
Once defined, just call it like this:
greet("Philippe");
This will log:
[info] Hello, Philippe!
↩️ Returning Values
Functions can return values using return:
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:
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):
Type | Example |
---|---|
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
Operator | Description | Example |
---|---|---|
+ |
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 NOT | !x |
🌐 Global Scope
main
– the full YAML input- Declared
modules
– globally exposed - Utility functions like
log(...)
🧪 Expressions & Statements
let upper = main.name.to_uppercase().trim();
🔀 Ternary Expressions
let msg = main.name == "" ? "Anonymous" : `Hello, ${main.name}`;
🔎 Type Conversion Helpers
let number = "42".to_int();
let flag = "true".to_bool();
🛠 Working with Maps & Arrays
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
log("debug", `Debugging var: ${data}`);
🧬 Nested Access in YAML
config:
retries: 3
labels:
- core
- beta
let retry = main.config.retries;
let tag = main.config.labels[0];
📍Future Support Notes
break
/continue
→ not supported yetmatch
/ pattern matching → plannedtry/catch
→ TBD
Dependencies
~13–21MB
~286K SLoC