178 lines
6.8 KiB
Markdown
178 lines
6.8 KiB
Markdown
# AGENTS.md — tricu Project Guide
|
||
|
||
> For AI agents and contributors working in this repository.
|
||
|
||
## 1. Build & Test
|
||
|
||
**`nix build .#` always runs tests.** This is the primary and only way to build and validate.
|
||
|
||
```bash
|
||
# Full build + tests (this is the default)
|
||
nix build .#
|
||
|
||
# Build only (skip tests)
|
||
nix build .#package
|
||
|
||
# Build the test-specific variant with doCheck enforced
|
||
nix build .#test
|
||
nix flake check
|
||
|
||
# Dev shell (includes ghcid, cabal-install, ghc, upx)
|
||
nix develop .#
|
||
```
|
||
|
||
### ⚠️ Never call `cabal` directly
|
||
|
||
This project uses a Nix flake that wraps `callCabal2nix` to produce the cabal package. All compilation, linking, and test execution are driven through Nix. Running `cabal build`, `cabal test`, `cabal repl`, or `cabal install` directly will use the system GHC (or `.stack-work`) and can produce artifacts that differ from the Nix-built ones — especially regarding `megaparsec` which is a project dependency.
|
||
|
||
> **Rule of thumb:** if it builds, links, or tests, it goes through `nix`.
|
||
|
||
## 2. Project Overview
|
||
|
||
**tricu** (pronounced "tree-shoe") is a programming-language experiment written in Haskell. It implements [Triage Calculus](https://olydis.medium.com/a-visual-introduction-to-tree-calculus-2f4a34ceffc2), an extension of Barry Jay's Tree Calculus, with lambda-abstraction sugar that gets eliminated back to pure tree calculus terms.
|
||
|
||
tricu is Lojban for "tree".
|
||
|
||
### Core types (in `src/Research.hs`)
|
||
|
||
| Type | Description |
|
||
|------|-------------|
|
||
| `T = Leaf \| Stem T \| Fork T T` | Tree Calculus term (the runtime value) |
|
||
| `TricuAST` | Parsed AST with `SDef`, `SApp`, `SLambda`, etc. |
|
||
| `LToken` | Lexer tokens |
|
||
| `Node` / `MerkleHash` | Content-addressed Merkle DAG nodes |
|
||
|
||
### Source modules
|
||
|
||
| Module | Purpose |
|
||
|--------|---------|
|
||
| `Main.hs` | CLI entry point (`cmdargs`), three modes: `repl`, `eval`, `decode` |
|
||
| `Eval.hs` | Interpreter: `evalTricu`, `result`, `evalSingle` |
|
||
| `Parser.hs` | Megaparsec parser → `TricuAST` |
|
||
| `Lexer.hs` | Megaparsec lexer → `LToken` |
|
||
| `FileEval.hs` | File loading, module imports, `!import` |
|
||
| `REPL.hs` | Interactive Read-Eval-Print Loop (haskeline) |
|
||
| `Research.hs` | Core types, `apply` reduction, booleans, marshalling (`ofString`, `ofNumber`), output formatters (`toAscii`, `toTernaryString`, `decodeResult`) |
|
||
| `ContentStore.hs` | SQLite-backed term persistence |
|
||
|
||
### File extensions
|
||
|
||
- `.hs` — Haskell source
|
||
- `.tri` — tricu language source (used in `lib/`, `test/`, `demos/`)
|
||
|
||
## 3. Test Suite
|
||
|
||
Tests live in `test/Spec.hs` and use **Tasty** + **HUnit**.
|
||
|
||
```bash
|
||
nix flake check # or: nix build .#test
|
||
```
|
||
|
||
### Test groups
|
||
|
||
| Group | What it covers |
|
||
|-------|----------------|
|
||
| `lexer` | Megaparsec lexer — identifiers, keywords, strings, escapes, invalid tokens |
|
||
| `parser` | Parser — defs, lambda, applications, lists, comments, parentheses |
|
||
| `simpleEvaluation` | Core `apply` reduction rules, variable substitution, immutability |
|
||
| `lambdas` | Lambda elimination, SKI calculus, higher-order functions, currying, shadowing, free vars |
|
||
| `providedLibraries` | `lib/list.tri` — triage, booleans, list ops (`head`, `tail`, `map`, `emptyList?`, `append`, `equal?`) |
|
||
| `fileEval` | Loading `.tri` files, multi-file context, decode |
|
||
| `modules` | `!import`, cyclic deps, namespacing, multi-level imports, unresolved vars, local namespaces |
|
||
| `demos` | `demos/*.tri` — structural equality, `toSource`, `size`, level-order traversal |
|
||
| `decoding` | `decodeResult` — Leaf, numbers, strings, lists, mixed |
|
||
| `elimLambdaSingle` | Lambda elimination: eta reduction, SDef binding, semantics preservation |
|
||
| `stressElimLambda` | Lambda elimination stress test: 200 vars, 800-body curried lambda |
|
||
|
||
### Adding tests
|
||
|
||
1. Append a `testCase "Description" $ do ...` block to the appropriate test group in `test/Spec.hs`.
|
||
2. Import any modules you need (lexer/parser are available via `runParser` from `Text.Megapparsec`; evaluation via `evalTricu`, `parseTricu`, `result`).
|
||
3. Run `nix flake check` to verify.
|
||
|
||
> The test-suite in `tricu.cabal` pulls in `src/` as `hs-source-dirs`, so tests import modules directly (e.g., `import Eval`, `import Lexer`). This is intentional — tests exercise the full pipeline end-to-end.
|
||
|
||
## 4. tricu Language Quick Reference
|
||
|
||
```
|
||
t → Leaf (the base term)
|
||
t t → Stem Leaf
|
||
t t t → Fork Leaf Leaf
|
||
|
||
x = t → Define term x = Leaf
|
||
id = (a : a) → Lambda identity (eliminates to tree calculus)
|
||
head (map f xs) → From lib/list.tri
|
||
|
||
!import "./path.tri" NS → Import file under namespace
|
||
|
||
-- line comment
|
||
|- block comment -|
|
||
```
|
||
|
||
## 5. Output Formats
|
||
|
||
The `eval` command accepts `--form` (shorthand `-t`):
|
||
|
||
| Format | Value | Description |
|
||
|--------|-------|-------------|
|
||
| `tree` | `TreeCalculus` | Simple `t` form (default) |
|
||
| `fsl` | `FSL` | Full show representation |
|
||
| `ast` | `AST` | Parsed AST representation |
|
||
| `ternary` | `Ternary` | Ternary string encoding |
|
||
| `ascii` | `Ascii` | ASCII-art tree diagram |
|
||
| `decode` | `Decode` | Human-readable (strings, numbers, lists) |
|
||
|
||
## 6. Content Addressing
|
||
|
||
Each `T` term is content-addressed via a Merkle DAG:
|
||
|
||
```
|
||
NLeaf → 0x00
|
||
NStem(h) → 0x01 || h (32 bytes)
|
||
NFork(l,r) → 0x02 || l (32 bytes) || r (32 bytes)
|
||
|
||
hash = SHA256("tricu.merkle.node.v1" <> 0x00 <> serialized_node)
|
||
```
|
||
|
||
This is stored in SQLite via `ContentStore.hs`. Hash suffixes on identifiers (e.g., `foo_abc123...`) are validated: 16–64 hex characters (SHA256).
|
||
|
||
## 7. Directory Layout
|
||
|
||
```
|
||
tricu/
|
||
├── flake.nix # Nix flake: packages, tests, devShell
|
||
├── tricu.cabal # Cabal package (used via callCabal2nix)
|
||
├── src/ # Haskell modules
|
||
│ ├── Main.hs
|
||
│ ├── Eval.hs
|
||
│ ├── Parser.hs
|
||
│ ├── Lexer.hs
|
||
│ ├── FileEval.hs
|
||
│ ├── REPL.hs
|
||
│ ├── Research.hs
|
||
│ └── ContentStore.hs
|
||
├── test/
|
||
│ ├── Spec.hs # Tasty + HUnit tests
|
||
│ ├── *.tri # tricu test programs
|
||
│ └── local-ns/ # Module namespace test files
|
||
├── lib/
|
||
│ ├── base.tri
|
||
│ ├── list.tri
|
||
│ └── patterns.tri
|
||
├── demos/
|
||
│ ├── equality.tri
|
||
│ ├── size.tri
|
||
│ ├── toSource.tri
|
||
│ ├── levelOrderTraversal.tri
|
||
│ └── patternMatching.tri
|
||
└── AGENTS.md # This file
|
||
```
|
||
|
||
## 8. Development Tips
|
||
|
||
- **Quick iteration:** `nix develop` then `ghcid` (provided in the devShell) watches files and re-runs.
|
||
- **REPL:** `nix run .#` starts the interactive REPL.
|
||
- **Evaluate files:** `nix run .# -- eval -f demos/equality.tri`
|
||
- **GHC options:** `-threaded -rtsopts -with-rtsopts=-N` for parallel runtime. Use `-N` RTS flag for multi-core.
|
||
- **Upx** is in the devShell for binary compression if needed.
|