phs 0.0.8

Runtime for Phlow Script, PHS
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
<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):

| 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 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*