# 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.