mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-14 13:49:08 +00:00
94 lines
5.1 KiB
Text
94 lines
5.1 KiB
Text
# Roc Compiler
|
|
|
|
This repository contains the source code for the Roc compiler -- both the original prototype written in Rust, and the new production version written in Zig.
|
|
|
|
All discussion, unless otherwise explicitly requested, is regarding the new Zig production version. Therefore, ONLY files under the `src/` directory should be considered moving forward.
|
|
|
|
## Documentation
|
|
|
|
Our approach is to review and update with each change. Quality documentation helps us understand quickly; however, this only works if we continuously improve and maintain it.
|
|
|
|
The documentation is aimed at our compiler engineers who only need high-level detail - they are comfortable using precise technical terminology. Therefore, our documentation should be concise, focusing on the WHY and avoiding the WHAT or HOW -- we shouldn't include implementation details as we expect these to change frequently.
|
|
|
|
Every `.zig` file should start with module documentation `//!` that describes the module's purpose.
|
|
Every `src/` directory should have a `README.md` file that describes the directory's purpose.
|
|
|
|
## Tests
|
|
|
|
Roc uses two different test strategies to Verify Correctness, and Validate Behaviour.
|
|
1. **Verify Correctness:** Where necessary, we add unit tests to ensure low-level or specific implementation details remain correct. These are written in `.zig` files using the `test` keyword alongside the code they are testing. These tests are typically limited in scope to a single function.
|
|
2. **Validate Behaviour:** More commonly, we add snapshot tests to provide assurance that the compiler continues to behave as expected. Snapshot files `.md` concurrently exercise many parts of the compiler by presenting the output of each stage for a given snippet of Roc code. Unlike unit tests, this has relevant debug-level information depth and multiple-compiler stage breadth.
|
|
|
|
### Usage
|
|
|
|
- Run all tests: `zig build test`. To run a specific test: `zig build test -- --test-filter "name of test"`. NEVER use the standard `zig test`, it does not work with our project.
|
|
- Generate or update all snapshots files: `zig build snapshot`
|
|
- Generate or update specific snapshot file: `zig build snapshot -- <file_path>`
|
|
- Update EXPECTED from PROBLEMS in snapshot file: `zig build update-expected -- <file_path>`
|
|
- To generate (or update) AND test the snapshots: `zig build snapshot && zig build test`.
|
|
|
|
### Snapshot File Structure
|
|
|
|
Each snapshot is a markdown file with the following key sections:
|
|
|
|
- **META**: Contains `description` and `type`. The `type` can be `file`, `expr`, `statement`, `header`, or `repl`, depending on the scope of the test.
|
|
- **SOURCE**: The Roc code being tested.
|
|
- **EXPECTED**: Defines the expected outcome, which can be `NIL`, an error name, or specific output. This can be populated automatically using `zig build update-expected`.
|
|
- **Generated Sections**: The tool automatically generates sections like `TOKENS`, `PARSE`, `CANONICALIZE`, `TYPES`, and `PROBLEMS`.
|
|
|
|
### REPL Snapshots
|
|
|
|
The `type=repl` snapshots are special tests for the Read-Eval-Print Loop functionality. They simulate interactive REPL sessions:
|
|
|
|
- **SOURCE**: Contains REPL commands prefixed with `»`, one per line
|
|
- **EXPECTED**: Contains the expected output for each command, separated by `---`
|
|
- The REPL maintains state between commands within a single snapshot
|
|
- Used to test type annotation display, expression evaluation, and error handling
|
|
|
|
Example:
|
|
```
|
|
# SOURCE
|
|
~~~roc
|
|
» 42
|
|
» "hello"
|
|
» [1, 2, 3]
|
|
~~~
|
|
# EXPECTED
|
|
42
|
|
---
|
|
"hello"
|
|
---
|
|
[1, 2, 3] : List(Num *)
|
|
```
|
|
|
|
### Best Practices for Creating Snapshots
|
|
|
|
1. **Focused Intent**: Each snapshot should test a single, specific compiler behaviour. This is useful for new features, error cases, edge cases, and regression prevention.
|
|
2. **Minimal Complexity**: Use the simplest possible code to demonstrate the behaviour you are testing. Consider using `...` ellipsis, which is valid syntax for "not implemented" and commonly used in examples.
|
|
3. **Clear Naming**: The file name should clearly describe the test's purpose (e.g., `can_list_type_mismatch.md` is better than `test1.md`).
|
|
|
|
### Debugging with Snapshots
|
|
|
|
Snapshots are powerful for debugging as they show the compiler's output at every stage:
|
|
`SOURCE` -> `TOKENS` -> `PARSE` -> `CANONICALIZE` -> `TYPES`.
|
|
|
|
The `PROBLEMS` section provides human-readable diagnostics. When you modify the compiler, run `zig build snapshot` and review the diffs to ensure any changes are intentional before they are committed to the repository.
|
|
|
|
## Roc Language Syntax
|
|
|
|
When writing Roc code in tests or examples, remember these syntax rules:
|
|
|
|
- **Naming Convention**: Roc uses `snake_case` for identifiers, not `camelCase`
|
|
- **Boolean Operators**: Use `and` and `or` keywords, not `&&` or `||`
|
|
- **Lambda Syntax**: Anonymous functions use bars: `my_fn = |arg1, arg2| other_fn(arg1, arg2)`
|
|
- **If Expressions**: The syntax is `if condition then_branch else else_branch` with no `then` keyword
|
|
- **Blocks**: Use curly braces to define inline variables. The last expression in the block is its return value:
|
|
```
|
|
my_fn = |arg1, arg2| {
|
|
inline = arg1
|
|
inline2 = other_fn(inline, arg2)
|
|
answer = inline + inline2
|
|
|
|
answer
|
|
}
|
|
```
|