CLI Examples

These examples demonstrate using the salata CLI binary to process .slt files and output text to stdout. All examples live in the examples/cli/ directory of the repository.

To run any example:

salata --config examples/cli/<example>/config.toml examples/cli/<example>/<file>.slt

hello-world/

The simplest possible examples -- one .slt file per runtime, each printing a greeting. These are the best starting point for verifying that your runtimes are installed and configured correctly.

python.slt:

<python>
print("Hello from Python!")
</python>

ruby.slt:

<ruby>
puts "Hello from Ruby!"
</ruby>

javascript.slt:

<javascript>
println("Hello from JavaScript!");
</javascript>

typescript.slt:

<typescript>
const greeting: string = "Hello from TypeScript!";
println(greeting);
</typescript>

php.slt:

<php>
echo "Hello from PHP!\n";
</php>

shell.slt:

<shell>
echo "Hello from Shell!"
</shell>

Run all of them:

salata --config examples/cli/hello-world/config.toml examples/cli/hello-world/python.slt
salata --config examples/cli/hello-world/config.toml examples/cli/hello-world/ruby.slt
salata --config examples/cli/hello-world/config.toml examples/cli/hello-world/javascript.slt
salata --config examples/cli/hello-world/config.toml examples/cli/hello-world/typescript.slt
salata --config examples/cli/hello-world/config.toml examples/cli/hello-world/php.slt
salata --config examples/cli/hello-world/config.toml examples/cli/hello-world/shell.slt

data-processing/

Three examples demonstrating Salata as a data processing tool, producing formatted text output rather than HTML.

csv-table.slt -- Python processes inline CSV data into a formatted ASCII table:

<python>
import csv
import io

data = """name,role,department
Alice,Engineer,Backend
Bob,Designer,Frontend
Charlie,Manager,Operations
Diana,Engineer,Frontend
Eve,Analyst,Data"""

reader = csv.DictReader(io.StringIO(data))
rows = list(reader)
headers = rows[0].keys()

# Calculate column widths
widths = {h: max(len(h), max(len(r[h]) for r in rows)) for h in headers}

# Print header
header_line = " | ".join(h.ljust(widths[h]) for h in headers)
print(header_line)
print("-+-".join("-" * widths[h] for h in headers))

# Print rows
for row in rows:
    print(" | ".join(row[h].ljust(widths[h]) for h in headers))

print(f"\nTotal: {len(rows)} records")
</python>

json-filter.slt -- Ruby filters and sorts an inline JSON array:

<ruby>
require 'json'

data = JSON.parse('[
  {"name": "Alice", "age": 30, "city": "New York"},
  {"name": "Bob", "age": 25, "city": "London"},
  {"name": "Charlie", "age": 35, "city": "Tokyo"},
  {"name": "Diana", "age": 28, "city": "Paris"},
  {"name": "Eve", "age": 32, "city": "Berlin"}
]')

# Filter: age >= 28, sort by name
filtered = data
  .select { |p| p["age"] >= 28 }
  .sort_by { |p| p["name"] }

puts "People aged 28 and older (sorted by name):"
puts "=" * 40
filtered.each do |person|
  puts "  #{person['name']} (#{person['age']}) — #{person['city']}"
end
puts "=" * 40
puts "#{filtered.length} of #{data.length} matched"
</ruby>

system-report.slt -- Shell generates a system information report:

<shell>
echo "=== System Report ==="
echo ""
echo "Hostname: $(hostname)"
echo "Kernel:   $(uname -sr)"
echo "Arch:     $(uname -m)"
echo "Date:     $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
echo "--- Disk Usage ---"
df -h / | tail -1 | awk '{printf "  Root: %s used of %s (%s)\n", $3, $2, $5}'
echo ""
echo "=== End Report ==="
</shell>

config-generator/

Demonstrates using Salata to generate configuration files. Multiple runtimes collaborate to produce a complete nginx configuration.

nginx.slt -- Shell detects the CPU count, Python computes upstream servers and outputs a full nginx.conf:

<shell>
# Detect available CPU cores for worker_processes
CORES=$(nproc || echo 2)
echo "$CORES"
</shell>
<python>
# Define upstream application servers
upstreams = [
    ("app1", "127.0.0.1", 8001),
    ("app2", "127.0.0.1", 8002),
    ("app3", "127.0.0.1", 8003),
]

cores = 2

print(f"""# Auto-generated nginx.conf
# Generated by Salata config-generator example

worker_processes {cores};

events {{
    worker_connections 1024;
}}

http {{
    upstream backend {{""")

for name, host, port in upstreams:
    print(f"        server {host}:{port};  # {name}")

print("""    }

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        location /static/ {
            root /var/www/html;
            expires 30d;
        }
    }
}""")
</python>

Usage:

salata --config examples/cli/config-generator/config.toml examples/cli/config-generator/nginx.slt > nginx.conf

markdown-report/

Shows how to generate Markdown output using multiple runtimes, with cross-runtime data sharing via #set/#get.

report.slt -- Python computes project statistics, Ruby formats a Markdown table, Shell adds build metadata:

<python>
# Compute some statistics
projects = [
    {"name": "Alpha", "status": "Complete", "tasks": 24, "done": 24},
    {"name": "Beta", "status": "In Progress", "tasks": 18, "done": 12},
    {"name": "Gamma", "status": "In Progress", "tasks": 30, "done": 21},
    {"name": "Delta", "status": "Planning", "tasks": 15, "done": 0},
]
total_tasks = sum(p["tasks"] for p in projects)
total_done = sum(p["done"] for p in projects)
overall_pct = round(total_done / total_tasks * 100, 1)

#set("projects", projects)
#set("total_tasks", total_tasks)
#set("total_done", total_done)
#set("overall_pct", overall_pct)
</python>
<ruby>
projects = #get("projects")
total_tasks = #get("total_tasks")
total_done = #get("total_done")
overall_pct = #get("overall_pct")

puts "# Project Status Report"
puts ""
puts "**Overall Progress:** #{total_done}/#{total_tasks} tasks (#{overall_pct}%)"
puts ""
puts "| Project | Status      | Progress        |"
puts "|---------|-------------|-----------------|"
projects.each do |p|
  pct = p["tasks"] > 0 ? (p["done"].to_f / p["tasks"] * 100).round(0) : 0
  bar = "#" * (pct / 10) + "." * (10 - pct / 10)
  puts "| #{p['name'].ljust(7)} | #{p['status'].ljust(11)} | #{p['done']}/#{p['tasks'].to_s.ljust(2)} [#{bar}] |"
end
puts ""
</ruby>
<shell>
echo "## Build Info"
echo ""
echo "- **Generated:** $(date '+%Y-%m-%d %H:%M:%S')"
echo "- **Host:** $(hostname)"
echo "- **Platform:** $(uname -s) $(uname -m)"
</shell>

Usage:

salata --config examples/cli/markdown-report/config.toml examples/cli/markdown-report/report.slt > report.md

cross-runtime-pipeline/

The flagship cross-runtime example: Python generates data, Ruby transforms it, JavaScript formats the output. This example has its own dedicated chapter -- see Cross-Runtime Pipeline for a full walkthrough.


scope-demo/

Two files demonstrating the difference between shared and isolated scope.

shared-scope.slt -- Two Python blocks that share a process. The second block can access variables defined in the first:

<python>
# First block: define variables
message = "Hello from the first block"
counter = 42
items = ["apple", "banana", "cherry"]
print(f"Block 1: Set message='{message}', counter={counter}")
</python>

Text between blocks — the Python process is still alive.

<python>
# Second block: access variables from the first block
# These variables are available because shared_scope = true (default)
print(f"Block 2: message='{message}'")
print(f"Block 2: counter={counter}")
print(f"Block 2: items={items}")

counter += 1
print(f"Block 2: incremented counter to {counter}")
</python>

isolated-scope.slt -- Two Python blocks with scope="isolated", each running in its own process. The second block cannot access the first block's variables:

<python scope="isolated">
# First block (isolated): define variables
message = "Hello from the first block"
counter = 42
print(f"Block 1: Set message='{message}', counter={counter}")
</python>

Text between blocks — each block gets a fresh Python process.

<python scope="isolated">
# Second block (isolated): try to access variables from block 1
# This will fail because scope="isolated" means a new process
try:
    print(f"Block 2: message='{message}'")
except NameError as e:
    print(f"Block 2: Cannot access 'message' — {e}")

try:
    print(f"Block 2: counter={counter}")
except NameError as e:
    print(f"Block 2: Cannot access 'counter' — {e}")

print("Block 2: Each isolated block starts fresh!")
</python>

json-api-mock/

Demonstrates using Salata to generate JSON output with the #content-type directive and cross-runtime data sharing.

api.slt:

#content-type application/json
<python>
import json

# Build the API response data
users = [
    {"id": 1, "name": "Alice", "email": "alice@example.com", "role": "admin"},
    {"id": 2, "name": "Bob", "email": "bob@example.com", "role": "user"},
    {"id": 3, "name": "Charlie", "email": "charlie@example.com", "role": "user"},
]

#set("users", users)
</python>
<javascript>
const users = #get("users");

const response = {
    status: "ok",
    data: users,
    meta: {
        total: users.length,
        page: 1,
        per_page: 10
    }
};

print(JSON.stringify(response, null, 2));
</javascript>

multi-format/

Three .slt files that take the same product inventory data and output it in three different formats: plain text, CSV, and YAML.

report.txt.slt -- Plain text table:

<python>
products = [
    {"name": "Laptop", "price": 999.99, "stock": 45},
    {"name": "Mouse", "price": 29.99, "stock": 200},
    {"name": "Keyboard", "price": 79.99, "stock": 120},
    {"name": "Monitor", "price": 449.99, "stock": 30},
]

print("PRODUCT INVENTORY REPORT")
print("=" * 45)
for p in products:
    status = "LOW" if p["stock"] < 50 else "OK"
    print(f"  {p['name']:<12} ${p['price']:>8.2f}  Stock: {p['stock']:>3}  [{status}]")
print("=" * 45)
print(f"  Total items: {sum(p['stock'] for p in products)}")
print(f"  Total value: ${sum(p['price'] * p['stock'] for p in products):,.2f}")
</python>

report.csv.slt -- CSV format:

<python>
products = [
    {"name": "Laptop", "price": 999.99, "stock": 45},
    {"name": "Mouse", "price": 29.99, "stock": 200},
    {"name": "Keyboard", "price": 79.99, "stock": 120},
    {"name": "Monitor", "price": 449.99, "stock": 30},
]

print("name,price,stock,status")
for p in products:
    status = "LOW" if p["stock"] < 50 else "OK"
    print(f"{p['name']},{p['price']},{p['stock']},{status}")
</python>

report.yaml.slt -- YAML format:

<python>
products = [
    {"name": "Laptop", "price": 999.99, "stock": 45},
    {"name": "Mouse", "price": 29.99, "stock": 200},
    {"name": "Keyboard", "price": 79.99, "stock": 120},
    {"name": "Monitor", "price": 449.99, "stock": 30},
]

print("inventory:")
print(f"  total_items: {sum(p['stock'] for p in products)}")
print(f"  total_value: {sum(p['price'] * p['stock'] for p in products):.2f}")
print("  products:")
for p in products:
    status = "low" if p["stock"] < 50 else "ok"
    print(f"    - name: {p['name']}")
    print(f"      price: {p['price']}")
    print(f"      stock: {p['stock']}")
    print(f"      status: {status}")
</python>

Usage:

salata --config examples/cli/multi-format/config.toml examples/cli/multi-format/report.txt.slt
salata --config examples/cli/multi-format/config.toml examples/cli/multi-format/report.csv.slt > report.csv
salata --config examples/cli/multi-format/config.toml examples/cli/multi-format/report.yaml.slt > report.yaml