Introduction
Welcome to Circuit - a node-based runtime engine for building applications with computational blocks written in Rust that runs anywhere.
What is Circuit?
Circuit is a flexible, cross-platform runtime engine that allows you to create applications using a visual node-based architecture. Write your computational blocks in Rust once, and run them on Swift (iOS/macOS), Kotlin (Android), and Web/React platforms.
Key Features
- Universal Blocks: Write computational blocks in Rust once, use everywhere
- Declarative Language: Define blocks and flows using
.blockand.flowfiles - Cross-Platform: Run on iOS, macOS, Android, and Web (via WebAssembly)
- Type-Safe: Strong typing with Rust's safety guarantees
- Visual Flow: Node-based execution graphs for clear data flow
- Extensible: Easy to add custom blocks for your use cases
- Zero-Copy FFI: Efficient cross-language communication
- Cycle Detection: Automatic validation of graph structures
- Topological Execution: Optimized execution order
- Comprehensive Testing: Unit, integration, and WASM tests included
Why Circuit?
Traditional cross-platform development often requires writing the same logic multiple times for different platforms. Circuit solves this by:
- Write Once, Run Anywhere: Write your computational blocks in Rust and automatically use them across all platforms
- Visual Programming: Define complex data flows using an intuitive node-based system
- Type Safety: Leverage Rust's type system for runtime safety across all platforms
- Performance: Compiled Rust code ensures optimal performance on every platform
- Maintainability: Update logic in one place and deploy everywhere
Use Cases
Circuit is perfect for:
- Data Processing Pipelines: Transform and process data through reusable blocks
- Business Logic: Share complex business rules across mobile and web
- Machine Learning: Build ML inference pipelines that work everywhere
- Game Logic: Create game mechanics once, deploy to all platforms
- Computational Apps: Any application with complex computational requirements
Quick Example
Here's a simple calculator flow that demonstrates Circuit's declarative syntax:
flow calculator {
description "Simple calculator: (5 + 3) * 2 = 16"
node const5: core.constant { value = 5 }
node const3: core.constant { value = 3 }
node add: math.add
node multiply: math.multiply
connect const5.value -> add.a
connect const3.value -> add.b
connect add.result -> multiply.a
}
This flow creates a calculation graph that:
- Creates two constant values (5 and 3)
- Adds them together
- Multiplies the result by another value
Next Steps
- New to Circuit? Start with the Quick Start guide
- Want to understand the architecture? Read the Architecture Overview
- Ready to build? Jump to Creating Custom Blocks
- Integrating with a platform? Check out the Platform Integration guides
Community and Support
Circuit is actively developed and welcomes contributions. If you encounter issues or have questions:
- GitHub Issues: Report bugs or request features
- Documentation: This comprehensive guide covers all aspects of Circuit
- Examples: Check the
examples/directory for working code samples
Let's get started building with Circuit!
Quick Start
Get up and running with Circuit in minutes!
Prerequisites
Before you begin, ensure you have:
- Rust installed (1.70 or later)
- Basic familiarity with Rust syntax
- A text editor or IDE
Installation
1. Install Rust
If you haven't already, install Rust:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
2. Add Circuit to Your Project
Add Circuit to your Cargo.toml:
[dependencies]
circuit-core = { path = "../circuit-core" }
circuit-lang = { path = "../circuit-lang" }
Or clone the repository:
git clone https://github.com/blankly-app/circuit.git
cd circuit
Your First Program
Let's create a simple calculator using Circuit's Rust API:
use circuit_core::{blocks::*, *}; use std::sync::Arc; fn main() -> Result<()> { // Create the engine let mut engine = Engine::new(); // Register built-in blocks engine.register_block(Arc::new(AddBlock))?; engine.register_block(Arc::new(MultiplyBlock))?; engine.register_block(Arc::new(ConstantBlock))?; // Create a graph let mut graph = Graph::new( "calculator".to_string(), "Simple Calculator".to_string() ); // Add nodes let const5 = graph.add_node(Node::new( "const5".to_string(), "core.constant".to_string(), vec![("value".to_string(), Value::Int(5))].into_iter().collect(), )); let const3 = graph.add_node(Node::new( "const3".to_string(), "core.constant".to_string(), vec![("value".to_string(), Value::Int(3))].into_iter().collect(), )); let add = graph.add_node(Node::new( "add".to_string(), "math.add".to_string(), HashMap::new(), )); // Connect nodes graph.connect(&const5, "value", &add, "a")?; graph.connect(&const3, "value", &add, "b")?; // Load and execute engine.load_graph(graph)?; let results = engine.execute_graph("calculator")?; println!("Result: {:?}", results); Ok(()) }
Run it:
cargo run
Using the Declarative Language
Circuit's declarative language makes it even easier. Create a file calculator.flow:
flow calculator {
description "Simple calculator: (5 + 3) * 2 = 16"
node const5: core.constant { value = 5 }
node const3: core.constant { value = 3 }
node const2: core.constant { value = 2 }
node add: math.add
node multiply: math.multiply
connect const5.value -> add.a
connect const3.value -> add.b
connect add.result -> multiply.a
connect const2.value -> multiply.b
}
Load and execute it:
use circuit_core::*; use circuit_lang::{parse_flow, flow_to_graph}; use std::fs; fn main() -> Result<()> { let mut engine = Engine::new(); // Register blocks (as before) // ... // Load flow file let source = fs::read_to_string("calculator.flow")?; let flow = parse_flow(&source)?; let graph = flow_to_graph(&flow)?; engine.load_graph(graph)?; let results = engine.execute_graph("calculator")?; println!("Calculator result: {:?}", results); Ok(()) }
Running the Examples
Circuit comes with several example flows in the examples/ directory:
# Run the calculator example
cargo run --example calculator
# Run tests to see all examples in action
cargo test -p circuit-lang --test integration_tests
Next Steps
Now that you've seen the basics, explore:
- Your First Flow - A detailed walkthrough of creating flows
- Architecture Overview - Understand how Circuit works
- Understanding Blocks - Deep dive into blocks
- The Declarative Language - Complete language reference
Common Issues
Missing Blocks
If you see "Block not found" errors, make sure you've registered all the blocks you're using:
#![allow(unused)] fn main() { engine.register_block(Arc::new(AddBlock))?; engine.register_block(Arc::new(MultiplyBlock))?; // etc. }
Connection Errors
Port names must match exactly. Check your block definitions for the correct input/output port names.
Parse Errors
When using .flow files, ensure:
- Block types are registered before loading
- Syntax matches the language specification
- All connections reference valid ports
Installation
This guide covers installing Circuit for different platforms and use cases.
Rust Development
Prerequisites
- Rust 1.70 or later
- Cargo (comes with Rust)
Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
Verify installation:
rustc --version
cargo --version
Using mise (optional/recommended)
If you prefer to use mise for developer tool/version management, you can use
the repo-provided mise.toml and the rust-toolchain file to automatically
install and pin the correct Rust version and components.
# Install mise (only if you don't have it)
curl https://mise.run | sh
# Activate mise in your shell (zsh example)
eval "$(~/.local/bin/mise activate zsh)"
# From the repository root, activate the Rust toolchain and install configured tools
cd circuit
mise use rust@1.90.0
mise install
# Verify cargo/rust version (runs inside mise environment)
mise x -- cargo --version
Note: .rust-toolchain.toml is also included and will ensure rustup uses the
right channel and components if you're using rustup directly.
Clone Circuit
git clone https://github.com/blankly-app/circuit.git
cd circuit
Build All Packages
# Build all packages in release mode
cargo build --release
# Run tests to verify everything works
cargo test
# Run an example
cargo run --example calculator
Platform-Specific Installation
iOS/macOS (Swift)
Install Additional Targets
# For iOS devices (ARM64)
rustup target add aarch64-apple-ios
# For iOS Simulator (x86_64)
rustup target add x86_64-apple-ios
# For iOS Simulator (ARM64, M1/M2 Macs)
rustup target add aarch64-apple-ios-sim
# For macOS
rustup target add aarch64-apple-darwin # Apple Silicon
rustup target add x86_64-apple-darwin # Intel Macs
Build for iOS
# Build the FFI library for iOS
cargo build --release --target aarch64-apple-ios -p circuit-ffi
# The library will be at: target/aarch64-apple-ios/release/libcircuit_ffi.a
See the Swift Integration Guide for detailed Xcode setup.
Android (Kotlin)
Install NDK and Targets
-
Install Android NDK via Android Studio or standalone
-
Add Rust targets:
rustup target add aarch64-linux-android # ARM64
rustup target add armv7-linux-androideabi # ARMv7
rustup target add i686-linux-android # x86
rustup target add x86_64-linux-android # x86_64
Configure Cargo for Android
Create or edit ~/.cargo/config.toml:
[target.aarch64-linux-android]
ar = "/path/to/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar"
linker = "/path/to/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang"
[target.armv7-linux-androideabi]
ar = "/path/to/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar"
linker = "/path/to/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi30-clang"
Replace /path/to/ndk with your NDK installation path.
Build for Android
# Build for ARM64 (most common)
cargo build --release --target aarch64-linux-android -p circuit-ffi
# The library will be at: target/aarch64-linux-android/release/libcircuit_ffi.so
See the Kotlin Integration Guide for Android Studio setup.
Web/React (WebAssembly)
Install wasm-pack
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
Or with cargo:
cargo install wasm-pack
Build WASM Package
cd circuit-wasm
wasm-pack build --target web --release
# Or for bundlers (webpack, vite, etc.)
wasm-pack build --target bundler --release
This creates a pkg/ directory with:
circuit_wasm_bg.wasm- The WebAssembly modulecircuit_wasm.js- JavaScript bindingscircuit_wasm.d.ts- TypeScript definitionspackage.json- NPM package manifest
Install in Your Web Project
cd your-web-project
npm install ../circuit/circuit-wasm/pkg
Or publish to NPM and install normally.
See the React Integration Guide for detailed web setup.
Development Tools
Optional: Install cargo-watch for Auto-rebuild
cargo install cargo-watch
Use it during development:
cargo watch -x test
cargo watch -x 'run --example calculator'
Optional: Install cargo-expand for Macro Debugging
cargo install cargo-expand
Useful for inspecting generated code:
cargo expand -p circuit-core
Optional: Install mdBook for Documentation
cargo install mdbook
Build and serve the documentation locally:
cd docs
mdbook serve --open
Verification
Run All Tests
cargo test --all
Build All Targets
# Native
cargo build --release
# iOS
cargo build --release --target aarch64-apple-ios -p circuit-ffi
# Android
cargo build --release --target aarch64-linux-android -p circuit-ffi
# WASM
cd circuit-wasm && wasm-pack build --target web
Run Examples
cargo run --example calculator
Troubleshooting
Linker Errors on macOS
If you encounter linker errors, ensure you have Xcode command line tools:
xcode-select --install
Android NDK Not Found
Ensure ANDROID_NDK_HOME is set:
export ANDROID_NDK_HOME=/path/to/android-ndk
Add to your .bashrc or .zshrc for persistence.
WASM Build Fails
Ensure you have the wasm32 target:
rustup target add wasm32-unknown-unknown
And install wasm-bindgen-cli:
cargo install wasm-bindgen-cli
Next Steps
- Quick Start - Create your first Circuit application
- Architecture Overview - Understand how Circuit works
- Platform Integration - Integrate with your platform
Your First Flow
This tutorial will walk you through creating your first Circuit flow from scratch.
What You'll Build
We'll create a temperature converter that converts Celsius to Fahrenheit using the formula:
F = (C × 9/5) + 32
Step 1: Create the Flow File
Create a new file called temp_converter.flow:
flow temp_converter {
description "Converts Celsius to Fahrenheit"
// Input temperature in Celsius
node celsius: core.constant {
value = 25
}
// Constants for the formula
node nine: core.constant {
value = 9
}
node five: core.constant {
value = 5
}
node thirtytwo: core.constant {
value = 32
}
// Calculations: (C × 9 / 5) + 32
node multiply: math.multiply
node divide: math.multiply // We'll use multiply with 1/5 = 0.2
node add: math.add
// Connect the graph
connect celsius.value -> multiply.a
connect nine.value -> multiply.b
connect multiply.result -> divide.a
connect five.value -> divide.b
connect divide.result -> add.a
connect thirtytwo.value -> add.b
// Output the final result
output add.result
}
Step 2: Understanding the Flow
Let's break down what this flow does:
-
Constants: We define constant values for:
celsius(25) - the input temperaturenine(9) - part of the conversion formulafive(5) - part of the conversion formulathirtytwo(32) - the offset in the formula
-
Operations: We create three math operation nodes:
multiply- multiplies Celsius by 9divide- divides by 5add- adds 32
-
Connections: We wire the nodes together to form the calculation pipeline
-
Output: We expose the final result from the
addnode
Step 3: Load and Execute
Create a Rust program to execute the flow:
use circuit_core::*; use circuit_core::blocks::*; use circuit_lang::{parse_flow, flow_to_graph}; use std::sync::Arc; use std::fs; fn main() -> Result<()> { // Create engine and register blocks let mut engine = Engine::new(); engine.register_block(Arc::new(AddBlock))?; engine.register_block(Arc::new(MultiplyBlock))?; engine.register_block(Arc::new(ConstantBlock))?; // Load the flow file let source = fs::read_to_string("temp_converter.flow")?; let flow = parse_flow(&source)?; let graph = flow_to_graph(&flow)?; // Execute let graph_id = graph.id.clone(); engine.load_graph(graph)?; let results = engine.execute_graph(&graph_id)?; // Print results for (node_id, outputs) in results { println!("Node {}: {:?}", node_id, outputs); } Ok(()) }
Step 4: Run It
cargo run
You should see output showing the intermediate calculations and the final result (25°C = 77°F).
Step 5: Make It Interactive
Now let's make it easier to change the input. Modify your Rust program:
use std::io::{self, Write}; fn main() -> Result<()> { // ... (setup code as before) // Get temperature from user print!("Enter temperature in Celsius: "); io::stdout().flush()?; let mut input = String::new(); io::stdin().read_line(&mut input)?; let celsius: f64 = input.trim().parse() .map_err(|_| CircuitError::InvalidInput("Invalid number".to_string()))?; // Modify the constant node's configuration // (In a real app, you'd modify the graph before loading) // ... (execution code) Ok(()) }
Next Steps
Congratulations! You've created your first Circuit flow. Here's what to explore next:
Create Custom Blocks
Instead of using just constants and math operations, create custom blocks:
block conversion.celsius_to_fahrenheit {
description "Converts Celsius to Fahrenheit"
input celsius: Number {
description "Temperature in Celsius"
}
output fahrenheit: Number {
description "Temperature in Fahrenheit"
}
execute {
fahrenheit = (celsius * 9 / 5) + 32
}
}
Add More Features
Extend your temperature converter:
- Support both directions (C→F and F→C)
- Add Kelvin conversion
- Add input validation
- Create a visual display
Learn More About Flows
- Creating Flows - Detailed flow guide
- The Declarative Language - Complete syntax reference
- Built-in Blocks - Available blocks
Try the Examples
Check out the example flows in examples/flows/:
cargo test -p circuit-lang --test integration_tests
Troubleshooting
"Block not found" error
Make sure all blocks used in your flow are registered:
#![allow(unused)] fn main() { engine.register_block(Arc::new(AddBlock))?; engine.register_block(Arc::new(MultiplyBlock))?; engine.register_block(Arc::new(ConstantBlock))?; }
Connection errors
Verify port names match exactly. Use:
.valuefor constant block outputs.aand.bfor math block inputs.resultfor math block outputs
Parse errors
Check your .flow syntax:
- Node definitions need
node id: type { config } - Connections need
connect from.port -> to.port - All statements should be inside the
flow { }block
Architecture Overview
Understanding Circuit's architecture will help you build better applications and debug issues more effectively.
High-Level Architecture
Circuit consists of four main layers:
┌─────────────────────────────────────────────────────┐
│ Platform Layer (Swift/Kotlin/JS) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ iOS │ │ Android │ │ Web │ │
│ └──────────────┘ └──────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────┐
│ FFI/WASM Bindings Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ circuit-ffi │ │ circuit-ffi │ │circuit- │ │
│ │ (Swift) │ │ (Kotlin) │ │ wasm │ │
│ └──────────────┘ └──────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────┐
│ Language Layer │
│ ┌──────────────┐ │
│ │ circuit-lang │ │
│ │ Parser & │ │
│ │ Converter │ │
│ └──────────────┘ │
└─────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────┐
│ Core Runtime Engine │
│ ┌──────────────┐ │
│ │ circuit-core │ │
│ │ Engine, │ │
│ │ Blocks, │ │
│ │ Graphs │ │
│ └──────────────┘ │
└─────────────────────────────────────────────────────┘
Core Components
1. circuit-core: The Runtime Engine
The heart of Circuit, circuit-core provides:
Blocks
The fundamental unit of computation. Each block:
- Has inputs (data it receives)
- Has outputs (data it produces)
- Has configuration (parameters that customize behavior)
- Implements an execute method (the actual computation)
#![allow(unused)] fn main() { pub trait Block: Send + Sync { fn metadata(&self) -> BlockMetadata; fn execute(&self, context: BlockContext) -> Result<HashMap<String, Value>>; fn validate(&self, _config: &HashMap<String, Value>) -> Result<()> { Ok(()) } } }
Graphs
A directed acyclic graph (DAG) representing data flow:
- Nodes: Instances of blocks with unique IDs
- Connections: Data paths between node ports
- Cycle Detection: Ensures no circular dependencies
- Validation: Checks that all connections are valid
Engine
The execution runtime that:
- Registers blocks (makes them available)
- Loads graphs (prepares them for execution)
- Executes graphs in topological order
- Routes data between connected nodes
- Returns final output values
Values
Type-safe data containers supporting:
Null,Bool,Int,Float,StringArray,Object(nested structures)Bytes(binary data)- Conversion and serialization via serde
2. circuit-lang: The Declarative Language
Provides a human-friendly way to define blocks and flows:
Parser
Uses Pest grammar to parse .block and .flow files into AST (Abstract Syntax Tree).
Converter
Transforms AST into executable Graph objects that the engine can run.
Benefits
- Declarative: Describe what you want, not how to build it
- Readable: Clear syntax for non-programmers
- Maintainable: Easy to modify flows without recompiling
3. circuit-ffi: Native Platform Bridge
C-compatible FFI layer for iOS and Android:
- C API: Simple functions for create, load, execute, destroy
- Memory Management: Safe handling of strings and pointers
- Error Handling: Proper error propagation across FFI boundary
- Thread Safety: Uses lazy_static for global engine registry
Swift and Kotlin wrappers provide idiomatic APIs on top of the C layer.
4. circuit-wasm: Web Assembly Bridge
WebAssembly bindings for browser and Node.js:
- wasm-bindgen: Automatic JavaScript bindings
- Zero-Copy: Efficient data transfer between JS and Rust
- TypeScript: Full type definitions included
- Promise-based: Async/await friendly API
Data Flow
Here's how data flows through a Circuit application:
1. Define Flow (.flow file or Rust code)
│
├─> Defines nodes (block instances)
├─> Defines connections (data paths)
└─> Defines configuration (block parameters)
2. Parse/Convert (circuit-lang)
│
└─> Converts to Graph object
3. Load Graph (Engine)
│
├─> Validates graph structure
├─> Checks for cycles
└─> Computes topological order
4. Execute Graph (Engine)
│
├─> Executes nodes in topological order
├─> Routes data through connections
└─> Collects final outputs
5. Return Results
│
└─> HashMap<String, HashMap<String, Value>>
Topological Execution
Circuit executes nodes in topological order, ensuring:
- A node only executes after all its dependencies
- Data flows in the correct direction
- No deadlocks or circular dependencies
Example
For this flow:
flow example {
node a: core.constant { value = 1 }
node b: core.constant { value = 2 }
node c: math.add
node d: math.multiply
connect a.value -> c.a
connect b.value -> c.b
connect c.result -> d.a
}
Execution order:
aandbexecute first (no dependencies)cexecutes next (depends onaandb)dexecutes last (depends onc)
Memory Model
Rust Side
- Blocks:
Arc<dyn Block>- Thread-safe, shared references - Graphs: Owned by Engine, stored in HashMap
- Values: Cloned when passed between nodes (cheap for small values)
FFI Boundary
- Strings: Converted between Rust String and C char*
- Pointers: Engine handles stored in global registry
- Memory Safety: Rust manages all allocations
WASM Boundary
- Values: Serialized as JSON across boundary
- Optimization: Uses wasm-bindgen for efficient marshaling
- Memory: WASM linear memory managed by Rust
Error Handling
Circuit uses Rust's Result type throughout:
#![allow(unused)] fn main() { pub type Result<T> = std::result::Result<T, CircuitError>; pub enum CircuitError { BlockNotFound(String), GraphNotFound(String), InvalidInput(String), InvalidConnection(String), CycleDetected, ExecutionError(String), ParseError(String), } }
Errors propagate through the stack and can be caught at the platform layer.
Performance Characteristics
- Graph Loading: O(N + E) where N = nodes, E = edges
- Topological Sort: O(N + E)
- Execution: O(N × B) where B = average block execution time
- Data Flow: Cloning overhead for Values (typically small)
Optimization Opportunities
- Lazy Evaluation: Only execute nodes needed for requested outputs
- Parallelization: Execute independent nodes concurrently
- Caching: Memoize block results for repeated inputs
- Streaming: Support streaming data for large datasets
These are future roadmap items.
Thread Safety
- Engine: Not thread-safe; use one per thread or protect with Mutex
- Blocks: Must be
Send + Sync(thread-safe) - Graphs: Immutable after loading (safe to share)
- Values: Cloned between nodes (no shared mutation)
Next Steps
- Understanding Blocks - Deep dive into blocks
- The Graph Engine - Engine internals
- Creating Custom Blocks - Build your own blocks
Understanding Blocks
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Understanding Blocks in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Creating Flows
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Creating Flows in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
The Declarative Language
Circuit provides a declarative language for defining computational blocks and flow graphs through .block and .flow files.
Overview
The Circuit Language consists of two main file types:
.blockfiles: Define reusable computational blocks (similar to function definitions).flowfiles: Define complete graphs/workflows that connect blocks together (similar to programs)
Both file types use a simple, readable syntax designed to be easy to write and maintain.
Why Use the Declarative Language?
While you can create blocks and graphs programmatically in Rust, the declarative language offers several advantages:
- Readability: Clear, self-documenting syntax
- Accessibility: Non-programmers can create and modify flows
- Rapid Prototyping: Quickly iterate on designs without recompiling
- Visual Mapping: Easy to visualize from the textual representation
- Separation of Concerns: Logic (blocks) separate from composition (flows)
File Structure
Block Files
block <qualified.name> {
description "Human-readable description"
input <name>: <Type> { ... }
output <name>: <Type> { ... }
config <name>: <Type> { ... }
execute { ... }
}
Flow Files
flow <name> {
description "Human-readable description"
node <id>: <block.type> { ... }
connect <from>.<port> -> <to>.<port>
output <node>.<port>
}
Syntax Rules
- Comments: Use
//for single-line comments - Whitespace: Whitespace is flexible and ignored
- Identifiers: Must start with a letter, can contain letters, numbers, and underscores
- Qualified names: Use dots to create namespaces (e.g.,
math.advanced.fft) - Case sensitivity: All identifiers are case-sensitive
Best Practices
- Use descriptive names: Choose clear, self-documenting names for blocks, nodes, and ports
- Add descriptions: Always include descriptions for blocks and ports
- Provide defaults: Set sensible default values for optional inputs and config parameters
- Organize blocks: Use qualified names to organize blocks into namespaces (e.g.,
math.,string.,io.) - Position nodes: Use position statements in flows for better visualization
- Comment your flows: Use the description field to explain what your flow does
Next Steps
- Block Syntax - Detailed block file syntax
- Flow Syntax - Detailed flow file syntax
- Type System - Data types and values
- Creating Custom Blocks - Hands-on guide
Examples
Check out the example files in the repository:
examples/blocks/- Example block definitionsexamples/flows/- Example flow definitions
Run the integration tests to see them in action:
cargo test -p circuit-lang --test integration_tests
Block Syntax
Block files define reusable computational units with inputs, outputs, configuration parameters, and execution logic.
Basic Structure
block <qualified.name> {
description "Human-readable description"
input <name>: <Type> {
description "Input description"
default = <value>
}
output <name>: <Type> {
description "Output description"
}
config <name>: <Type> {
description "Config parameter description"
default = <value>
}
execute {
// Execution statements
}
}
Block Name
The block name must be a qualified identifier using dot notation:
block math.add { ... }
block string.format { ... }
block myapp.custom.processor { ... }
Best practices:
- Use namespaces to organize related blocks
- Keep names descriptive but concise
- Use lowercase with dots for namespacing
Description
Optional but highly recommended:
block math.square {
description "Squares a number (x²)"
...
}
Inputs
Define input ports that receive data:
input x: Number {
description "The number to square"
}
input optional_value: Number {
description "An optional input"
default = 0
}
Properties:
name: Port identifier (required)type: Data type (required)description: Human-readable description (optional)default: Default value if not connected (optional)
Outputs
Define output ports that produce data:
output result: Number {
description "The squared result"
}
Properties:
name: Port identifier (required)type: Data type (required)description: Human-readable description (optional)
Config Parameters
Configuration values set at design-time:
config multiplier: Number {
description "Multiplication factor"
default = 1
}
Difference from inputs:
- Config values are static (set when creating the node)
- Inputs are dynamic (data flowing through the graph)
Execute Block
Contains the computation logic:
execute {
result = x * x
}
Supported Operations
Assignment:
output = input
result = calculation
Arithmetic:
result = a + b
result = a - b
result = a * b
result = a / b
Comparisons:
equal = a == b
not_equal = a != b
greater = a > b
less = a < b
Logical:
and_result = a && b
or_result = a || b
not_result = !value
Conditionals:
if condition {
output = value1
}
Function Calls:
result = pow(base, exponent)
result = sqrt(value)
Member Access:
result = object.property
result = object.method()
Complete Examples
Example 1: Simple Math Block
block math.square {
description "Squares a number (x²)"
input x: Number {
description "The number to square"
}
output result: Number {
description "The squared result"
}
execute {
result = x * x
}
}
Example 2: Block with Config
block math.power {
description "Raises a base to an exponent"
input base: Number {
description "The base number"
}
config exponent: Number {
description "The exponent"
default = 2
}
output result: Number {
description "The result of base^exponent"
}
execute {
result = base * base
}
}
Example 3: String Processing
block string.format {
description "Formats a string with prefix and suffix"
input template: String {
description "The template string"
}
config prefix: String {
description "Prefix to add"
default = ""
}
config suffix: String {
description "Suffix to add"
default = ""
}
output result: String {
description "The formatted string"
}
execute {
result = prefix + template + suffix
}
}
Example 4: Multiple Outputs
block logic.compare {
description "Compares two numbers"
input a: Number
input b: Number
output equal: Bool
output greater: Bool
output less: Bool
execute {
equal = a == b
greater = a > b
less = a < b
}
}
Loading Block Definitions
Use the circuit-lang crate to parse block files:
#![allow(unused)] fn main() { use circuit_lang::parse_block; let source = std::fs::read_to_string("math.square.block")?; let block = parse_block(&source)?; println!("Block: {}", block.name); println!("Inputs: {}", block.inputs.len()); println!("Outputs: {}", block.outputs.len()); }
See Also
- Flow Syntax - How to use blocks in flows
- Type System - Available data types
- Creating Custom Blocks - Practical guide
Flow Syntax
Flow files define complete computational graphs by instantiating blocks and connecting them together.
Basic Structure
flow <name> {
description "Human-readable description"
node <id>: <block.type> {
<config_param> = <value>
position(<x>, <y>)
}
connect <from_node>.<from_port> -> <to_node>.<to_port>
output <node>.<port>
}
Flow Name
Simple identifier (not qualified like blocks):
flow calculator { ... }
flow data_pipeline { ... }
flow my_workflow { ... }
Description
Optional but recommended:
flow calculator {
description "Simple calculator: (5 + 3) * 2 = 16"
...
}
Node Definitions
Each node is an instance of a block:
node <id>: <block.type> {
<config_param> = <value>
position(<x>, <y>)
}
Example:
node const5: core.constant {
value = 5
position(100, 100)
}
node add: math.add {
position(250, 150)
}
Properties:
id: Unique identifier for this node instanceblock.type: The qualified name of the block to instantiate- Configuration values are set inline
position(x, y)is optional (for visual layout)
Connections
Define data flow between nodes:
connect <from_node>.<from_port> -> <to_node>.<to_port>
Examples:
connect const5.value -> add.a
connect add.result -> multiply.a
connect input.data -> processor.input
Rules:
- Port names must match the block's input/output definitions
- Connections create directed edges in the graph
- A single output can connect to multiple inputs
- An input can only receive one connection
Output Declarations
Specify which node outputs are exposed as flow outputs:
output <node>.<port>
Example:
output multiply.result
output final_node.computed_value
Complete Examples
Example 1: Simple Calculator
flow calculator {
description "Simple calculator: (5 + 3) * 2 = 16"
node const5: core.constant {
value = 5
position(100, 100)
}
node const3: core.constant {
value = 3
position(100, 200)
}
node const2: core.constant {
value = 2
position(400, 150)
}
node add: math.add {
position(250, 150)
}
node multiply: math.multiply {
position(550, 150)
}
connect const5.value -> add.a
connect const3.value -> add.b
connect add.result -> multiply.a
connect const2.value -> multiply.b
output multiply.result
}
Example 2: Data Pipeline
flow data_pipeline {
description "Process data through multiple stages"
node input: core.constant {
value = 10
}
node stage1: math.multiply {
position(200, 100)
}
node stage2: math.add {
position(400, 100)
}
node stage3: core.debug {
position(600, 100)
}
connect input.value -> stage1.a
connect stage1.result -> stage2.a
connect stage2.result -> stage3.value
output stage3.value
}
Example 3: String Processing
flow string_processing {
description "Concatenates strings together"
node hello: core.constant {
value = "Hello"
}
node space: core.constant {
value = " "
}
node world: core.constant {
value = "World"
}
node concat1: string.concat
node concat2: string.concat
connect hello.value -> concat1.a
connect space.value -> concat1.b
connect concat1.result -> concat2.a
connect world.value -> concat2.b
output concat2.result
}
Loading and Executing Flows
Use the circuit-lang crate:
#![allow(unused)] fn main() { use circuit_core::*; use circuit_lang::{parse_flow, flow_to_graph}; use std::fs; // Parse flow file let source = fs::read_to_string("calculator.flow")?; let flow = parse_flow(&source)?; // Convert to executable graph let graph = flow_to_graph(&flow)?; // Execute with engine let graph_id = graph.id.clone(); engine.load_graph(graph)?; let results = engine.execute_graph(&graph_id)?; }
Graph Validation
Circuit automatically validates flows:
- Cycle Detection: Ensures no circular dependencies
- Port Validation: Checks that connected ports exist
- Type Checking: Validates data type compatibility (future)
Position Coordinates
The position(x, y) directive is optional and used for visual graph editors:
node add: math.add {
position(250, 150)
}
- Coordinates are arbitrary (typically pixels)
- Not required for execution
- Helpful for visual tools and documentation
Common Patterns
Fan-out (One output → Many inputs)
node source: core.constant { value = 5 }
node consumer1: math.add
node consumer2: math.multiply
connect source.value -> consumer1.a
connect source.value -> consumer2.a
Sequential Processing
node step1: processor.first
node step2: processor.second
node step3: processor.third
connect step1.output -> step2.input
connect step2.output -> step3.input
Parallel Processing
node input: core.constant { value = 10 }
node pathA: math.add
node pathB: math.multiply
connect input.value -> pathA.a
connect input.value -> pathB.a
See Also
- Block Syntax - Define blocks to use in flows
- Examples - Real-world flow examples
- Creating Flows - Practical guide
Type System
Circuit supports a rich type system for data flowing through graphs.
Supported Types
| Type | Description | Example Values |
|---|---|---|
Number | Floating-point numbers | 42, 3.14, -10.5 |
String | Text strings | "hello", "world" |
Bool | Boolean values | true, false |
Array | Ordered lists | [1, 2, 3], ["a", "b"] |
Object | Key-value maps | {"key": "value"} |
Bytes | Binary data | Raw byte arrays |
Any | Any type | Any of the above |
Null | Null value | null |
Value Literals
Null
null
Booleans
true
false
Numbers
Circuit uses 64-bit floating-point numbers internally:
42
3.14159
-10.5
0.0
1e6 // Scientific notation
Strings
Double-quoted text:
"hello world"
"multi word string"
"with \"escaped\" quotes"
"line 1\nline 2" // Escape sequences
Arrays
Ordered collections of values:
[1, 2, 3]
["a", "b", "c"]
[true, false, true]
[] // Empty array
Arrays can contain mixed types:
[1, "two", true, null]
Objects
Key-value pairs (like JSON objects):
{"name": "Alice", "age": 30}
{"x": 10, "y": 20}
{} // Empty object
Nested Structures
Arrays and objects can be nested:
{
"user": "bob",
"scores": [10, 20, 30],
"metadata": {
"active": true,
"level": 5
}
}
Type Annotations
In block and flow definitions, specify types:
input count: Number
input name: String
input flag: Bool
input items: Array
input data: Object
input anything: Any
Rust Value Type
In Rust code, all values use the Value enum:
#![allow(unused)] fn main() { pub enum Value { Null, Bool(bool), Int(i64), Float(f64), String(String), Array(Vec<Value>), Object(HashMap<String, Value>), Bytes(Vec<u8>), } }
Creating Values
#![allow(unused)] fn main() { use circuit_core::Value; let num = Value::Int(42); let text = Value::String("hello".to_string()); let flag = Value::Bool(true); let list = Value::Array(vec![ Value::Int(1), Value::Int(2), Value::Int(3), ]); let obj = Value::Object([ ("key".to_string(), Value::String("value".to_string())), ].iter().cloned().collect()); }
Extracting Values
#![allow(unused)] fn main() { // Safe extraction with Option if let Some(num) = value.as_int() { println!("Integer: {}", num); } if let Some(text) = value.as_str() { println!("String: {}", text); } // Check type if value.is_null() { println!("Value is null"); } }
Type Conversion
#![allow(unused)] fn main() { // Convert to JSON let json = serde_json::to_string(&value)?; // Parse from JSON let value: Value = serde_json::from_str(&json)?; }
Type Checking (Future)
Currently, Circuit performs minimal type checking. Future versions will include:
- Compile-time type validation: Catch type errors before execution
- Type inference: Automatically infer types from connections
- Custom types: Define your own data structures
- Generics: Blocks that work with multiple types
Best Practices
1. Use Specific Types
Prefer specific types over Any:
// Good
input count: Number
// Less good
input count: Any
2. Document Expected Formats
For Object types, document the expected structure:
input config: Object {
description "Config object with fields: timeout (Number), retries (Number)"
}
3. Validate in Execute
Add validation in your block's execute method:
#![allow(unused)] fn main() { fn execute(&self, context: BlockContext) -> Result<HashMap<String, Value>> { let count = context.get_input("count") .and_then(|v| v.as_int()) .ok_or_else(|| CircuitError::InvalidInput("count must be a number".into()))?; if count < 0 { return Err(CircuitError::InvalidInput("count must be positive".into())); } // ... rest of logic } }
4. Handle Null Values
Check for null before processing:
#![allow(unused)] fn main() { match context.get_input("optional_value") { Some(Value::Null) | None => { // Use default } Some(value) => { // Process value } } }
Type Coercion
Circuit performs automatic type coercion in some cases:
Int→Float: AutomaticFloat→Int: Truncates (may lose precision)Bool→Number:true= 1,false= 0- Any type →
String: Viato_string()
Example:
#![allow(unused)] fn main() { // These are equivalent internally Value::Int(42) == Value::Float(42.0) }
JSON Compatibility
All Circuit values are JSON-compatible:
#![allow(unused)] fn main() { use serde_json; let value = Value::Object(/* ... */); // Serialize let json = serde_json::to_string(&value)?; // Deserialize let value: Value = serde_json::from_str(&json)?; }
This makes it easy to:
- Store graphs and values
- Transfer data across platforms
- Integrate with web APIs
Platform-Specific Types
Swift
// Circuit values map to Swift types
Int64 -> Int
Double -> Double
String -> String
Array -> [Any]
Dictionary -> [String: Any]
Kotlin
// Circuit values map to Kotlin types
Int64 -> Long
Double -> Double
String -> String
Array -> List<Any>
Object -> Map<String, Any>
JavaScript/TypeScript
// Circuit values map to JS types
Int64 -> number
Float64 -> number
String -> string
Array -> Array<any>
Object -> Record<string, any>
Bool -> boolean
Null -> null
See Also
- Values and Types Guide - Practical usage
- Block Syntax - Using types in blocks
- API Reference - Complete Value API
Built-in Blocks
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Built-in Blocks in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Creating Custom Blocks
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Creating Custom Blocks in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Using .block Files
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Using .block Files in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Using Rust Code
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Using Rust Code in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
The Graph Engine
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover The Graph Engine in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Values and Types
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Values and Types in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Error Handling
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Error Handling in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Platform Overview
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Platform Overview in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Swift (iOS/macOS)
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Swift (iOS/macOS) in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Kotlin (Android)
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Kotlin (Android) in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
React (Web)
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover React (Web) in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
WebAssembly
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover WebAssembly in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Building from Source
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Building from Source in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
FFI Integration
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover FFI Integration in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Performance Optimization
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Performance Optimization in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Testing Your Blocks
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Testing Your Blocks in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Cross-Compilation
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Cross-Compilation in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Calculator Example
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Calculator Example in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Data Pipeline
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Data Pipeline in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
String Processing
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover String Processing in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Core API
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Core API in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Language API
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Language API in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
WASM API
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover WASM API in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
FFI API
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover FFI API in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Generated Rust Docs
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Generated Rust Docs in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
How to Contribute
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover How to Contribute in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Development Setup
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Development Setup in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request
Code Style
Note: This section is under development. Check back soon for detailed documentation.
Overview
This page will cover Code Style in detail.
Coming Soon
Detailed documentation for this topic is being written.
In the Meantime
- Check out the Getting Started guide
- Explore the examples directory
- Read the source code in the repository
Contribute
Help us improve this documentation! If you'd like to contribute, please:
- Fork the repository
- Add your documentation
- Submit a pull request