h2lang 0.5.4

H2 Language - A Herbert Online Judge compatible programming language with multi-agent support
docs.rs failed to build h2lang-0.5.4
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

H2 Language

CI License: MIT Rust WebAssembly Crates.io Documentation

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 h2lang

From npm (WebAssembly)

npm install h2lang

From 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 test

Requirements

  • 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, straight

Macros

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 macro

Functions

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 work

Implementation 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: ssssssss

Complex 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 pkg

Native Build

# Debug build
cargo build

# Release build
cargo build --release

# Run tests
cargo test

# Generate documentation
cargo doc --no-deps --open

Testing

# 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 --chrome

Project 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 License

Architecture

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

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Write tests for new functionality
  4. Ensure all tests pass (cargo test)
  5. Format code (cargo fmt)
  6. Run linter (cargo clippy)
  7. Commit with clear messages following Conventional Commits
  8. 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 test

Community

License

This project is licensed under the MIT License - see the LICENSE file for details.

Related Links

Acknowledgments

  • Herbert Online Judge by @quolc
  • Microsoft ImagineCup for the original Herbert game concept
  • The Rust community for excellent tooling