Documentation

Morflow Engine & Syntax Reference

Section 1

1. CLI Reference

The morflow CLI handles dependency resolution, offline action caching, action discovery, and action specification inspection.

1.1. Installation

Install the standalone morflow command-line interface globally via cURL, npm, or pip:

CLI InstallationShell
# Install standalone native binary via cURL
curl -fsSL https://morflow.org/install.sh | bash

# Or install via NPM (Global CLI)
npm install -g morflow

# Or install via Pip (Python CLI)
pip install morflow

1.2. Core CLI Commands

CommandSyntaxDescription
checkmorflow check <file.morf> [-p <dir>]Validates syntax, verifies Single Static Assignment (SSA), inspects dynamic action FFI types, and performs a dry-run execution simulation.
prepmorflow prep <file.morf> [-p <dir>] [-f]Pre-downloads required actions for completely offline execution.
searchmorflow search <query> [-l <limit>]Fuzzy searches actions in the central registry.
specmorflow spec <pkg>/<ver>/<action>Inspects argument schemas, types, and action documentation.
installmorflow install <action> [-p <dir>] [-f]Installs a specific action binary directly into the cache.
listmorflow list [-p <dir>]Lists all installed action binaries in the local cache.
cleanmorflow clean [-p <dir>]Removes cached action binaries from the action path.

1.3. Pipeline Verification (check)

The check command performs a comprehensive static analysis and dry-run simulation of your .morf pipeline without requiring live input data. It guarantees that syntax, Single Static Assignment (SSA) rules, action binaries, and data type transitions are 100% valid before deployment:

Pipeline Static Analysis & Dry-RunBash
# Verify pipeline syntax, action binaries, and type compatibility
morflow check ./pipeline.morf

# Check with a custom action cache directory
morflow check ./pipeline.morf --path ./actions_cache

1.4. Offline Prep (prep)

In production deployments, you can ensure zero network requests at runtime by pre-downloading all action binaries during container image build:

Offline Action PreparationBash
# Download required action binaries into local cache
morflow prep ./pipeline.morf

# Force re-download even if action is already present
morflow prep ./pipeline.morf --force

# Download action binaries into a custom directory
morflow prep ./pipeline.morf --path ./actions_cache

1.5. Search & Spec

Search for available actions and view their expected parameters directly in your terminal:

Action DiscoveryBash
# Search for color or audio transformation actions
morflow search color
morflow search biquad

# Inspect argument schemas and SPEC.md docs
morflow spec image_essentials/latest/color_adjust
morflow spec audio_essentials/latest/biquad_filter

1.6. Environment Variables

VariableDefaultDescription
MORFLOW_ACTIONS_PATH~/.morflow/actionsDirectory where Morflow caches and resolves compiled dynamic action libraries (.so, .dylib, .dll).
MORFLOW_REPOJiraPit/MorflowTarget GitHub repository used by the CLI to query releases and download precompiled action binaries.

Section 2

2. .morf Syntax Guide

Learn .morf syntax from the ground up: starting from the smallest building block (an Action), to chaining them into a Flow, producing results with Emit, and composing Multiple Flows in a single file.

2.1. Anatomy of an Action

An Action is the fundamental atomic unit of computation in Morflow. It represents an isolated, high-performance compiled operation (e.g. image resizing, Gaussian blur, audio biquad filtering, tensor normalization).

Actions can be invoked in three main forms:

Action Invocation Formsmorf
# Form 1: Zero-argument actions (parentheses are optional)
to_image
to_wav
identity

# Form 2: Keyword arguments (key=value)
resize(width=512, height=512, filter="bilinear")
color_adjust(contrast=1.15, saturation=1.05, brightness=0.02)
biquad_filter(type="highpass", freq=30.0, sample_rate=48000)

# Form 3: Variable arguments (passing pipeline parameters to action parameters)
resize(width=$target_width, height=$target_height)
resample(to_rate=$target_sample_rate)

Before using actions, import their parent Action Pack at the top of your .morf file:

Importing Actionsmorf
# Package-level import (brings actions into scope or under an alias)
import base/latest
import audio_essentials/latest as audio

# Item import (brings specific actions into local scope)
from image_essentials/latest import to_tensor, resize, color_adjust, gaussian_blur, to_image

2.2. Constructing a Flow

A Flow connects an input variable through a sequence of actions using the >> dataflow operator. Each action receives the output payload of the previous action with zero memory copying.

Inputs and parameters are declared using accept at the top of the file:

Declaring Inputs and Parametersmorf
# Required input passed from host runtime
accept $img_in

# Configurable parameters with default values
accept $target_width = 512
accept $target_height = 512

Once inputs are declared, chain actions into a continuous dataflow stream:

A Sequential Flow Chainmorf
# The flow starts with the input variable $img_in and passes data step-by-step
$img_in
    >> to_tensor(color="rgb", dtype="f32", layout="hwc", normalize=true)
    >> resize(width=$target_width, height=$target_height, filter="bilinear")
    >> color_adjust(contrast=1.15, saturation=1.05)
    >> gaussian_blur(sigma=1.2)
    >> to_image(color="rgba", dtype="u8")

2.3. Emitting Output & Intermediate Emission (emit)

Outputs are transferred back to the calling host application across many platforms and languages using the emit keyword. Importantly, emit is a non-terminating passthrough operation — it yields the computed payload at that point in execution while passing the data directly down the chain for subsequent operations.

This enables intermediate emission, allowing a single continuous flow to dispatch checkpoints, previews, or multi-scale outputs without duplicating upstream work or branching into separate pipelines:

Intermediate Emission & Chainingmorf
# Form A: Single primary emission
$img_in >> to_tensor >> resize(width=512, height=512) >> emit

# Form B: Named stream emission
$audio_in >> to_audio >> normalize >> emit("normalized_audio")

# Form C: Intermediate emission with chained downstream processing
$img_in
    >> to_tensor(color="rgb", normalize=true)
    >> resize(width=1024, height=1024)
    >> emit("high_res")               # Emits intermediate 1024x1024 tensor to host
    >> resize(width=256, height=256)   # Continues stream without re-ingesting $img_in
    >> color_adjust(contrast=1.2)
    >> emit("preview")                # Emits downstream preview thumbnail
Host Return Type & Multi-Emission: When a flow contains multiple or named emissions (such as intermediate emissions), host SDKs receive a structured dictionary or map of typed outputs (e.g. dict[str, np.ndarray] in Python, Map<String, MorflowTensor> in Java, or MorflowOutput in Rust). For streaming invocations, intermediate emissions can also be consumed asynchronously as they are produced.

2.4. Multiple Flows in One File

A single .morf file can contain multiple flows. You can capture intermediate states mid-stream into a tapped variable using >> $var_name, and then spawn independent branch flows from that variable.

Morflow automatically compiles all flows into an acyclic dependency graph (DAG) and executes non-dependent branches concurrently across multi-threaded CPU cores with zero lock overhead:

Multi-Flow Audio Split Pipelinemorf
import audio_essentials/latest
 
accept $input_audio

# FLOW 1: Ingest input audio, convert to structured stream, and tap into $decoded
$input_audio >> to_audio >> $decoded

# FLOW 2: Extract channel 0 (Left), apply low-pass filtering, convert to WAV, and emit
$decoded[0] >> biquad_filter(type="lowpass", freq=1200.0) >> to_wav >> emit("left_filtered")

# FLOW 3: Extract channel 1 (Right), apply high-pass filtering, convert to WAV, and emit
$decoded[1] >> biquad_filter(type="highpass", freq=800.0) >> to_wav >> emit("right_filtered")

In this pipeline, Flow 2 and Flow 3 run concurrently in parallel immediately after Flow 1 finishes. Because both downstream branches depend solely on the shared output of Flow 1 ($decoded) but have no dependency on each other, Morflow's runtime automatically schedules and evaluates them simultaneously across CPU worker threads with zero lock overhead.

Static Single Assignment (SSA) Rule: A tapped variable identifier (e.g. $decoded) cannot be reassigned or mutated within the same scope. This mathematical guarantee ensures safe multi-threaded memory buffer sharing without copy overhead.

2.5. Advanced Control Flow (Loops & Conditionals)

Morflow includes built-in constructs for parallel loops, conditional branching, and pattern routing within flows:

Parallel Map Loop (each): The each block applies a transformation sub-flow across elements of a collection, batch, or multi-channel audio/image tensor. Each iteration is executed concurrently in parallel across multi-threaded CPU workers. Once all parallel iterations complete, their resulting outputs are automatically stacked back together in sequence order and passed directly to the next action in the stream.

Parallel Map Loop (each)morf
# Iterates each channel concurrently in parallel, transforms, and stacks back together
$input_audio
    >> to_audio(sample_rate=44100)
    >> each ($channel) {
        $channel
            >> biquad_filter(type="highpass", freq=30.0, sample_rate=48000)
            >> compressor(threshold_db=-12.0, ratio=3.0)
    }
    >> stereo_widen(width=1.2)   # Receives the stacked multi-channel stream
    >> to_wav
    >> emit
Emission Rule for Loops: Direct emission (emit) inside an each block is strictly not allowed. Because loop iterations execute concurrently and asynchronously out-of-order, emissions must be performed outside the each block on the aggregated and stacked stream to maintain deterministic host synchronization and graph integrity.

Conditionals & Dynamic Routing: Branch pipelines conditionally based on runtime metadata or route streams dynamically:

Conditionals & Dynamic Routingmorf
# 1. If / Else branching
if ($sample_rate != 48000) {
    $input_audio >> resample(to_rate=48000) >> $prepared
} else {
    $input_audio >> $prepared
}

# 2. Dynamic route pattern matching
$prepared >> route {
    $channels == 1 => biquad_filter(type="lowpass", freq=1000.0)
    $channels == 2 => stereo_widen(width=1.4)
    else => identity
} >> to_wav >> emit

2.6. Complete End-to-End Examples

Here are two complete, production-ready .morf pipeline definitions:

Complete Vision Pipelinemorf
# Morflow Image Processing Pipeline (.morf)

import base/latest
from image_essentials/latest import to_tensor, resize, color_adjust, gaussian_blur, to_image

accept $img_in
accept $target_width = 512
accept $target_height = 512

# Direct chained dataflow from host image input to emitted output
$img_in
    >> to_tensor(color="rgb", dtype="f32", layout="hwc", normalize=true)
    >> resize(width=$target_width, height=$target_height, filter="bilinear")
    >> color_adjust(contrast=1.15, saturation=1.05, brightness=0.02)
    >> gaussian_blur(sigma=1.2)
    >> to_image(color="rgba", dtype="u8")
    >> emit
Complete Audio DSP Pipelinemorf
# Morflow Audio DSP Pipeline (.morf)

import audio_essentials/latest

accept $input_audio
accept $sample_rate = 44100

# Direct chained DSP processing with inline multi-channel loop
$input_audio
    >> to_audio(sample_rate=$sample_rate)
    >> resample(to_rate=48000)
    >> normalize(target_peak=0.95)
    >> each ($channel) {
        $channel
            >> biquad_filter(type="highpass", freq=30.0, sample_rate=48000)
            >> compressor(threshold_db=-12.0, ratio=3.0)
    }
    >> stereo_widen(width=1.2)
    >> limiter(ceiling_db=-0.1)
    >> to_wav
    >> emit

Section 3

3. Pipeline Execution

Execute compiled .morf dataflow graphs natively across languages with zero train-serve skew. Morflow provides idiomatic host SDKs for Python, JavaScript/Node.js, Java, and Rust, mapping host tensors, image arrays, and raw binary streams directly into the engine's execution graph.

3.1. Python

The Python SDK (morflow) integrates directly with NumPy arrays, raw bytes, and file streams with zero-copy buffer sharing into the underlying engine.

InstallationShell
pip install morflow
Image & Audio Processing in Pythonpython
import numpy as np
from PIL import Image
import morflow

# 1. Load pipeline from file
pipeline = morflow.load("image_pipeline.morf")

# 2. Pass NumPy arrays directly [H, W, C]
img = Image.open("input.png").convert("RGB")
np_img = np.array(img, dtype=np.uint8)

# 3. Execute pipeline synchronously with native speed
result_np = pipeline.run(np_img)

# 4. Save result
out_img = Image.fromarray(result_np)
out_img.save("output.png")

# ----------------------------------------------------
# Audio Processing (Pass and receive raw WAV bytes)
# ----------------------------------------------------
audio_pipe = morflow.load("audio_pipeline.morf")
with open("input.wav", "rb") as f:
    input_bytes = f.read()

# Single output returns bytes; multi-stream returns dict[str, bytes]
output_wav_bytes = audio_pipe.run(input_bytes)
with open("output.wav", "wb") as f:
    f.write(output_wav_bytes)

3.2. JavaScript / Node.js

The Node.js SDK provides both asynchronous execution (via pipeline.run() to offload work to a background worker pool without blocking the V8 event loop) and synchronous execution (via pipeline.runSync()).

InstallationShell
npm install morflow
Async Pipeline Execution in Node.jsjavascript
import fs from 'node:fs';
import morflow from 'morflow';

// 1. Load pipeline from file
const pipeline = morflow.load('image_pipeline.morf');

// 2. Construct TensorInput with shape and buffer
const inputBuffer = fs.readFileSync('input_raw_rgb.bin');
const tensorInput = {
  data: inputBuffer,
  shape: [1080, 1920, 3],
  dtype: 'u8'
};

// 3. Run asynchronously off the main thread
const outputTensor = await pipeline.run(tensorInput);

console.log(`Shape: ${outputTensor.shape}, Dtype: ${outputTensor.dtype}`);
const outBuffer = outputTensor.toBuffer();
fs.writeFileSync('output_raw_rgba.bin', outBuffer);

// ----------------------------------------------------
// Audio DSP Execution (Direct Buffer I/O)
// ----------------------------------------------------
const audioPipe = morflow.load('audio_pipeline.morf');
const wavBuffer = fs.readFileSync('input.wav');

// Execute asynchronously
const outWav = await audioPipe.run(wavBuffer);
fs.writeFileSync('output.wav', outWav.toBuffer());

3.3. Java

The Java SDK wraps native JNI bindings with type-safe classes, automatic memory cleanup, and direct tensor buffers for high-throughput enterprise JVM backends.

Maven Dependency (pom.xml)XML
<dependency>
    <groupId>org.morflow</groupId>
    <artifactId>morflow</artifactId>
    <version>0.1.0</version>
</dependency>
Pipeline Execution in Javajava
import org.morflow.Morflow;
import org.morflow.Pipeline;
import org.morflow.MorflowTensor;
import java.nio.file.Files;
import java.nio.file.Path;

public class MorflowRunner {
    public static void main(String[] args) throws Exception {
        // 1. Load pipeline
        try (Pipeline pipeline = Morflow.load("image_pipeline.morf")) {
            // 2. Wrap image bytes into MorflowTensor [H, W, C]
            byte[] rgbBytes = Files.readAllBytes(Path.of("input_rgb.raw"));
            MorflowTensor inputTensor = MorflowTensor.fromByteArray(
                rgbBytes,
                new int[]{1080, 1920, 3}
            );

            // 3. Execute pipeline
            try (MorflowTensor outputTensor = pipeline.run(inputTensor)) {
                int[] outShape = outputTensor.getShape();
                byte[] outBytes = outputTensor.toByteArray();
                Files.write(Path.of("output.raw"), outBytes);
            }
        }

        // Audio WAV execution
        Pipeline audioPipe = Morflow.load("audio_pipeline.morf");
        byte[] inputWav = Files.readAllBytes(Path.of("input.wav"));
        MorflowTensor audioOut = audioPipe.run(inputWav);
        Files.write(Path.of("output.wav"), audioOut.toByteArray());
    }
}

3.4. Rust

The native Rust engine crate (morflow) provides direct access to Payload variants (Image, Audio, Tensor, Data) with compile-time type safety and zero runtime overhead.

Cargo DependenciesShell
cargo add morflow
Native Pipeline Execution in Rustrust
use morflow::{ColorSpace, Image, Morflow, Payload, RVec};
use std::fs;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. Load pipeline from file
    let mut pipeline = Morflow::load("image_pipeline.morf")?;

    // 2. Wrap image buffer into a Morflow Image payload
    let img = image::open("input.png")?.to_rgb8();
    let input = Image::from_u8_hwc(
        &img,
        img.width() as usize,
        img.height() as usize,
        ColorSpace::Rgb,
    ).map_err(|e| e.to_string())?;

    // 3. Execute Morflow pipeline
    let outputs = pipeline.run(Payload::Image(input))?;

    // 4. Extract emitted payload and save output
    if let Payload::Image(out) = outputs.into_single()? {
        let bytes = out.to_contiguous_bytes();
        image::save_buffer(
            "output.png",
            bytes.as_slice(),
            out.width() as u32,
            out.height() as u32,
            image::ColorType::Rgba8,
        )?;
    }

    // ----------------------------------------------------
    // Audio DSP Execution (Direct Data Payload)
    // ----------------------------------------------------
    let mut audio_pipe = Morflow::load("audio_pipeline.morf")?;
    let wav_bytes = fs::read("input.wav")?;

    let audio_out = audio_pipe.run(Payload::Data {
        buffer: RVec::from(wav_bytes),
    })?;

    if let Payload::Data { buffer } = audio_out.into_single()? {
        fs::write("output.wav", buffer.as_slice())?;
    }

    Ok(())
}