Caméléon — Language Concepts (v1)

You're reading the v1 language documentation. The v1 is a desktop application built in C++/Qt and is available on Framagit. A full web-based v2 is in development.


Overview

Caméléon is a visual functional programming language based on a formal extension of Petri-net theory. Programs are called compositions — directed graphs of typed operators connected by plugs, executed by the Caméléon Virtual Machine.

The fundamental insight of Caméléon is that execution should be data-driven, not schedule-driven. You don't tell operators when to run. You connect them, and the engine figures out the correct execution order by evaluating the state of each connector at each step.

NEW OLD EMPTY SOURCE Load CSV TRANSFORM Filter Rows COMPUTE Add Values OUTPUT Export JSON DONE RUNNING RUNNABLE WAITING
Fig. CON-1 — data-pipeline.cm : Petri-net graph (each circle is an operator; the smaller circles on arcs are connector places)

Each circle is an operator. The smaller circles on the arcs are connector places holding a data token — their state (NEW, OLD, EMPTY) determines which operator fires next.


Theoretical basis

Caméléon's execution model is formally described in Caméléon language Part 1: Processor (Cugnon de Sévricourt & Tariel, 2011). The model extends classical Petri-net theory to support typed data tokens, hierarchical compositions, and multi-scale workflows.

In standard Petri-net terms: places correspond to connector plugs, transitions correspond to operators, and tokens correspond to data values. The firing condition of a transition maps directly to the executionCondition() of an operator.


Operator

Core primitive — An operator is a pure function with a graphical representation.

It takes typed input data and produces typed output data: y = f(x). An operator is represented as a circle with smaller circles (plugs) around it representing its inputs and outputs.

Every operator in Caméléon has three key properties:

PropertyDescription
keyUnique identifier used by the Factory to instantiate the operator (e.g. NumberAddNumber)
plugIn[]Vector of typed input connectors. Data flows into the operator through these.
plugOut[]Vector of typed output connectors. Produced data is written here and automatically propagated to connected inputs.

// Minimal operator implementation (C++)
void add::exec() {
  double v1 = dynamic_cast<DataNumber*>(plugIn()[0]->getData())->getValue();
  double v2 = dynamic_cast<DataNumber*>(plugIn()[1]->getData())->getValue();
  dynamic_cast<DataNumber*>(plugOut()[0]->getData())->setValue(v1 + v2);
}

Operator states during execution:

StateColorMeaning
NOTRUNNABLEGrayExecution condition not met — waiting for data.
RUNNABLEGreen #00e5a0All conditions met — ready to execute on next tick.
RUNNINGAmber #ffb547Currently executing.
ERRORRed #ff5050Execution failed. Composition halted.

Connector (Plug)

Data state machine — A plug is a typed slot that holds a data value and its freshness state.

Plugs are the mechanism by which operators communicate. An output plug, once written, automatically copies or shares its data to all connected input plugs.

StateColorDescription
EMPTYGray dimNo data available. The plug has never received a value, or was reset after a stop.
NEWGreen #00e5a0Fresh data available. This plug was just written and has not yet been consumed by its operator.
OLDSignal #4f8affData available but already consumed. The value is still accessible, but will not trigger re-execution alone.

Connection rules: you can only connect an output plug to an input plug of the same type or a generic type. One output plug can feed multiple input plugs, but an input plug can only receive from one output plug.


Composition

The program — A composition is a graph of operators and controls — it is the Caméléon program.

Compositions are serialized as .cm XML files. They contain operator definitions, connector states, connection topology, and layout information. In v2, this format will migrate to JSONB stored in PostgreSQL.

Sub-composition (Sub-process)

A set of operators and controls can be merged into a sub-composition, represented as a gray circle. Sub-compositions can be nested to any depth, enabling hierarchical program organization. They expose connectors to their parent scope.

Pattern

A sub-composition saved as a reusable .pa XML file. Patterns appear in the glossary and can be instantiated like any operator. They are the primary mechanism for code reuse in Caméléon.


Control

UI component — A control is an operator with a graphical user interface for viewing or editing data.

Controls can be connected to operator plugs. A number slider connected to an operator input lets you change that input value in real time while the composition runs — this is the core of Caméléon's real-time calibration workflow.

Controls exist in two contexts: inside the Composer (side by side with the graph) or inside the Controller (a separate window that only shows controls, for end-user operation of a composition).


Dictionary — DDK

Extension point — A dictionary is a shared library (.so / .dll) that extends the operator vocabulary.

The Caméléon Dictionary Development Kit (DDK) lets any C++ developer package a set of operators into a dictionary plugin. The factory system loads it dynamically and makes all operators available in the Glossary tree. This is the primary extensibility mechanism of the language.

The built-in SCD (Standard Caméléon Dictionary) includes operators for Number, String, Table, Vector, Map, Image, File, Boolean, DateTime, and HTTP operations.


Connector state transitions

Connector states transition according to strict rules that the CVM enforces:

EventTransition
Operator writes to output plugEMPTY/OLD → NEW (on connected input plugs)
Operator starts executingAll input plugs: NEW/OLD → (no change yet)
Operator finishes executingInput plugs: NEW → OLD · Output plugs: EMPTY → NEW
Composition stoppedAll plugs → EMPTY

Execution condition

The standard execution condition determines when an operator becomes RUNNABLE:


executionCondition() → true
// All inputs must be NEW or OLD
iff ∀ plugIn[i].state ∈ { NEW, OLD }
// At least one input must be NEW
AND ∃ plugIn[i].state == NEW
// All outputs must be EMPTY or OLD
AND ∀ plugOut[j].state ∈ { OLD, EMPTY }

Some operators override executionCondition() to implement different semantics. For example, a Merge operator only requires one of its inputs to be NEW, enabling fan-in patterns.


The CVM

The CVM is implemented in CProcessor. It maintains a FIFO queue of operator IDs waiting for execution, and runs a tight loop:


// Simplified execution loop (from CProcessor.cpp)
void CProcessor::executingLoop() {
  while (getPlayer() == START) {
    int opid = getOperator(); // pop from FIFO
    if (opid == -1) break;   // queue empty
    executeOperator(opid);   // check condition, run, propagate
  }
}

When an operator executes, updateMarkingAfterExecution() propagates the output data to connected input plugs and adds those operators to the execution queue if their condition is now met. This is the cascade that drives the dataflow.


Code ↔ Graph bijection

One of the foundational design goals of Caméléon v2 is a perfect bijection between the visual composition and a textual representation. Every graph has exactly one canonical text form, and every valid text program produces exactly one graph. They are the same program in two different representations — neither is primary.

Design principle — The visual and the textual are two views of a shared AST. Internally, a composition is represented as an Abstract Syntax Tree. The visual editor and the text editor are both renderers of that AST. An edit in one is immediately reflected in the other, with no lossy translation.

NEW OLD EMPTY NODE rag_fetch NODE llm_call NODE validator
Fig. CON-2 — Code ↔ Graph bijection: visual representation of pipeline.cm

This solves a longstanding friction with visual languages: developers distrust tools where the "real" representation is hidden. With bijection, the text is always a first-class citizen — versionable in git, diffable in pull requests, readable by humans and LLMs alike.

Why it matters for AI pipelines

A composition in text form can be passed directly to an LLM as context:

PromptWhat happens
"Explain this pipeline"The LLM reads the text form and describes what each node does
"Add a retry step after the LLM call"LLM outputs modified text → graph updates automatically
"Generate a RAG pipeline with validation"LLM writes the text form from scratch → graph appears
"Find the bottleneck in my composition"LLM reasons over topology and plug types

Text syntax — v2

The v2 text syntax is designed to be explicit about topology. Unlike YAML-based workflow tools, it preserves the full plug-to-plug connection graph rather than abstracting it away.


// A complete Caméléon v2 composition
pipeline RAGPipeline:

  // Each node declares its type, inputs and outputs
  node rag_fetch : RAGFetch
    out.chunks  → llm_call.in.context   // explicit connection

  node llm_call : GPT4Call
    in.context  : DataChunks           // typed input
    out.response → validator.in.raw

  node validator : SchemaCheck
    in.raw      : DataString
    out.valid   → formatter.in.data

  node formatter : FormatJSON
    in.data     : DataString
    out.json                            // terminal output

Syntax rules:

ElementSyntaxDescription
pipelinepipeline Name:Declares a top-level composition
nodenode id : OperatorTypeInstantiates an operator
connectionout.plug → node.in.plugExplicit typed connection between plugs
type annotationin.plug : DataTypeOptional type annotation on inputs
sub-pipelinepipeline Name extends Base:Nested composition (sub-process)

Note — v2 preview: This syntax is under active design and subject to change. The bijection feature is part of the v2 roadmap. The v1 C++/Qt implementation uses XML (.cm files) as its serialization format — the text syntax above is the planned v2 successor.


The Composer

The Composer is the main IDE window — a Qt QGraphicsScene-based canvas where you create and connect operators. It provides:

FeatureDescription
Player BarRun, pause, stop, next-step the execution engine.
Glossary TreeBrowse and search all available operators and patterns by drag & drop.
Project TreeNavigate sub-compositions and interfaces.
Command historyFull undo/redo via the Command pattern (CGCommand/).
Execution errorsErroneous operators are highlighted in red; click to navigate.

The Controller

The Controller is a separate window that shows only Controls — no graph, no operators. It is designed for end-user operation of a composition. An Interface in Caméléon is a composition made exclusively of controls, managed by the Controller window.

The three launch modes map to different combinations of Composer and Controller visibility:

ModeComposerControllerUse case
IDEFull development environment
GUIEnd-user operation only
CLIHeadless / pipeline automation

CLI mode

Caméléon compositions can be executed headless from the command line:


# Launch a composition in CLI mode
./cameleon -mode="CLI" -cm="../project/myPipeline.cm"

# Available modes
./cameleon -mode="IDE"   # Full IDE (default)
./cameleon -mode="GUI"   # Controller only
./cameleon -mode="CLI"   # No UI, pipeline mode

# View help for a specific .cm file
./cameleon -h -cm="../project/myPipeline.cm"

In v2, CLI mode becomes a Docker-native headless execution via cameleon-cli — a lightweight Go binary that accepts a composition JSON and runs it without any UI dependency.


Caméléon v1 (2.1.3-testing) · C++/Qt · MIT License · Source on Framagit