Project Structure
Part of: Reef Language Reference Last reviewed on version: 0.8.0
Overview
This guide covers how to organize Reef projects, use the build system, and configure your project with reef.toml.
Creating a New Project
Using reefc new
reefc new myproject
This creates:
myproject/
├── reef.toml # Project manifest
├── README.md # Project readme
├── .gitignore # Git ignore file
├── src/
│ └── main.reef # Entry point
├── tests/ # Test files
└── docs/ # Documentation output
Project Naming Rules
Project names must:
- Start with a lowercase letter
- Contain only:
a-z,0-9,_,-
# Valid names
reefc new myproject
reefc new my-app
reefc new web_server_2
# Invalid names
reefc new MyProject # No uppercase
reefc new 123app # Must start with letter
reefc new my.project # No dots
Initialize Existing Directory
To add Reef to an existing project:
cd existing-project
reefc init
This creates reef.toml without overwriting existing files.
Project Layout
Recommended Structure
myproject/
├── reef.toml # Required: Project configuration
├── src/
│ ├── main.reef # Entry point (proc main)
│ ├── utils.reef # Utility module
│ └── models/
│ ├── user.reef # Submodule
│ └── order.reef # Submodule
├── tests/
│ ├── test_utils.reef
│ └── test_models.reef
├── docs/ # Generated documentation
├── build/ # Build artifacts (generated)
└── README.md
Entry Point
The compiler looks for the entry point in this order:
src/main.reefmain.reef
The entry point must contain a main function, which can be either a procedure or a function returning an exit code:
// Option 1: proc main() - always exits with code 0
proc main()
println("Hello, world!")
end main
// Option 2: fn main(): int - return exit code to OS
fn main(): int
if some_error
return 1 // Exit with error
end if
return 0 // Exit with success
end main
Exit Codes:
proc main()always returns exit code 0 to the operating systemfn main(): intreturns whatever integer value you return- Use
exit(code)to exit immediately from anywhere in the program
reef.toml Configuration
Full Example
[package]
name = "myproject"
version = "0.1.0"
author = "Your Name <email@example.com>"
description = "A Reef project"
license = "MIT"
url = "https://github.com/yourusername/myproject"
[build]
entry = "src/main.reef"
output = "myproject"
output_dir = "build"
source_dirs = ["src"]
[docs]
output = "docs"
include_private = false
[package] Section
| Field | Required | Description |
|---|---|---|
name |
Yes | Project name (lowercase, no spaces) |
version |
Yes | Semantic version (e.g., "1.0.0") |
author |
No | Author name and email |
description |
No | Short project description |
license |
No | License identifier (e.g., "MIT", "Apache-2.0") |
url |
No | Project homepage or repository URL |
[build] Section
| Field | Default | Description |
|---|---|---|
entry |
"src/main.reef" |
Entry point file containing proc main() |
output |
project name | Name of the compiled executable |
output_dir |
"build" |
Directory for build artifacts |
source_dirs |
["src"] |
Additional directories to search for local modules. Each entry is resolved relative to the project root (where reef.toml lives), searched non-recursively, after the entry file's own directory and src/. |
[docs] Section
| Field | Default | Description |
|---|---|---|
output |
"docs" |
Directory for generated documentation |
include_private |
false |
Include non-exported items in docs |
Build Commands
reefc build
Compile the project:
cd myproject
reefc build
This:
- Reads
reef.toml - Compiles
src/main.reef(ormain.reef) - Resolves and compiles imported modules
- Links with the runtime
- Creates executable in
build/(named after project)
reefc run
Build and execute:
reefc run
Equivalent to reefc build && ./build/myproject
reefc run (single file)
Run a specific file without a project:
reefc run examples/hello.reef
Common Options
reefc build --check # Type-check only, don't compile
reefc build --emit-c # Generate C code only
reefc build --keep-c # Keep intermediate C file
reefc build -v # Verbose output
Module Organization
File-to-Module Mapping
Each .reef file defines a module. The module name matches the file path:
| File Path | Module Name |
|---|---|
src/main.reef |
(entry point, no module) |
src/utils.reef |
utils |
src/models/user.reef |
models.user |
lib/http/client.reef |
http.client |
Declaring Modules
// src/utils.reef
module utils
export
fn helper(): int
end export
fn helper(): int
return 42
end helper
end module
Importing Modules
// src/main.reef
import utils
import models.user
import http.client as http
proc main()
let x = helper() // From utils
let u = user.create() // From models.user
let c = http.connect() // From http.client (aliased)
end main
Import Aliases
import very.long.module.name as short
// Use short.function() instead of very.long.module.name.function()
Module Resolution
The compiler searches for modules in this order:
-
Current directory
import foolooks for./foo.reef
-
Project source directory (
src/)import foolooks forsrc/foo.reefimport models.userlooks forsrc/models/user.reef
-
Standard library (via
REEF_STDLIB_PATHor system installation)import core.strlooks for$REEF_STDLIB_PATH/core/str.reefimport net.httplooks for$REEF_STDLIB_PATH/net/http.reef
Local Module Example
myproject/
├── src/
│ ├── main.reef # import utils
│ ├── utils.reef # Found as src/utils.reef
│ └── db/
│ └── conn.reef # import db.conn → src/db/conn.reef
Environment Variables
| Variable | Purpose |
|---|---|
REEF_STDLIB_PATH |
Path to standard library |
REEF_RUNTIME_PATH |
Path to runtime library |
REEF_HOME |
Base path (sets both STDLIB and RUNTIME paths) |
Set these for development:
export REEF_HOME=/path/to/reef
# Or set individually:
export REEF_STDLIB_PATH=/path/to/reef-stdlib
export REEF_RUNTIME_PATH=/path/to/reef-runtime
Visibility and Exports
Public vs Private
By default, all declarations are private. Use export to make them public:
module mymodule
export
fn public_function(): int
type PublicType
proc public_procedure()
end export
// Public - listed in export
fn public_function(): int
return helper() // Can call private functions
end public_function
// Private - not in export
fn helper(): int
return 42
end helper
end module
What Can Be Exported
- Functions (
fn) - Procedures (
proc) - Types (
type) - Active Objects (
active object)
Documentation Generation
Generate Documentation
reefc doc
This:
- Reads source files
- Extracts comments before declarations
- Generates Markdown in
docs/directory
Documentation Comments
Use regular // comments before declarations:
// Calculates the factorial of n.
// Returns 1 for n <= 1.
fn factorial(n: int): int
if n <= 1
return 1
end if
return n * factorial(n - 1)
end factorial
reefc doc Options
reefc doc # Generate to docs/
reefc doc --output api # Generate to api/
reefc doc --private # Include private items
reefc doc -v # Verbose output
Testing
Test File Organization
myproject/
├── src/
│ └── math.reef
└── tests/
└── test_math.reef
Test File Structure
// tests/test_math.reef
import test.framework
import math
proc main()
let runner = new framework.TestRunner()
runner.assert_eq_int(add(2, 3), 5, "add works")
runner.assert_eq_int(multiply(4, 5), 20, "multiply works")
runner.report()
end main
See 080_TESTING.md for the full test.framework API —
TestRunner is qualified through the module's last path segment
(framework.TestRunner()), same rule as any other dotted import.
Running Tests
# Run a specific test file
reefc run tests/test_math.reef
# Run all tests (shell script)
for test in tests/*.reef; do
echo "Running $test..."
reefc run "$test"
done
Build Artifacts
Generated Files
After reefc build:
myproject/
├── build/
│ └── myproject # Executable (Linux/macOS)
│ └── myproject.exe # Executable (Windows)
└── ...
Cleaning
reefc clean
Removes:
build/directory- Generated
.cfiles (if--keep-cwas used) - Generated
.ofiles
.gitignore
The generated .gitignore includes:
build/
*.c
*.o
*.so
*.exe
.DS_Store
Multi-File Projects
Example: Web Server
webserver/
├── reef.toml
├── src/
│ ├── main.reef # Entry point
│ ├── config.reef # Configuration handling
│ ├── http/
│ │ ├── server.reef # HTTP server
│ │ ├── request.reef # Request parsing
│ │ └── response.reef # Response building
│ ├── handlers/
│ │ ├── static.reef # Static file handler
│ │ └── api.reef # API handlers
│ └── utils/
│ ├── logging.reef # Logging utilities
│ └── json.reef # JSON parsing
└── tests/
├── test_http.reef
└── test_handlers.reef
main.reef for Multi-File Project
// src/main.reef
import config
import http.server
import handlers.static
import handlers.api
import utils.logging
proc main()
let cfg = config.load("config.toml")
let logger = logging.create(cfg.log_level)
let server = new server.Server(cfg.port)
server.add_handler("/static", static.handler)
server.add_handler("/api", api.handler)
logger.info("Starting server on port ${cfg.port}")
server.start()
end main
Note: A dotted import http.server is referenced by its last path
segment (server.Type, server.function()), not the full dotted path —
new http.server.Server(...) is a syntax error. This matches import
aliasing: import http.server as hs would instead make it hs.Server.
Best Practices
1. Keep main.reef Small
// Good: main.reef is just entry point
import app
proc main()
app.start()
end main
Note: Name the module's entry function something other than run —
run is reserved (it's the Active Object run() method keyword) and cannot
be used as a proc/fn name, even module-qualified.
2. One Concept Per File
src/
├── user.reef # User type and operations
├── order.reef # Order type and operations
├── payment.reef # Payment processing
3. Group Related Modules
src/
├── models/ # Data types
├── services/ # Business logic
├── handlers/ # Request handlers
└── utils/ # Utilities
4. Test Mirror Source Structure
src/models/user.reef → tests/test_user.reef
src/services/auth.reef → tests/test_auth.reef
5. Document Public APIs
import core.option
// User represents a registered user in the system.
// All users have a unique ID and email address.
type User = struct
id: int
email: string
name: string
end User
// Creates a new user with the given email and name.
// Returns None if the email is already registered.
fn create_user(email: string, name: string): option.Option[User]
// ... (check for an existing user, assign an id, etc.)
return @option.Option[User].None()
end create_user
Troubleshooting
"Module not found"
- Check file exists at expected path
- Check
REEF_STDLIB_PATHis set for stdlib imports - Check module declaration matches filename
"Entry point not found"
- Ensure
src/main.reeformain.reefexists - Ensure it contains
proc main()
"reef.toml not found"
- Run
reefc initto create it - Or create manually with
[package]section
Previous: 005_COMPILER_USAGE.md Next: 015_BASICS.md