Overview
AetherShell provides multiple interfaces for programmatic usage:
- 📦 Rust Library - Embed AetherShell in Rust applications
- 🔧 Command-line Interface - Execute scripts from shell
- 🖥️ REPL API - Interactive programming interface
- 🌐 HTTP API (Future) - REST API for remote execution
Rust Library API
Adding Dependency
[dependencies]
aethershell = { path = "../AetherShell" }
# or when published:
# aethershell = "1.0.0"
Basic Usage
use aethershell::{parser, eval, env::Env};
fn main() -> anyhow::Result<()> {
// Parse AetherShell code
let program = parser::parse_program(r#"
[1, 2, 3, 4, 5]
| map(fn(x) => x * 2)
| reduce(fn(a,b) => a + b, 0)
"#)?;
// Create environment
let mut env = Env::new();
// Evaluate
let result = eval::eval_program(&program, &mut env)?;
println!("Result: {:?}", result);
Ok(())
}
Core Modules
| Module |
Description |
Key Types |
parser |
Parse AetherShell code to AST |
parse_program(), parse_expr() |
ast |
Abstract Syntax Tree definitions |
Expr, Stmt, BinOp |
eval |
AST evaluation engine |
eval_program(), eval_expr() |
value |
Runtime value system |
Value, Lambda |
env |
Variable environment |
Env |
builtins |
Built-in functions |
eval_builtin() |
syntax_kb |
Syntax Knowledge Base |
SyntaxKB, AgenticBinary |
Value System
Value Enum
pub enum Value {
Null,
Int(i64),
Float(f64),
Str(String),
Bool(bool),
Array(Vec<Value>),
Record(BTreeMap<String, Value>),
Lambda(Lambda),
}
Creating Values
use aethershell::value::Value;
use std::collections::BTreeMap;
// Primitive values
let num = Value::Int(42);
let text = Value::Str("hello".to_string());
let flag = Value::Bool(true);
// Collections
let arr = Value::Array(vec![
Value::Int(1),
Value::Int(2),
Value::Int(3),
]);
let mut map = BTreeMap::new();
map.insert("name".to_string(), Value::Str("Alice".to_string()));
map.insert("age".to_string(), Value::Int(30));
let record = Value::Record(map);
Value Operations
use aethershell::value::Value;
// Pattern matching
match value {
Value::Int(n) => println!("Integer: {}", n),
Value::Str(s) => println!("String: {}", s),
Value::Array(arr) => println!("Array with {} elements", arr.len()),
Value::Record(map) => println!("Record with {} fields", map.len()),
_ => println!("Other type"),
}
Parser API
Parsing Programs
use aethershell::parser;
// Parse full program
let program = parser::parse_program(r#"
x = 10
y = 20
x + y
"#)?;
// Parse single expression
let expr = parser::parse_expr("1 + 2 * 3")?;
AST Types
use aethershell::ast::{Expr, Stmt, BinOp};
pub enum Expr {
Literal(Value),
Ident(String),
BinOp(Box<Expr>, BinOp, Box<Expr>),
Pipeline(Box<Expr>, Box<Expr>),
Lambda(Vec<String>, Vec<Stmt>, Box<Expr>),
Call(Box<Expr>, Vec<Expr>),
FieldAccess(Box<Expr>, String),
IndexAccess(Box<Expr>, Box<Expr>),
// ... more variants
}
pub enum Stmt {
VarDecl(String, Mutability, Expr),
Expression(Expr),
}
Evaluator API
Evaluating Code
use aethershell::{parser, eval, env::Env};
fn evaluate_code(code: &str) -> anyhow::Result<Value> {
let program = parser::parse_program(code)?;
let mut env = Env::new();
eval::eval_program(&program, &mut env)
}
// Usage
let result = evaluate_code("[1,2,3] | len")?;
assert_eq!(result, Value::Int(3));
Environment Management
use aethershell::env::Env;
// Create environment
let mut env = Env::new();
// Set variables
env.set("x", Value::Int(10));
env.set_mutable("counter", Value::Int(0));
// Get variables
if let Some(value) = env.get("x") {
println!("x = {:?}", value);
}
// Child scope
{
let mut child_env = env.child();
child_env.set("y", Value::Int(20));
// y only visible in child scope
}
Syntax Knowledge Base API
Accessing Syntax KB
use aethershell::syntax_kb::{get_kb, SyntaxEntry};
fn get_protocol() -> anyhow::Result<()> {
let kb = get_kb()?;
// Get entry
if let Some(entry) = kb.get("ab") {
println!("Protocol: {}", entry.name);
println!("Category: {}", entry.category);
}
// Search
let results = kb.search("binary");
println!("Found {} results", results.len());
// List by category
let protocols = kb.list_by_category("protocol");
Ok(())
}
Adding Entries
use aethershell::syntax_kb::{get_kb, SyntaxEntry};
fn add_custom_syntax() -> anyhow::Result<()> {
let kb = get_kb()?;
let entry = SyntaxEntry {
id: "myproto".to_string(),
name: "My Protocol".to_string(),
category: "protocol".to_string(),
specification: "Custom protocol specification...".to_string(),
examples: vec!["example1".to_string(), "example2".to_string()],
};
kb.add(entry)?;
Ok(())
}
AgenticBinary Encoding/Decoding
use aethershell::syntax_kb::AgenticBinary;
fn encode_decode() -> anyhow::Result<()> {
// Encode message
let bytes = AgenticBinary::encode("command", "ping", "hello")?;
// Decode message
let msg = AgenticBinary::decode(&bytes)?;
println!("Type: {}", msg.msg_type);
println!("Opcode: {}", msg.opcode);
println!("Payload: {}", msg.payload);
Ok(())
}
Command-Line Interface
Basic Usage
# Run script file
ae script.ae
# Run with arguments
ae script.ae arg1 arg2
# REPL mode
ae
# TUI mode
ae tui
# Execute inline code
ae -c "print([1,2,3] | len)"
Environment Variables
| Variable |
Purpose |
Example |
AETHER_AI |
Default AI provider |
openai, ollama |
OPENAI_API_KEY |
OpenAI API key |
sk-... |
AGENT_ALLOW_CMDS |
Whitelisted agent commands |
ls,find,grep,cat |
Exit Codes
| Code |
Meaning |
0 |
Success |
1 |
General error |
2 |
Parse error |
3 |
Runtime error |
4 |
File not found |
REPL API
Interactive Session
# Start REPL
ae
# Execute commands
> x = 10
> y = 20
> x + y
30
# Multi-line input
> [1, 2, 3, 4, 5]
| map(fn(x) => x * 2)
| print
# Load file
> load("script.ae")
# Exit
> exit(0)
REPL Commands
| Command |
Description |
help() |
Show help |
clear() |
Clear screen |
exit(code) |
Exit REPL |
env("VAR") |
Get environment variable |
Type Checking API
Type Inference
use aethershell::typecheck;
fn check_types() -> anyhow::Result<()> {
let program = parser::parse_program(r#"
x = 10
y = 20
x + y
"#)?;
// Infer types
let type_env = typecheck::infer_program(&program)?;
// Get type of variable
if let Some(ty) = type_env.get("x") {
println!("x has type: {:?}", ty);
}
Ok(())
}
Type Representation
pub enum Type {
Int,
Float,
Str,
Bool,
Null,
Array(Box<Type>),
Record(BTreeMap<String, Type>),
Lambda(Vec<Type>, Box<Type>),
Var(String), // Type variable for polymorphism
}
Error Handling
Error Types
use anyhow::{Result, Context};
fn safe_evaluation(code: &str) -> Result<Value> {
let program = parser::parse_program(code)
.context("Failed to parse code")?;
let mut env = Env::new();
let result = eval::eval_program(&program, &mut env)
.context("Failed to evaluate program")?;
Ok(result)
}
// Usage with error handling
match safe_evaluation("[1,2,3] | len") {
Ok(value) => println!("Result: {:?}", value),
Err(e) => eprintln!("Error: {}", e),
}
Building & Compilation
Build Commands
# Build debug binary
cargo build
# Build release binary
cargo build --release
# Build all binaries (ae + aimodel)
cargo build --bins --release
# Run tests
cargo test
# Run with features
cargo build --features "ai,mcp"
Binary Outputs
| Binary |
Purpose |
Location |
ae |
Main AetherShell executable |
target/release/ae.exe |
aimodel |
AI model management tool |
target/release/aimodel.exe |
Extension Points
Adding Custom Builtins
// In builtins.rs
// 1. Add to lookup
map.insert("my_function", 72);
// 2. Add to dispatch table
|args, input, _| bi_my_function(args, input),
// 3. Implement function
fn bi_my_function(args: Vec<Value>, input: Option<Value>) -> Result<Value> {
// Get argument or piped input
let value = if let Some(inp) = input {
inp
} else {
args.get(0).context("Expected argument")?.clone()
};
// Process and return
Ok(value)
}
Adding to Syntax KB
// In syntax_kb.rs - add_builtin_syntaxes()
kb.add(SyntaxEntry {
id: "my_protocol".to_string(),
name: "My Custom Protocol".to_string(),
category: "protocol".to_string(),
specification: "Protocol specification...".to_string(),
examples: vec!["example1".to_string()],
})?;
Performance Considerations
Optimization Tips
- 🔹 Use release builds for production:
cargo build --release
- 🔹 Pipeline operations are lazy-evaluated when possible
- 🔹 AgenticBinary is 3-5x more compact than JSON
- 🔹 Syntax KB caches are thread-safe and efficient
- 🔹 Type inference happens at parse time
Memory Usage
// Values are reference-counted internally
let arr = Value::Array(vec![...]); // No deep copies
let shared = arr.clone(); // Cheap clone (Rc)
Testing
Unit Tests
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pipeline() -> Result<()> {
let program = parser::parse_program(
"[1,2,3] | map(fn(x) => x * 2)"
)?;
let mut env = Env::new();
let result = eval::eval_program(&program, &mut env)?;
assert_eq!(
result,
Value::Array(vec![
Value::Int(2),
Value::Int(4),
Value::Int(6)
])
);
Ok(())
}
}
Running Tests
# All tests
cargo test
# Specific test
cargo test test_pipeline
# With output
cargo test -- --nocapture
# Integration tests only
cargo test --test '*'
Documentation
Generate Rust Docs
# Generate documentation
cargo doc --open
# With dependencies
cargo doc --open --document-private-items
API Documentation Location
target/doc/aethershell/index.html
Complete! You now have the full AetherShell API reference. For more details, see the
GitHub repository.