🦊 LynxQL - Declarative Modeling Language
LynxQL is a declarative modeling language for defining and solving combinatorial optimization problems using Integer Linear Programming (ILP). It features a complete Rust parser implementation with real-time type checking and VSCode extension support.
✨ Key Features
Language Features
- Declarative syntax — no loops, no side effects
- Strong typing with support for primitive and composite types
- Enum support with type-safe variant checking
- Logic composition using built-ins like
All,Any,AtLeast,Not, etc. - Lambda expressions with type inference
- Built-in functions:
find,match,sum,solve, and more - ILP-compatible: all model logic compiles to linear formulations
Implementation Features
- Complete Rust parser using
nomparsing combinators - Comprehensive type checker with detailed error reporting
- Language Server Protocol (LSP) support for real-time diagnostics
- VSCode extension with syntax highlighting and intelligent error positioning
- Resilient parsing that continues after errors to show multiple issues
📦 Quick Example
enum Material {
Steel,
Wood,
Plastic
}
type Hammer: bool {
material: Material,
size: int,
cost: float
}
type Toolbox: All {
hammers: AtLeast<1>[Hammer] = (_) -> find((h: Hammer) -> h.size >= 8),
nails: Any[Nail]?,
weight: int = (t: Toolbox) -> match({
Any(find((h: Hammer) -> h.material == Material.Steel)): 10,
Any(find((h: Hammer) -> h.material == Material.Wood)): 7,
_: 5
})
}
type Carpenter: All {
name: string,
age: int,
workable: bool = (c: Carpenter) -> c.age >= 18,
toolbox: Toolbox,
salary: float = (c: Carpenter) -> 20000.0 - sum(c.toolbox.hammers.cost)
}
Hammer hammer1 {
material: Material.Steel,
size: 12,
cost: 25.50
}
Carpenter john {
name: "John Doe",
age: 30,
toolbox: Toolbox {
hammers: AtLeast<1> { hammer1 }
}
}
solution = solve(john, { hammer1: 1.0 }, { Not { hammer1 } })
🚀 Getting Started
Installation
-
Clone the repository:
-
Build the parser:
-
Install VSCode extension (optional):
Usage
Parse and Type-check Files
# Parse a Lynx file
# Type-check with detailed output
Use as Rust Library
use ;
let lynx_code = r#"
type Hammer: bool {
material: string,
size: int,
cost: float
}
Hammer hammer1 {
material: "steel",
size: 12,
cost: 25.50
}
"#;
match parse_program
Language Server (LSP)
# Start LSP server for IDE integration
🧠 Language Concepts
🧱 Type System
Primitive Types
bool- Boolean valuesint- Integer valuesfloat- Floating point valuesstring- String values
Named Types
type Size: int
type Product: All {
options: Exactly<1>[Option],
price: float = (p: Product) -> sum(p.options.price)
}
Enum Types
enum Material {
Steel,
Wood,
Plastic
}
type Tool: bool {
material: Material // Type-safe enum usage
}
🔗 Logic Types & Relationships
Logic types express cardinality and relationship constraints:
All[T]– all connected instances must be satisfiedAny[T]– at least one must be satisfiedExactly<N>[T]– exactly N instances must be selectedAtLeast<N>[T]– at least N instances must be selectedAtMost<N>[T]– at most N instances must be selectedNot[T]– negation of the condition- Optional relationships use
?after the type (e.g.Any[Nail]?)
🪮 Computed Properties
Properties can be computed using pure, side-effect-free lambda expressions:
type Toolbox: All {
hammers: Any[Hammer],
total_cost: float = (t: Toolbox) -> sum(t.hammers.cost),
weight: int = (t: Toolbox) -> match({
Any(find((h: Hammer) -> h.material == Material.Steel)): 10,
_: 5
})
}
🔍 Find & Filter
Use find((x: Type) -> condition) to dynamically match instances:
heavy_hammers = find((h: Hammer) -> h.size >= 10)
steel_tools = find((t: Tool) -> t.material == Material.Steel)
🧲 Match Expressions
Use match for piecewise logic with multiple conditions:
shipping_cost: float = (order: Order) -> match({
order.weight <= 5: 10.0,
order.weight <= 20: 25.0,
order.priority == "express": 50.0,
_: 35.0
})
🚀 Optimization
The solve function initiates optimization with:
- Target variable to optimize
- Weighted objectives (what to maximize/minimize)
- Constraints (what must be satisfied)
solution = solve(
john, // Target variable
{ hammer1: 1.0, nail1: 0.5 }, // Objectives with weights
{ Not { expensive_tool } } // Constraints
)
🔧 Built-in Functions
| Function | Description | Example |
|---|---|---|
solve(var, obj, constraints) |
Optimize a variable with objectives and constraints | solve(john, {hammer1: 1.0}, {Not{tool2}}) |
find((x: T) -> condition) |
Select instances based on filter | find((h: Hammer) -> h.size > 10) |
sum(collection) |
Add numeric values over a collection | sum(toolbox.hammers.cost) |
match({conditions}) |
Piecewise logic mapping | match({x > 10: "big", _: "small"}) |
propagate(logic) |
Forward-evaluate logical implications | propagate(All{hammer1, hammer2}) |
first(collection) |
Get the first element from a collection | first(available_tools) |
🏗️ Parser & Type Checker
Architecture
The LynxQL implementation uses:
nomparsing combinators for robust, composable parsing- Complete AST representation of all language constructs
- Comprehensive type checker with detailed error reporting
- Resilient parsing that recovers from errors to show multiple issues
AST Structure
Program- Root node containing multiple statementsStatement- Top-level constructs (TypeDecl, InstanceDecl, Assignment, SolveCall)TypeDecl- Type declarations with logic types and field specificationsExpr- Expressions including literals, lambdas, logic expressions, constructorsLogicType- Logic types (All, Any, Not, Boolean, Integer, IntegerRange, etc.)
Type Checking Features
- Complete type system validation for all Lynx constructs
- Enum validation ensuring valid variants and type compatibility
- Lambda type inference with parameter and return type checking
- Field validation for assignments, required fields, and optional fields
- Logic type support for all cardinality and relationship operators
- Comprehensive error reporting with detailed, actionable messages
Error Types
The type checker provides detailed errors through TypeCheckError:
UndefinedType- Reference to undefined typeUndefinedEnumVariant- Invalid enum variant usageTypeMismatch- Type incompatibilityFieldNotFound- Unknown field in typeMissingRequiredField- Required field not providedInvalidLambda- Lambda expression errorsInvalidLogicExpression- Logic type constraint errors
💻 VSCode Extension
The VSCode extension provides:
- Complete syntax highlighting for all Lynx constructs
- Real-time type checking with LSP integration
- Intelligent error positioning showing errors at exact source locations
- Auto-completion for types, fields, and built-in functions
- Custom color theme optimized for Lynx code
- Multi-comment support (
//and/* */)
Installation
- Download
lynx-language-4.0.6.vsixfrom releases - Install via VSCode: Extensions → Install from VSIX
- Configure LSP server path in settings
📜 Design Constraints
LynxQL compiles to Integer Linear Programs, which enforces:
- ❌ No loops or recursion
- ❌ No nonlinear math (e.g.
x * y,x / y) - ❌ No side effects or mutations
- ✅ All functions must return linear-compatible results
- ✅ All expressions are deterministic and pure
- ✅ All logic must be expressible as linear constraints
📃 File Format
- Extension:
.lynx - Comments:
- Single-line:
// comment - Multi-line:
/* block comment */
- Single-line:
- Encoding: UTF-8
🔧 Development Commands
Building and Testing
Parser Testing
Documentation
🛣️ Roadmap
- Complete Rust parser with
nom - Comprehensive type checker
- Enum type support with validation
- Lambda expressions with type inference
- Language Server Protocol (LSP) implementation
- VSCode extension with real-time diagnostics
- Intelligent error positioning
- Resilient parsing for multiple error reporting
- Static linearity checker for ILP compatibility
- Integration with ILP solvers
- Advanced type inference and shape analysis
- Performance optimizations
- Additional IDE integrations
📚 Learn More
- Examples: See
example.lynxandexample_with_enums.lynx - Grammar: Reference
grammar.larkfor complete syntax specification - VSCode Extension: Check
vscode-lynx-extension/directory - API Documentation: Run
cargo doc --open
🤝 Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass with
cargo test - Run
cargo clippyandcargo fmt - Submit a pull request
📄 License
MIT License - see LICENSE file for details.