h2lang
v0.5.4
Published
H2 Language - A Herbert Online Judge compatible programming language with multi-agent support
Downloads
735
Maintainers
Readme
H2 Language
H2 Language (h2lang) is a programming language compiler fully compatible with the Herbert Online Judge (HOJ) H language specification, extended with multi-agent support for robot swarm control.
Features
- HOJ Compatible - Full support for Herbert Online Judge syntax including macros, functions, and recursion
- Multi-Agent Support - Control multiple robots simultaneously with parallel scheduling
- WebAssembly Ready - Runs in browsers via wasm-bindgen
- Zero Dependencies Runtime - Lightweight compiled output
- Type-Safe - Written in Rust with comprehensive error handling
- Well Documented - Extensive documentation following Rust API guidelines
Quick Start
# Single robot drawing a square
0: f(X):XXXX f(sssr)
# Two robots moving in parallel
0: srl
1: lrs
# Recursive pattern with numeric argument
0: a(X):sra(X-1) a(4)Installation
From crates.io (Rust)
cargo add h2langFrom npm (WebAssembly)
npm install h2langFrom Source
# Clone the repository
git clone https://github.com/ekusiadadus/h2lang.git
cd h2lang
# Build WebAssembly package
wasm-pack build --target web --out-dir pkg
# Or build native library
cargo build --release
# Run tests
cargo testRequirements
- Rust 1.70+
- wasm-pack (for WebAssembly builds)
- Node.js 16+ (for npm usage)
Language Specification
Basic Commands
| Command | Description | Action |
|---------|-------------|--------|
| s | Straight | Move forward one step |
| r | Right | Rotate 90° clockwise |
| l | Left | Rotate 90° counter-clockwise |
Agent Definition
Each line defines commands for a specific robot (agent):
agent_id: commands
# Examples
0: srl # Agent 0: straight, right, left
1: llss # Agent 1: left, left, straight, straightMacros
Define reusable command sequences with single lowercase letters:
# Syntax: name:body
x:ss # Define macro 'x' as 'ss'
xrx # Expands to: ssrss
# Full example
0: x:sssr xrxrxrx # Square pattern using macroFunctions
Functions support parameters (uppercase letters) and recursion:
# Syntax: name(PARAMS):body
f(X):XXX f(s) # Repeats argument 3 times → sss
f(X):XXXX f(sssr) # Square pattern → sssrsssrsssrsssr
# Multiple parameters
a(X,Y):Ya(X-1,Y) a(4,s) # Repeat 's' four times → ssss
# Numeric arguments with recursion
a(X):sra(X-1) a(4) # Spiral pattern (terminates when X ≤ 0)Numeric Expressions
Functions support numeric arguments and arithmetic:
a(X):sa(X-1) a(4) # X decrements: 4→3→2→1→0(stop)
a(X):sa(X+1) a(-2) # X increments: -2→-1→0(stop)Termination Rule: When a numeric argument is ≤ 0, the function returns empty (recursion stops).
Comments
# This is a comment
0: srl # Inline comment
// C-style comments also workImplementation Notes
Whitespace Handling
- Agent ID: Agent IDs must appear at the start of a line. Leading spaces/tabs before the agent ID are permitted and treated as line-start context.
- Function/Macro Definitions: No whitespace is allowed between the identifier and
(in function definitions (e.g.,f(X):...is valid,f (X):...is not). - Spaces: Spaces and tabs between tokens are generally ignored except where they affect line-start detection.
Recursion and Termination
- Maximum Recursion Depth: The expander has a maximum recursion depth of 100 to prevent stack overflow from deeply nested macro/function calls. Exceeding this limit results in an expansion error.
- Numeric Termination: When any numeric argument becomes ≤ 0, the function call returns an empty sequence (no commands). This applies to all numeric parameters in the function.
- Expansion Limit: There is no explicit limit on the total number of expanded commands, but deeply recursive patterns may hit the depth limit first.
Error Handling
Compilation errors include:
- Line and column information for precise error location
- Expected vs. found tokens for parse errors
- Undefined macro/function references
- Maximum recursion depth exceeded
Examples
Drawing Shapes
# Square (4 sides)
0: f(X):XXXX f(sssr)
# Triangle (3 sides)
0: f(X):XXX f(ssssrr)
# Spiral
0: a(X):sra(X-1) a(8)Multi-Robot Choreography
# Two robots moving in mirror pattern
0: srlsrl
1: slrslr
# Three robots with different patterns
0: f(X):XXXX f(sr)
1: f(X):XXXX f(sl)
2: ssssssssComplex Recursion
# Nested function calls
0: f(X):XX f(f(s)) # f(s)=ss, f(ss)=ssss → 4 commands
# Parameterized repetition
0: a(X,Y):Ya(X-1,Y) a(3,sr) # srsrsr (repeat 'sr' 3 times)API Reference
Rust (Native)
use h2lang::compile_native;
use h2lang::output::CompileResult;
let result = compile_native("0: srl\n1: lrs");
match result {
CompileResult::Success { program } => {
println!("Agents: {}", program.agents.len());
println!("Max steps: {}", program.max_steps);
for entry in &program.timeline {
println!("Step {}: {:?}", entry.step, entry.agent_commands);
}
}
CompileResult::Error { errors } => {
for err in errors {
eprintln!("Error at {}:{}: {}", err.line, err.column, err.message);
}
}
}JavaScript/TypeScript (WebAssembly)
import init, { compile, validate, version } from 'h2lang';
await init();
// Compile source code
const result = compile('0: srl');
if (result.status === 'success') {
console.log(result.program.timeline); // Parallel execution timeline
console.log(result.program.agents); // Per-agent command lists
}
// Validate without compiling
const validation = validate('0: srl');
console.log(validation.valid); // true or false
// Get compiler version
console.log(version()); // "0.1.0"Output Format
The compiler produces a JSON structure:
{
"status": "success",
"program": {
"agents": [
{
"id": 0,
"commands": [
{"type": "straight", "steps": 1},
{"type": "rotate_right", "angle": 90},
{"type": "rotate_left", "angle": -90}
]
}
],
"max_steps": 3,
"timeline": [
{
"step": 0,
"agent_commands": [
{"agent_id": 0, "command": {"type": "straight", "steps": 1}}
]
}
]
}
}Building from Source
WebAssembly Build
# Install wasm-pack if not already installed
cargo install wasm-pack
# Build for web
npm run build
# or
wasm-pack build --target web --out-dir pkg
# Build for Node.js
wasm-pack build --target nodejs --out-dir pkgNative Build
# Debug build
cargo build
# Release build
cargo build --release
# Run tests
cargo test
# Generate documentation
cargo doc --no-deps --openTesting
# Run all tests (241 tests)
cargo test
# Run tests with output
cargo test -- --nocapture
# Run specific test module
cargo test basic_commands
# WebAssembly tests (requires Chrome)
wasm-pack test --headless --chromeProject Structure
h2lang/
├── .github/
│ ├── workflows/
│ │ └── ci.yml # CI pipeline (fmt, clippy, test, wasm)
│ ├── ISSUE_TEMPLATE/ # Issue templates
│ └── PULL_REQUEST_TEMPLATE.md
├── src/
│ ├── lib.rs # Main entry point, WASM bindings
│ ├── lexer.rs # Tokenizer
│ ├── parser.rs # Recursive descent parser
│ ├── ast.rs # Abstract Syntax Tree definitions
│ ├── expander.rs # Macro/function expansion
│ ├── scheduler.rs # Multi-agent parallel scheduling
│ ├── output.rs # JSON output structures
│ ├── token.rs # Token definitions
│ └── error.rs # Error types
├── tests/
│ └── h_language_compatibility.rs # 145 HOJ compatibility tests
├── Cargo.toml # Rust dependencies
├── package.json # npm configuration
├── rust-toolchain.toml # Rust toolchain configuration
├── CONTRIBUTING.md # Contribution guidelines
├── CODE_OF_CONDUCT.md # Community guidelines (Contributor Covenant)
├── CHANGELOG.md # Version history
└── LICENSE # MIT LicenseArchitecture
Source Code → Lexer → Parser → AST → Expander → Scheduler → Output
↓ ↓ ↓ ↓ ↓ ↓ ↓
"0:srl" Tokens Parse Tree Commands Timeline JSON
Tree (expanded) (parallel)Contributing
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
Quick Contribution Guide
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Write tests for new functionality
- Ensure all tests pass (
cargo test) - Format code (
cargo fmt) - Run linter (
cargo clippy) - Commit with clear messages following Conventional Commits
- Open a Pull Request
Development Commands
# Format code
cargo fmt
# Run linter
cargo clippy -- -D warnings
# Run all checks before PR
cargo fmt --check && cargo clippy -- -D warnings && cargo testCommunity
- GitHub Issues - Bug reports and feature requests
- GitHub Discussions - Questions and ideas
License
This project is licensed under the MIT License - see the LICENSE file for details.
Related Links
- Herbert Online Judge - Original HOJ platform
- HOJ GitHub - HOJ source code
- Codeforces Discussion - Community discussion
- wasm-bindgen - Rust/WebAssembly bindings
Acknowledgments
- Herbert Online Judge by @quolc
- Microsoft ImagineCup for the original Herbert game concept
- The Rust community for excellent tooling
