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.
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:
| Property | Description |
|---|---|
key | Unique 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:
| State | Color | Meaning |
|---|---|---|
NOTRUNNABLE | Gray | Execution condition not met — waiting for data. |
RUNNABLE | Green #00e5a0 | All conditions met — ready to execute on next tick. |
RUNNING | Amber #ffb547 | Currently executing. |
ERROR | Red #ff5050 | Execution 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.
| State | Color | Description |
|---|---|---|
EMPTY | Gray dim | No data available. The plug has never received a value, or was reset after a stop. |
NEW | Green #00e5a0 | Fresh data available. This plug was just written and has not yet been consumed by its operator. |
OLD | Signal #4f8aff | Data 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:
| Event | Transition |
|---|---|
| Operator writes to output plug | EMPTY/OLD → NEW (on connected input plugs) |
| Operator starts executing | All input plugs: NEW/OLD → (no change yet) |
| Operator finishes executing | Input plugs: NEW → OLD · Output plugs: EMPTY → NEW |
| Composition stopped | All 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.
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:
| Prompt | What 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:
| Element | Syntax | Description |
|---|---|---|
| pipeline | pipeline Name: | Declares a top-level composition |
| node | node id : OperatorType | Instantiates an operator |
| connection | out.plug → node.in.plug | Explicit typed connection between plugs |
| type annotation | in.plug : DataType | Optional type annotation on inputs |
| sub-pipeline | pipeline 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:
| Feature | Description |
|---|---|
| Player Bar | Run, pause, stop, next-step the execution engine. |
| Glossary Tree | Browse and search all available operators and patterns by drag & drop. |
| Project Tree | Navigate sub-compositions and interfaces. |
| Command history | Full undo/redo via the Command pattern (CGCommand/). |
| Execution errors | Erroneous 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:
| Mode | Composer | Controller | Use case |
|---|---|---|---|
| IDE | ✓ | ✓ | Full development environment |
| GUI | ✗ | ✓ | End-user operation only |
| CLI | ✗ | ✗ | Headless / 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