FerrisScript Documentation

FerrisScript πŸ¦€ Development Guide

This guide covers development setup, workflows, and contribution guidelines for FerrisScript.


πŸš€ Quick Start

Prerequisites

Clone and Build

# Clone the repository
git clone https://github.com/dev-parkins/FerrisScript.git
cd FerrisScript

# Build all crates
cargo build --workspace

# Run all tests (96 tests)
cargo test --workspace

# Build optimized release
cargo build --workspace --release

Quick Commands

# Build specific crate
cargo build -p ferrisscript_compiler
cargo build -p ferrisscript_runtime
cargo build -p ferrisscript_godot_bind

# Run tests with output
cargo test --workspace -- --show-output

# Check code formatting
cargo fmt --all -- --check

# Run clippy linter (strict - treats warnings as errors)
cargo clippy --workspace --all-targets --all-features -- -D warnings

# Generate documentation
cargo doc --workspace --open

Cross-Platform Builds

FerrisScript supports building for multiple platforms. The CI automatically builds native binaries for Linux, macOS, and Windows.

Native Build (Recommended):

# Build for your current platform (always works)
cargo build --workspace --release

Cross-Compilation Setup:

⚠️ Note: Cross-compilation from Windows requires platform-specific linkers and is complex to set up. For most development, use native builds or rely on CI for multi-platform builds.

If you need to verify target compatibility locally:

# Install target platform support
rustup target add x86_64-unknown-linux-gnu
rustup target add aarch64-unknown-linux-gnu
rustup target add x86_64-apple-darwin
rustup target add aarch64-apple-darwin

# Verify compilation (may fail at linking stage without proper linkers)
cargo build --workspace --release --target x86_64-unknown-linux-gnu

CI Builds: The GitHub Actions workflow automatically builds native binaries for:

See .github/workflows/ci.yml for the full build matrix.


πŸ“ Project Structure

ferrisscript/
β”œβ”€β”€ .github/workflows/       # CI/CD automation
β”œβ”€β”€ crates/
β”‚   β”œβ”€β”€ compiler/            # Lexer, parser, type checker (69 tests)
β”‚   β”œβ”€β”€ runtime/             # AST interpreter (26 tests)
β”‚   └── godot_bind/          # GDExtension integration (1 test)
β”œβ”€β”€ docs/
β”‚   β”œβ”€β”€ archive/             # Version-specific documentation
β”‚   └── DEVELOPMENT.md       # This file
β”œβ”€β”€ examples/                # 11 example .ferris scripts
β”œβ”€β”€ godot_test/              # Test Godot project
β”œβ”€β”€ ARCHITECTURE.md          # Technical design documentation
β”œβ”€β”€ LICENSE                  # MIT License
β”œβ”€β”€ README.md                # Main project documentation
└── RELEASE_NOTES.md         # v0.0.1 release information

Crate Responsibilities

Crate Purpose Lines of Code Tests
ferrisscript_compiler Tokenization, parsing, type checking ~1,500 69
ferrisscript_runtime AST interpretation, execution engine ~1,200 26
ferrisscript_godot_bind Godot 4.x GDExtension integration ~800 1

πŸ”§ Development Workflow

1. Choose a Task

Check the roadmap in ARCHITECTURE.md or create an issue for new features.

2. Create a Branch

Follow our branch naming convention to get the right PR template automatically:

# For bug fixes β†’ Bug Fix PR template
git checkout -b bugfix/your-bug-description
# or
git checkout -b fix/parser-null-pointer

# For new features β†’ Feature PR template
git checkout -b feature/your-feature-name
# or
git checkout -b feat/async-loading

# For documentation β†’ Documentation PR template
git checkout -b docs/add-api-examples
# or
git checkout -b doc/update-readme

Why Branch Naming Matters:

See CONTRIBUTING.md for full details.

3. Make Changes

4. Test Your Changes

πŸ“š See testing/README.md for comprehensive testing documentation

# Run all tests (843+ tests across all layers)
cargo test --workspace

# Run specific test types
cargo test -p ferrisscript_compiler    # Unit tests (compiler)
cargo test -p ferrisscript_runtime     # Unit tests (runtime)
ferris-test --all                      # Integration tests (.ferris scripts)

# Run specific test
cargo test test_compile_hello

# Check formatting and linting
cargo fmt --all
cargo clippy --workspace --all-targets --all-features -- -D warnings

Testing Layers:

  1. Unit Tests (Rust) - Pure logic testing (compiler/runtime)
  2. Integration Tests (.ferris) - End-to-end testing with Godot
  3. GDExtension Tests - Godot bindings requiring runtime
  4. Benchmark Tests - Performance measurement

Quick Links:

4.5. Validate Documentation Changes

If you modified any .md files, always run documentation checks before committing:

# Option 1: VS Code Task (Recommended)
# Install dependencies (first time only)
npm install

# Check markdown formatting
npm run docs:lint

# Auto-fix formatting issues
npm run docs:fix

Why This Matters:

Common Issues Caught:

Link Checking: Broken links are automatically checked in CI. To check links manually:

# Check specific file
npx markdown-link-check your-file.md

# Check with config (quieter output)
npx markdown-link-check your-file.md --config .markdown-link-check.json -q

See ../scripts/README.md for full documentation linting guide.

5. Commit with Conventional Commits

# Feature additions
git commit -m "feat(compiler): add support for array types"

# Bug fixes
git commit -m "fix(runtime): handle division by zero correctly"

# Documentation
git commit -m "docs: update README with new examples"

# Tests
git commit -m "test(parser): add tests for field access"

# Maintenance
git commit -m "chore: update dependencies to latest versions"

6. Push and Create PR

# Push your branch (use the actual branch name you created)
git push origin bugfix/your-bug-description
# or
git push origin feature/your-feature-name
# or
git push origin docs/your-doc-update

# Then create a pull request on GitHub

What Happens Next:

  1. πŸ€– Our automation detects your branch name
  2. πŸ“‹ Appropriate PR template is auto-applied to your PR
  3. ✍️ Fill out the template sections (marked with <!-- ... --> comments)
  4. βœ… CI runs tests and checks
  5. πŸ‘€ Maintainers review and provide feedback

For GitHub Copilot Users: If you’re using GitHub Copilot to create PRs automatically, the branch naming convention ensures your automated PRs get the correct template applied!


πŸ§ͺ Testing

Test Organization

Running Tests

# All tests
cargo test --workspace

# Specific crate
cargo test -p ferrisscript_compiler

# Specific module
cargo test --lib lexer::tests

# With output (see println! statements)
cargo test -- --show-output --nocapture

# Single test
cargo test test_compile_hello -- --exact

Adding Tests

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_your_feature() {
        let result = your_function();
        assert_eq!(result, expected_value);
    }
}

οΏ½ Code Coverage

FerrisScript maintains high test coverage to ensure code quality and catch regressions early. This section explains how to generate, view, and interpret coverage reports.

Overview: Two Coverage Tools

We use different coverage tools for different environments:

Tool Environment Purpose Platform
cargo-llvm-cov Local Development Interactive development, Windows-compatible Windows, macOS, Linux
cargo-tarpaulin CI/CD (GitHub Actions) Automated coverage in CI pipelines Linux only

Why two tools?

See infrastructure/COVERAGE_SETUP_NOTES.md for the technical investigation that led to this approach.

Running Coverage Locally

Quick Method: Use Coverage Scripts

# PowerShell (Windows)
.\scripts\coverage.ps1

# Bash (Linux/macOS)
./scripts/coverage.sh

These scripts will:

  1. Check if cargo-llvm-cov is installed (auto-install if missing)
  2. Run coverage analysis on all workspace crates
  3. Generate HTML and LCOV reports in target/coverage/
  4. Display report locations

Manual Method: Direct Commands

# First time setup
rustup component add llvm-tools-preview
cargo install cargo-llvm-cov

# Generate HTML report (human-readable)
cargo llvm-cov --workspace --html --output-dir target/coverage

# Generate LCOV report (for tools like VSCode extensions)
cargo llvm-cov --workspace --lcov --output-path target/coverage/lcov.info

# Generate both formats at once
cargo llvm-cov --workspace --html --output-dir target/coverage
cargo llvm-cov --workspace --lcov --output-path target/coverage/lcov.info

# Coverage for specific crate
cargo llvm-cov -p ferrisscript_compiler --html

# Coverage with test output visible
cargo llvm-cov --workspace --html -- --nocapture

Viewing Coverage Reports

HTML Report (most user-friendly):

# Windows
Invoke-Item target/coverage/html/index.html

# Linux
xdg-open target/coverage/html/index.html

# macOS
open target/coverage/html/index.html

The HTML report shows:

LCOV Report (for tool integration):

The target/coverage/lcov.info file can be used by:

Understanding Coverage Metrics

Coverage reports show several metrics:

Metric Description Target
Line Coverage % of code lines executed by tests 80%+
Branch Coverage % of conditional branches tested 75%+
Function Coverage % of functions called by tests 90%+

Color Coding in HTML Reports:

Coverage in CI/CD

GitHub Actions Workflow:

Coverage is automatically generated on every push to main and in pull requests:

# .github/workflows/ci.yml
coverage:
  name: Code Coverage
  runs-on: ubuntu-latest
  steps:
    - name: Generate coverage
      run: cargo tarpaulin --workspace --out Xml --output-dir coverage
    
    - name: Upload to Codecov
      uses: codecov/codecov-action@v4

Why Tarpaulin in CI?

Viewing CI Coverage:

Coverage Configuration

tarpaulin.toml:

[tool]
out = ["Html", "Lcov", "Stdout"]
output-dir = "target/coverage"
workspace = true
timeout = "5m"
follow-exec = true
count = true
fail-under = 0  # Currently no enforcement, will increase as coverage improves

[report]
branches = true
lines = true

What’s Excluded:

Coverage Goals and Thresholds

Current Status:

Target Goals:

For New Code:

Troubleshooting Coverage Issues

Issue: β€œcargo-llvm-cov not found”

Solution:

# Install llvm-tools-preview component
rustup component add llvm-tools-preview

# Install cargo-llvm-cov
cargo install cargo-llvm-cov

# Verify installation
cargo llvm-cov --version

Issue: Windows File Locking (OS Error 32)

Solution: Use cargo-llvm-cov instead of cargo-tarpaulin:

# Don't use tarpaulin on Windows
# cargo tarpaulin  # ❌ May fail with file locks

# Use llvm-cov instead
cargo llvm-cov --workspace --html  # βœ… Works on Windows

Why this happens:

If you must use tarpaulin on Windows:

# Close VS Code and all IDEs
# Kill rust-analyzer process
Get-Process rust-analyzer -ErrorAction SilentlyContinue | Stop-Process

# Then run tarpaulin
cargo tarpaulin --workspace --skip-clean

Issue: Coverage Seems Low

Check:

  1. Are tests running? cargo test --workspace should show test execution
  2. Are tests in the right place? Tests should be in #[cfg(test)] modules or tests/ directories
  3. Are features enabled? Some code may be behind feature flags
  4. Are examples included? Example files aren’t included in coverage by default

Improve coverage:

// Add tests for error paths
#[test]
#[should_panic(expected = "expected error message")]
fn test_error_handling() {
    // Test code that should panic
}

// Test edge cases
#[test]
fn test_boundary_conditions() {
    // Test empty input, maximum values, etc.
}

Issue: CI Coverage Differs from Local

This is normal:

If differences are large (>5%):

Advanced Coverage Techniques

Exclude Code from Coverage:

// Exclude specific function
#[cfg(not(tarpaulin_include))]
fn debug_only_function() {
    // ...
}

// Exclude block
#[cfg(not(tarpaulin))]
{
    // Debug-only code
}

Coverage for Specific Test:

# Run coverage for specific test
cargo llvm-cov --test integration_tests --html

Coverage with Clean Build:

# Clean previous coverage data
cargo llvm-cov clean

# Generate fresh coverage
cargo llvm-cov --workspace --html

Best Practices

βœ… Do:

❌ Don’t:


οΏ½πŸ“ Code Style

Rust Conventions

Documentation

/// Compiles FerrisScript source code into an AST.
///
/// # Arguments
/// * `source` - The FerrisScript source code as a string
///
/// # Returns
/// * `Ok(Program)` - Successfully compiled AST
/// * `Err(String)` - Compilation error message
///
/// # Examples
/// ```
/// let source = "fn _ready() { print(\"Hello\"); }";
/// let ast = compile(source)?;
/// ```
pub fn compile(source: &str) -> Result<Program, String> {
    // ...
}

�️ Rust Edition Explained

What is a Rust Edition?

Rust uses an edition system (2015, 2018, 2021) to introduce backwards-incompatible changes without breaking existing code. Think of it like:

FerrisScript Uses 2021 Edition

[package]
edition = "2021"  # In all Cargo.toml files

Benefits of 2021 Edition:

Backwards Compatibility: Code compiled with edition 2021 works with libraries using 2015/2018. The edition only affects how your code is compiled, not the ABI.

Should We Upgrade?

Current: 2021 edition (latest)
Recommendation: βœ… Keep 2021 - it’s the latest stable edition


πŸ”„ Dependency Management

Updating Dependencies

# Check for outdated dependencies
cargo outdated

# Update all dependencies to latest compatible versions
cargo update

# Update specific dependency
cargo update -p gdext

# Update to breaking versions (edit Cargo.toml first)
cargo build

Adding Dependencies

# In crates/compiler/Cargo.toml
[dependencies]
serde = { version = "1.0", features = ["derive"] }

πŸ› Debugging

Debug Prints

// Use dbg! macro for debugging
let result = dbg!(some_calculation());

// Use eprintln! for errors
eprintln!("Error: {}", error_message);

Godot Integration Debugging

// In godot_bind/src/lib.rs
godot_print!("Debug info: {}", value);
godot_warn!("Warning: {}", warning);
godot_error!("Error: {}", error);

LLDB/GDB Debugging

# Build with debug symbols
cargo build

# Run with debugger
rust-lldb target/debug/your_binary
# or
rust-gdb target/debug/your_binary

πŸ“Š Performance Profiling

# Build with release optimizations
cargo build --release

# Profile with perf (Linux)
perf record target/release/your_binary
perf report

# Benchmark (if you add benches/)
cargo bench

🀝 Contributing

Commit Message Convention

We use Conventional Commits:

<type>(<scope>): <subject>

<body>

<footer>

Types:

Examples:

feat(compiler): add array type support
fix(runtime): handle division by zero
docs: update ARCHITECTURE.md with new patterns
test(parser): add tests for function calls
chore: update gdext to 0.2.0

Code Review Checklist

Before submitting a PR:


πŸ“š Resources

FerrisScript Docs

Rust Learning

Compiler/Interpreter Resources

Godot + Rust


πŸ“„ License

FerrisScript is licensed under the MIT License

MIT (to be added)