Thank you for your interest in contributing to Forge! This guide will help you get set up and productive.
- Rust 1.85+ — install via rustup
- Git
- A code editor (VS Code recommended — we have syntax highlighting in
editors/vscode/)
git clone https://github.com/humancto/forge-lang.git
cd forge-lang
cargo build
cargo testAll 189 tests should pass. If they don't, please open an issue.
# 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/forgeForge 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
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
- Add the token variant to
Tokenenum insrc/lexer/token.rs - Add the string-to-token mapping in
keyword_from_str()in the same file - Add parsing logic in
src/parser/parser.rs(usually inparse_statement()) - Add an AST node in
src/parser/ast.rsif it needs new syntax - Add execution logic in
src/interpreter/mod.rs(inexec_stmt()oreval_expr()) - Add a test
- Add the function name to
register_builtins()insrc/interpreter/mod.rs - Add a match arm in
call_builtin()in the same file - Add a test
- That's it — it's immediately available in Forge code
- Create
src/stdlib/mymodule.rs - Add
pub mod mymodule;tosrc/stdlib/mod.rs - Add a
create_module()function that returnsVec<(&str, Value)> - Register it in
register_builtins()in the interpreter - Add tests
- Add a match arm in
extract_routes()insrc/runtime/server.rs - Add axum route registration in
start_server()in the same file - Add a test
- Add the token variant in
src/lexer/token.rs - Add lexing in
src/lexer/lexer.rs - Add to the appropriate precedence level in the parser
- Add a
BinOpvariant insrc/parser/ast.rs - Add evaluation in
eval_binop()insrc/interpreter/mod.rs - Add a test
- Rust 2021 edition, targets Rust 1.85+
- No
unwrap()in production code — use?or proper error handling - No
unsafecode — 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
- 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)
cargo testcargo test test_name./target/debug/forge test tests/Tests live in #[cfg(test)] modules at the bottom of each source file. Use the helper pattern:
#[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)
}
- Fork and branch — create a feature branch from
main - Write code — follow the code style guidelines above
- Add tests — every new feature or bugfix needs tests
- Run the full suite —
cargo test && cargo clippy - Run examples —
forge run examples/showcase.fgas a smoke test - Write a clear PR description — what, why, and how to test
-
cargo testpasses (all 189+ tests) -
cargo clippyhas no new warnings - New features have tests
- Examples still work (
forge run examples/showcase.fg) - Code follows the style guide (no
unwrap(), nounsafe)
When filing a bug report, please include:
- Forge version (
forge version) - OS and Rust version (
rustc --version) - Minimal reproduction — the smallest
.fgfile that triggers the bug - Expected vs actual behavior
- Full error output
- Fix clippy warnings (see
cargo clippyoutput) - Add doc comments to stdlib functions
- Improve error messages with more context
- Add more examples in
examples/
- VM feature parity — the bytecode VM doesn't support all interpreter features yet
- Standard library expansion —
net,time,path,testingmodules - Formatter improvements —
forge fmthandles basic indentation; needs more - LSP completion — the LSP server is scaffolded but needs intellisense
- Package registry —
forge installworks with git URLs; needs a registry
By contributing to Forge, you agree that your contributions will be licensed under the MIT License.