Phase 5: HAMT Persistent Map + Persistent Set, 86 tests pass
- T5.3: HAMT Persistent Map (lib/cljnim_pmap.nim)
O(log32 n) assoc/dissoc/get with structural sharing
16 unit tests, ckMap migrated from seq to HAMT
- T5.4: Persistent Set backed by HAMT map
ckSet with conj/disj/contains?/get, set literal #{}
- Fix: cljContains/cljCount return CljVal (not bool/int)
- Fix: equality in generics via explicit cljEq parameter
- Updated roadmap, task board, CI/Makefile
This commit is contained in:
+163
@@ -0,0 +1,163 @@
|
||||
# AI Agent Onboarding Guide
|
||||
|
||||
> If you are an AI reading this: welcome. This project is designed to be worked on by AI agents. Everything you need is in this repo. No human required.
|
||||
|
||||
## What is this project?
|
||||
|
||||
**Clojure/Nim** is a Clojure dialect that compiles to Nim, then to C, then to native binaries.
|
||||
|
||||
Current state: **Working compiler + REPL + macro system + file/git ops + Nim interop.**
|
||||
|
||||
## Quick Start (for AI agents)
|
||||
|
||||
```bash
|
||||
# 1. Build
|
||||
make build
|
||||
|
||||
# 2. Run tests
|
||||
make test
|
||||
|
||||
# 3. Check everything works
|
||||
make check
|
||||
|
||||
# 4. Try the REPL
|
||||
./cljnim repl
|
||||
user> (+ 1 2)
|
||||
=> 3
|
||||
```
|
||||
|
||||
## How to add a feature
|
||||
|
||||
### Step 1: Pick a task from TASKS.md
|
||||
```bash
|
||||
cat TASKS.md
|
||||
```
|
||||
Each task has:
|
||||
- **Files to modify** — exact Nim/Clojure files
|
||||
- **Acceptance criteria** — how to verify it works
|
||||
- **Complexity** — Small / Medium / Large
|
||||
|
||||
### Step 2: Implement
|
||||
|
||||
Most tasks follow this pattern:
|
||||
|
||||
1. **If adding a Clojure core function** (e.g. `range`, `str`):
|
||||
- Add the Nim implementation in `lib/cljnim_runtime.nim`
|
||||
- Add the name mapping in `src/emitter.nim` → `runtimeName()`
|
||||
- Add a test in `tests/test_emitter.nim`
|
||||
- Add an example in `examples/`
|
||||
|
||||
2. **If adding emitter special form support** (e.g. `loop`/`recur`):
|
||||
- Modify `src/emitter.nim` → `emitSpecialForm()`
|
||||
- Add test in `tests/test_emitter.nim`
|
||||
|
||||
3. **If adding a macro** (e.g. `lazy-seq`):
|
||||
- Add macro implementation in `src/macros.nim` → `initBuiltinMacros()`
|
||||
- Add test
|
||||
|
||||
### Step 3: Test your change
|
||||
|
||||
```bash
|
||||
# Build after any change
|
||||
make build
|
||||
|
||||
# Run all tests
|
||||
make test
|
||||
|
||||
# Run a specific example to verify
|
||||
./cljnim run examples/hello.clj
|
||||
./cljnim run examples/your_new_example.clj
|
||||
```
|
||||
|
||||
### Step 4: Commit
|
||||
|
||||
```bash
|
||||
# From Clojure code:
|
||||
(git/commit "Your commit message")
|
||||
(git/push)
|
||||
```
|
||||
|
||||
Or from shell:
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "Your commit message"
|
||||
git push
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
├── src/
|
||||
│ ├── cljnim.nim # CLI entry point — commands: compile, run, read, repl
|
||||
│ ├── reader.nim # Clojure → AST (EDN parser)
|
||||
│ ├── emitter.nim # AST → Nim code generator (~960 lines)
|
||||
│ ├── macros.nim # Macro expansion engine (~470 lines)
|
||||
│ ├── repl.nim # Human + JSON REPL
|
||||
│ ├── types.nim # AST node types
|
||||
│ ├── core.nim # Core arithmetic/runtime helpers
|
||||
│ └── runtime.nim # Additional runtime
|
||||
├── lib/
|
||||
│ └── cljnim_runtime.nim # Clojure runtime in Nim (~1100 lines)
|
||||
│ # This is where Clojure functions live
|
||||
├── examples/
|
||||
│ ├── hello.clj # Basic hello world
|
||||
│ ├── math.clj # Functions + recursion
|
||||
│ ├── core.clj # map/filter/reduce
|
||||
│ ├── macros.clj # defmacro, ->, ->>
|
||||
│ ├── interop.clj # Nim interop demo
|
||||
│ ├── ffi.clj # C FFI demo
|
||||
│ └── ai_tools.clj # File + Git operations
|
||||
├── tests/
|
||||
│ ├── test_reader.nim
|
||||
│ └── test_emitter.nim
|
||||
├── docs/ # Full bilingual documentation (EN + BG)
|
||||
├── Makefile
|
||||
└── cljnim.nimble
|
||||
```
|
||||
|
||||
## Key Files for AI Agents
|
||||
|
||||
| If you want to... | Modify this file |
|
||||
|---|---|
|
||||
| Add a Clojure function | `lib/cljnim_runtime.nim` + `src/emitter.nim` |
|
||||
| Add a special form | `src/emitter.nim` |
|
||||
| Add a macro | `src/macros.nim` |
|
||||
| Add REPL command | `src/repl.nim` |
|
||||
| Fix a reader bug | `src/reader.nim` |
|
||||
| Add file/git op | `lib/cljnim_runtime.nim` + `src/emitter.nim` |
|
||||
|
||||
## How the compiler works (in 5 steps)
|
||||
|
||||
1. **Reader** (`src/reader.nim`): Text → `CljVal` AST
|
||||
2. **Macros** (`src/macros.nim`): Expands macros on `CljVal` AST
|
||||
3. **Emitter** (`src/emitter.nim`): `CljVal` AST → Nim source code
|
||||
4. **Nim compiler**: Nim → C
|
||||
5. **C compiler**: C → binary
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
Every change MUST be tested with:
|
||||
1. `make test` — runs Nim unit tests
|
||||
2. `make check` — runs examples
|
||||
3. Manual REPL test if relevant: `./cljnim repl --json`
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
1. **Forgetting `initBuiltinMacros()`**: If you add a macro but it doesn't expand, check that `initBuiltinMacros()` is called before compilation. It's called in `src/cljnim.nim` and `src/repl.nim`.
|
||||
|
||||
2. **Not adding to `runtimeName`**: If you add a runtime function in `lib/cljnim_runtime.nim` but Clojure can't find it, add the name mapping in `src/emitter.nim` → `runtimeName()`.
|
||||
|
||||
3. **String escaping**: Clojure strings with `\n` or quotes may break Nim code generation. The emitter needs to escape strings properly.
|
||||
|
||||
## Communication Protocol
|
||||
|
||||
When you finish a task, update:
|
||||
1. `TASKS.md` — mark the task as done
|
||||
2. `docs/ROADMAP.md` — update if phase completed
|
||||
3. Commit with descriptive message
|
||||
|
||||
## Questions?
|
||||
|
||||
Read `docs/ARCHITECTURE.md` for deep technical details.
|
||||
Read `docs/API.md` for REPL protocol reference.
|
||||
Read `docs/GUIDE.md` for user-facing features.
|
||||
Reference in New Issue
Block a user