forge-lang 0.2.0

Forge — Internet-native programming language with natural syntax, bytecode VM, and built-in HTTP/database/crypto
# Contributing to Forge

Thank you for your interest in contributing to Forge! This guide will help you get set up and productive.

## Getting Started

### Prerequisites

- **Rust 1.85+** — install via [rustup]https://rustup.rs/
- **Git**
- A code editor (VS Code recommended — we have syntax highlighting in `editors/vscode/`)

### Setup

```bash
git clone https://github.com/humancto/forge-lang.git
cd forge-lang
cargo build
cargo test
```

All 189 tests should pass. If they don't, please open an issue.

### Verify your setup

```bash
# Run the test suite
cargo test

# Run clippy (linter)
cargo clippy

# Run examples
./target/debug/forge run examples/hello.fg
./target/debug/forge run examples/showcase.fg

# Start the REPL
./target/debug/forge
```

## Architecture

Forge is a programming language implemented in Rust. Here's how the pieces fit together:

```
Source (.fg) → Lexer → Tokens → Parser → AST → Interpreter → Result
                                      Type Checker
                                    Bytecode Compiler (--vm)
                                     Register VM + GC
```

### Module Map

```
src/
├── main.rs              # CLI entry point (clap)
├── lexer/
│   ├── token.rs         # Token enum — every atom of the language
│   └── lexer.rs         # Hand-rolled lexer, string interpolation
├── parser/
│   ├── ast.rs           # AST node definitions (Stmt, Expr, Pattern)
│   └── parser.rs        # Recursive descent parser, Pratt precedence
├── interpreter/
│   └── mod.rs           # Tree-walk interpreter, builtins, environment
├── vm/
│   ├── compiler.rs      # AST → bytecode
│   ├── machine.rs       # Register-based VM execution
│   ├── gc.rs            # Mark-sweep garbage collector
│   ├── value.rs         # VM value types
│   ├── bytecode.rs      # Instruction set
│   ├── frame.rs         # Call frames
│   └── green.rs         # Green thread scheduler (scaffold)
├── stdlib/
│   ├── math.rs          # sqrt, pow, abs, sin, cos, random, etc.
│   ├── fs.rs            # read, write, list, mkdir, copy, etc.
│   ├── io.rs            # prompt, print, args
│   ├── crypto.rs        # sha256, md5, base64, hex
│   ├── db.rs            # SQLite (open, query, execute, close)
│   ├── pg.rs            # PostgreSQL (connect, query, execute, close)
│   ├── env.rs           # Environment variables
│   ├── json_module.rs   # parse, stringify, pretty
│   ├── regex_module.rs  # test, find, replace, split
│   ├── log.rs           # info, warn, error, debug
│   ├── exec_module.rs   # run_command
│   ├── term.rs          # Colors, tables, sparklines, UI widgets
│   ├── http.rs          # HTTP client, download, crawl
│   └── csv.rs           # parse, stringify, read, write
├── runtime/
│   ├── server.rs        # HTTP server (axum), route extraction
│   └── client.rs        # HTTP client (reqwest)
├── repl/
│   └── mod.rs           # REPL with rustyline, history, completion
├── lsp/
│   └── mod.rs           # Language Server Protocol
├── testing/
│   └── mod.rs           # Test runner for @test functions
├── typechecker.rs       # Gradual type checking
├── formatter.rs         # forge fmt
├── scaffold.rs          # forge new
├── package.rs           # forge install
├── manifest.rs          # forge.toml parsing
├── learn.rs             # Interactive tutorials
├── chat.rs              # AI chat mode
└── errors.rs            # Error formatting with ariadne
```

## How to Make Changes

### Adding a new keyword

1. Add the token variant to `Token` enum in `src/lexer/token.rs`
2. Add the string-to-token mapping in `keyword_from_str()` in the same file
3. Add parsing logic in `src/parser/parser.rs` (usually in `parse_statement()`)
4. Add an AST node in `src/parser/ast.rs` if it needs new syntax
5. Add execution logic in `src/interpreter/mod.rs` (in `exec_stmt()` or `eval_expr()`)
6. Add a test

### Adding a new builtin function

1. Add the function name to `register_builtins()` in `src/interpreter/mod.rs`
2. Add a match arm in `call_builtin()` in the same file
3. Add a test
4. That's it — it's immediately available in Forge code

### Adding a new stdlib module

1. Create `src/stdlib/mymodule.rs`
2. Add `pub mod mymodule;` to `src/stdlib/mod.rs`
3. Add a `create_module()` function that returns `Vec<(&str, Value)>`
4. Register it in `register_builtins()` in the interpreter
5. Add tests

### Adding a new HTTP route method

1. Add a match arm in `extract_routes()` in `src/runtime/server.rs`
2. Add axum route registration in `start_server()` in the same file
3. Add a test

### Adding a new operator

1. Add the token variant in `src/lexer/token.rs`
2. Add lexing in `src/lexer/lexer.rs`
3. Add to the appropriate precedence level in the parser
4. Add a `BinOp` variant in `src/parser/ast.rs`
5. Add evaluation in `eval_binop()` in `src/interpreter/mod.rs`
6. Add a test

## Code Style

- **Rust 2021 edition**, targets Rust 1.85+
- **No `unwrap()` in production code** — use `?` or proper error handling
- **No `unsafe` code** — the entire codebase is safe Rust
- Keep modules focused: one concept per file
- Tests go in the same file as the code (`#[cfg(test)]` module)
- Use `IndexMap<String, Value>` for Forge objects (preserves insertion order)
- Error messages should be helpful — include suggestions when possible

### Naming Conventions

- Rust: standard snake_case for functions, PascalCase for types
- Forge builtins: snake_case (e.g., `run_command`, `base64_encode`)
- Forge modules: lowercase (e.g., `math`, `fs`, `crypto`)
- Test functions: descriptive names (e.g., `test_array_map_filter`)

## Testing

### Run all tests

```bash
cargo test
```

### Run a specific test

```bash
cargo test test_name
```

### Run Forge integration tests

```bash
./target/debug/forge test tests/
```

### Writing tests

Tests live in `#[cfg(test)]` modules at the bottom of each source file. Use the helper pattern:

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

    fn run(code: &str) -> String {
        // helper that captures output
    }

    #[test]
    fn test_my_feature() {
        let output = run("say \"hello\"");
        assert!(output.contains("hello"));
    }
}
```

For Forge-level tests, create `.fg` files in `tests/` with `@test` functions:

```
@test
define my_test() {
    assert(1 + 1 == 2)
    assert_eq(len("forge"), 5)
}
```

## Pull Request Process

1. **Fork and branch** — create a feature branch from `main`
2. **Write code** — follow the code style guidelines above
3. **Add tests** — every new feature or bugfix needs tests
4. **Run the full suite**`cargo test && cargo clippy`
5. **Run examples**`forge run examples/showcase.fg` as a smoke test
6. **Write a clear PR description** — what, why, and how to test

### PR Checklist

- [ ] `cargo test` passes (all 189+ tests)
- [ ] `cargo clippy` has no new warnings
- [ ] New features have tests
- [ ] Examples still work (`forge run examples/showcase.fg`)
- [ ] Code follows the style guide (no `unwrap()`, no `unsafe`)

## Issue Reporting

When filing a bug report, please include:

1. **Forge version** (`forge version`)
2. **OS and Rust version** (`rustc --version`)
3. **Minimal reproduction** — the smallest `.fg` file that triggers the bug
4. **Expected vs actual behavior**
5. **Full error output**

## Areas for Contribution

### Good first issues

- Fix clippy warnings (see `cargo clippy` output)
- Add doc comments to stdlib functions
- Improve error messages with more context
- Add more examples in `examples/`

### Larger projects

- **VM feature parity** — the bytecode VM doesn't support all interpreter features yet
- **Standard library expansion**`net`, `time`, `path`, `testing` modules
- **Formatter improvements**`forge fmt` handles basic indentation; needs more
- **LSP completion** — the LSP server is scaffolded but needs intellisense
- **Package registry**`forge install` works with git URLs; needs a registry

## License

By contributing to Forge, you agree that your contributions will be licensed under the MIT License.