What is JCL?

JCL (Jack-of-All Configuration Language) is a modern, powerful configuration language designed for clarity, flexibility, and developer productivity. It combines the simplicity of JSON with the power of a full programming language.

Key Features

  • Simple & Readable - Clean syntax without excessive punctuation
  • Powerful Functions - 70+ built-in functions for strings, encoding, hashing, dates, and more
  • Type Safe - Optional type annotations and schema validation
  • Developer Friendly - LSP support, syntax highlighting, auto-formatting
  • Cross-Platform - Native Rust implementation with bindings for Python, Node.js, Go, Java, and Ruby

Installation

From Source

git clone https://github.com/hemmer-io/jcl
cd jcl
cargo build --release

# Install CLI tools
cargo install --path .

Using Language Bindings

Python:

pip install jcl-lang

Node.js:

npm install @hemmer-io/jcl

Go:

go get github.com/hemmer-io/jcl

Your First JCL File

Create a file config.jcf:

name = "my-app"
version = "1.0.0"
port = 8080
enabled = true

database = (
    host = "localhost",
    port = 5432,
    name = "myapp_db"
)

features = ["auth", "api", "websockets"]

Evaluate it:

jcl eval config.jcf

Basic Syntax

Variables

name = "John"
age = 30
active = true
score = 95.5

Lists

ports = [80, 443, 8080]
names = ["Alice", "Bob", "Carol"]
mixed = [1, "two", true, 4.5]

# Generate number sequences with ranges
numbers = [0..5]           # [0, 1, 2, 3, 4, 5]
evens = [0..10:2]          # [0, 2, 4, 6, 8, 10]
countdown = [5..1:-1]      # [5, 4, 3, 2, 1]

Maps (Objects)

person = (
    name = "Alice",
    age = 30,
    email = "alice@example.com"
)

# Nested maps
config = (
    app = (
        name = "myapp",
        version = "1.0.0"
    ),
    database = (
        host = "localhost",
        port = 5432
    )
)

String Interpolation

name = "World"
greeting = "Hello, ${name}!"

port = 8080
url = "http://localhost:${port}"

Multi-line Strings (Heredocs)

Heredoc syntax is ideal for embedding scripts, SQL, YAML, or any multi-line content:

# Basic heredoc
startup_script = <<BASH
#!/bin/bash
echo "Starting application..."
./app --port 8080
BASH

# Heredoc with variable interpolation
db_name = "myapp"
init_sql = <<SQL
CREATE DATABASE ${db_name};
USE ${db_name};
CREATE TABLE users (id INT, name VARCHAR(100));
SQL

# Heredoc with indentation stripping (<<-)
# Automatically removes common leading whitespace
nginx_config = <<-CONF
    server {
        listen 80;
        server_name example.com;
        root /var/www/html;
    }
CONF

Comments

# Single-line comment

x = 42  # Inline comment

Conditionals

env = "production"
debug = if env == "development" then true else false

port = if env == "production" then 80 else 8080

Functions

# Define a function
fn double(x) = x * 2

# Use it
result = double(21)  # 42

# Function with multiple parameters
fn add(a, b) = a + b
sum = add(10, 20)  # 30

# Multi-line function
fn greet(name) = (
    prefix = "Hello",
    message = "${prefix}, ${name}!"
)

List Comprehensions

# Map over a list
numbers = [1, 2, 3, 4, 5]
doubled = [x * 2 for x in numbers]
# Result: [2, 4, 6, 8, 10]

# Use ranges to generate sequences
squares = [x * x for x in [1..10]]
# Result: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

# Filter a list
evens = [x for x in numbers if x % 2 == 0]
# Result: [2, 4]

# Transform objects
users = [(name = "Alice"), (name = "Bob")]
names = [user.name for user in users]
# Result: ["Alice", "Bob"]

# Extract attributes with splat operator (shorthand)
users = [(name = "Alice", age = 30), (name = "Bob", age = 25)]
names = users[*].name  # ["Alice", "Bob"]
ages = users[*].age    # [30, 25]

Type Annotations

name: String = "Alice"
age: Int = 30
score: Float = 95.5
active: Bool = true

numbers: List<Int> = [1, 2, 3]
config: Map<String, Int> = (x = 1, y = 2)

Built-in Functions

JCL comes with 70+ built-in functions organized by category:

String Functions

upper("hello")           # "HELLO"
lower("WORLD")           # "world"
trim("  space  ")        # "space"
split("a,b,c", ",")      # ["a", "b", "c"]
join(["a", "b"], "-")    # "a-b"
replace("hello", "l", "L")  # "heLLo"

List Functions

length([1, 2, 3])        # 3
reverse([1, 2, 3])       # [3, 2, 1]
sort([3, 1, 2])          # [1, 2, 3]
unique([1, 2, 2, 3])     # [1, 2, 3]
flatten([[1, 2], [3, 4]]) # [1, 2, 3, 4]

Encoding Functions

base64_encode("hello")   # "aGVsbG8="
base64_decode("aGVsbG8=") # "hello"
url_encode("hello world") # "hello%20world"

Hashing Functions

md5("hello")            # "5d41402abc4b2a76b9719d911017c592"
sha1("hello")           # "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"
sha256("hello")         # "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c..."

Date/Time Functions

now()                   # Current timestamp
format_date(now(), "%Y-%m-%d")

Type Checking

is_string("hello")      # true
is_int(42)              # true
is_float(3.14)          # true
is_bool(true)           # true
is_list([1, 2])         # true
is_map((x = 1))         # true

See full function reference →

CLI Tools

JCL comes with a comprehensive suite of tools:

jcl - Main CLI

# Evaluate a file
jcl eval config.jcf

# Start REPL
jcl repl

# Format files
jcl fmt config.jcf

# Lint files
jcl lint config.jcf

jcl-validate - Schema Validation

# Validate against a schema
jcl-validate config.jcf --schema schema.yaml

jcl-migrate - Format Migration

# Convert from JSON
jcl-migrate config.json > config.jcf

# Convert from YAML
jcl-migrate config.yaml -o config.jcf

# Convert from TOML
jcl-migrate config.toml > config.jcf

jcl-fmt - Code Formatter

# Format files in place
jcl-fmt config.jcf

# Check formatting (CI mode)
jcl-fmt --check config.jcf

# Format multiple files
jcl-fmt *.jcf

jcl-watch - Auto-format on Save

# Watch a directory
jcl-watch ./configs --recursive

# Watch specific files
jcl-watch config.jcf app.jcf

jcl-bench - Performance Benchmarking

# Benchmark a file
jcl-bench config.jcf

# Run built-in benchmarks
jcl-bench --builtin

# Custom iteration count
jcl-bench config.jcf -n 10000

jcl-lsp - Language Server

# Start LSP server
jcl-lsp

Interactive REPL

The JCL REPL provides an interactive environment for experimenting:

$ jcl repl
JCL REPL v0.1.0
Type :help for help, :quit to exit

jcl:1 x = 42
✓
jcl:2 x * 2
84
jcl:3 fn double(n) = n * 2
✓
jcl:4 double(21)
42
jcl:5 :vars
Variables:
  double = <function>
  x = 42
jcl:6 :quit
Goodbye!

REPL Features:

  • Persistent history (~/.jcf_history)
  • Multi-line input (use \ at end of line)
  • Tab completion
  • History search (Ctrl-R)
  • Variable inspection (:vars)

Example: Complete Configuration

# Application Configuration
app_name = "web-server"
version = "1.2.3"
environment = "production"

# Server configuration
server = (
    host = "0.0.0.0",
    port = if environment == "production" then 80 else 8080,
    workers = 4,
    timeout = 30
)

# Database configuration
database = (
    host = "db.example.com",
    port = 5432,
    name = "${app_name}_${environment}",
    pool_size = 20,
    ssl = environment == "production"
)

# Feature flags
features = (
    auth = true,
    api = true,
    websockets = environment == "production",
    debug = environment != "production"
)

# Allowed origins for CORS
cors_origins = [
    "https://example.com",
    "https://www.example.com"
]

# Build connection string
db_url = "postgres://${database.host}:${database.port}/${database.name}"

# Generate a unique deployment ID
deployment_id = sha256("${app_name}-${version}-${now()}")

# Log configuration
log_level = if features.debug then "debug" else "info"
log_format = "json"

Working with Multiple Files

JCL supports importing configurations from other files for better organization and reusability.

Basic Imports

# database.jcf
database = (
    host = "localhost",
    port = 5432,
    name = "myapp_db"
)

connection_string = "postgres://${database.host}:${database.port}/${database.name}"
# main.jcf
import "./database.jcf" as db

# Use imported configuration
app_config = (
    name = "myapp",
    db_url = db.connection_string
)

Selective Imports

Import only what you need:

# main.jcf
import (database, connection_string) from "./database.jcf"

# Use directly
app_config = (name = "myapp", db_url = connection_string)

Organizing Configuration

Recommended structure:

config/
├── database.jcf       # Database settings
├── server.jcf         # Server configuration
├── features.jcf       # Feature flags
└── main.jcf           # Main config that imports others

main.jcf:

import "./database.jcf" as db
import "./server.jcf" as srv
import "./features.jcf" as feat

# Combine configurations
config = (
    database = db.database,
    server = srv.server,
    features = feat.features
)

Learn more about imports →

Next Steps

Editor Support

  • VS Code - Full LSP support with syntax highlighting and auto-completion
  • Vim - Syntax highlighting available in editors/vim/
  • Any LSP-compatible editor - Use jcl-lsp for full language support

Back to top

Copyright © 2025 JCL. Distributed under the MIT OR Apache-2.0 license.