Skip to content

Commit a2ad1fe

Browse files
committed
docs: add CLAUDE.md with project architecture and development guide
Comprehensive documentation covering: - Build system and common development commands - Complete compilation pipeline architecture (CoreFn -> IR -> Lua) - Code style conventions (Fourmolu, HLint, unicode syntax) - Testing strategy with golden tests and property-based tests - Development workflow and debugging tips This provides essential context for understanding the multi-stage compiler architecture and project conventions.
1 parent 684b8d2 commit a2ad1fe

File tree

1 file changed

+284
-0
lines changed

1 file changed

+284
-0
lines changed

CLAUDE.md

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
This is `pslua` - a PureScript to Lua compiler backend. It takes PureScript CoreFn (the PureScript compiler's intermediate representation) and compiles it to Lua. The project supports dead code elimination (DCE), code inlining, FFI with Lua, and can emit either Lua modules or standalone applications.
8+
9+
## Build System & Commands
10+
11+
The project uses **Nix with flakes** for reproducible builds and **Cabal** for Haskell development.
12+
13+
### Development Environment
14+
15+
```bash
16+
# Enter development shell (provides all tools)
17+
nix develop
18+
19+
# Or use direnv (if configured)
20+
direnv allow
21+
```
22+
23+
### Building
24+
25+
```bash
26+
# Build the project
27+
cabal build
28+
29+
# Build specific component
30+
cabal build exe:pslua
31+
cabal build lib:pslua
32+
```
33+
34+
### Testing
35+
36+
```bash
37+
# Run all tests with detailed output
38+
cabal test all --test-show-details=direct
39+
40+
# Run specific test suite
41+
cabal test spec
42+
43+
# Run tests in watch mode (requires ghcid)
44+
ghcid --command="cabal repl test:spec" --test=":main"
45+
```
46+
47+
The test suite includes:
48+
- **Unit tests**: Property-based testing with Hedgehog
49+
- **Golden tests**: Compiles PureScript test modules from `test/ps/golden/Golden/*/Test.purs` to Lua and compares against golden files
50+
- **Evaluation tests**: Runs generated Lua code and verifies output
51+
- **Luacheck tests**: Validates generated Lua code syntax
52+
53+
### Testing PureScript Code
54+
55+
Golden tests require compiling PureScript sources first:
56+
57+
```bash
58+
# Compile PureScript test sources (from test/ps directory)
59+
cd test/ps
60+
spago build -u '-g corefn'
61+
cd ../..
62+
```
63+
64+
### Resetting Golden Files
65+
66+
```bash
67+
# Remove all golden files and regenerate them
68+
./scripts/golden_reset
69+
```
70+
71+
This finds all files named `golden.*` in `test/ps/output` and deletes them, then runs `cabal test` to regenerate them.
72+
73+
### Code Formatting & Linting
74+
75+
```bash
76+
# Format Haskell code with Fourmolu
77+
fourmolu -i lib/ exe/ test/
78+
79+
# Or use treefmt to format all files
80+
treefmt
81+
82+
# Run HLint
83+
hlint lib/ exe/ test/
84+
85+
# Check Lua files
86+
luacheck --quiet --std min test/ps/output/
87+
```
88+
89+
### Running the Compiler
90+
91+
```bash
92+
# Build and run
93+
cabal run pslua -- --help
94+
95+
# Or after building
96+
./dist-newstyle/build/x86_64-linux/ghc-9.8.1/pslua-0.1.0.0/x/pslua/build/pslua/pslua --help
97+
98+
# Or via nix
99+
nix run . -- --help
100+
```
101+
102+
Typical usage:
103+
```bash
104+
pslua \
105+
--foreign-path ./foreign \
106+
--ps-output ./output \
107+
--lua-output-file ./dist/Main.lua \
108+
--entry Main.main
109+
```
110+
111+
## Code Architecture
112+
113+
### Compilation Pipeline
114+
115+
The compilation happens in several distinct phases:
116+
117+
```
118+
PureScript Source → CoreFn → IR → Lua → Optimized Lua
119+
```
120+
121+
1. **CoreFn Reading** (`Language.PureScript.CoreFn.*`)
122+
- Reads PureScript compiler's CoreFn JSON output
123+
- Parses module structure, imports, and expressions
124+
125+
2. **IR Translation** (`Language.PureScript.Backend.IR.*`)
126+
- Converts CoreFn to an intermediate representation (IR)
127+
- IR is simpler than CoreFn but still high-level
128+
- Handles data declarations, bindings, and module structure
129+
130+
3. **IR Optimization** (`Language.PureScript.Backend.IR.Optimizer`)
131+
- Performs optimizations on IR:
132+
- Eta reduction/expansion
133+
- Beta reduction
134+
- Constant folding
135+
- Case-of-case transformation
136+
- **Inliner** (`IR.Inliner`): Marks expressions for inlining
137+
- **Dead Code Elimination** (`IR.DCE`): Removes unused bindings
138+
139+
4. **Linking** (`Language.PureScript.Backend.IR.Linker`)
140+
- Creates an "UberModule" containing all reachable code
141+
- Supports two modes:
142+
- `LinkAsModule`: Creates a Lua module (returns a table)
143+
- `LinkAsApplication`: Creates a runnable Lua script (calls entry point)
144+
145+
5. **Lua Code Generation** (`Language.PureScript.Backend.Lua`)
146+
- Converts optimized IR to Lua AST
147+
- Handles foreign imports via FFI
148+
- Manages name mangling and scope
149+
150+
6. **Lua Optimization** (`Language.PureScript.Backend.Lua.Optimizer`)
151+
- Optimizes generated Lua code
152+
153+
7. **Lua Printing** (`Language.PureScript.Backend.Lua.Printer`)
154+
- Pretty-prints Lua AST to text
155+
156+
### Key Module Structure
157+
158+
**CoreFn Layer** (`Language.PureScript.CoreFn.*`):
159+
- `CoreFn.Reader`: Reads CoreFn JSON from disk
160+
- `CoreFn.FromJSON`: JSON deserialization
161+
- `CoreFn.Expr`: CoreFn expression types
162+
- `CoreFn.Traversals`: Traversal utilities
163+
164+
**IR Layer** (`Language.PureScript.Backend.IR.*`):
165+
- `IR.Types`: Core IR data types (`RawExp`, `Module`, `Binding`)
166+
- `IR.Names`: Name types (`Qualified`, `ModuleName`, etc.)
167+
- `IR.Linker`: Creates UberModule from multiple modules
168+
- `IR.Optimizer`: IR-level optimizations
169+
- `IR.DCE`: Dead code elimination
170+
- `IR.Inliner`: Inlining annotations and logic
171+
172+
**Lua Backend** (`Language.PureScript.Backend.Lua.*`):
173+
- `Lua.Types`: Lua AST types (`Chunk`, `Statement`, `Exp`)
174+
- `Lua.Name`: Safe Lua identifier generation
175+
- `Lua.Printer`: Pretty-printing Lua code
176+
- `Lua.Optimizer`: Lua-level optimizations
177+
- `Lua.DCE`: Lua-specific DCE
178+
- `Lua.Linker.Foreign`: FFI support for Lua foreign modules
179+
- `Lua.Fixture`: Runtime support code injected into output
180+
181+
**Main Entry** (`Language.PureScript.Backend`):
182+
- `compileModules`: Top-level compilation function orchestrating the pipeline
183+
184+
### Important Concepts
185+
186+
**De Bruijn Indices**: The IR uses De Bruijn indices for variable references. A `Ref` contains:
187+
- A qualified name
188+
- An index indicating which binding of that name to reference
189+
190+
**Groupings**: Bindings are wrapped in `Grouping`:
191+
- `Standalone`: Non-recursive binding
192+
- `RecursiveGroup`: Mutually recursive bindings
193+
194+
**AppOrModule**: Determines compilation mode:
195+
- `AsModule ModuleName`: Generate a Lua module
196+
- `AsApplication ModuleName Ident`: Generate an executable that calls the entry point
197+
198+
**UberModule**: A flattened representation of all modules after linking, containing:
199+
- All reachable bindings
200+
- Module exports
201+
- Foreign imports
202+
203+
## Code Style
204+
205+
### Haskell Style
206+
207+
This project follows specific Haskell style conventions:
208+
209+
- **Indentation**: 2 spaces (enforced by Fourmolu)
210+
- **Line length**: Max 80 characters
211+
- **Unicode**: Always use unicode syntax (`` instead of `::`, `` instead of `->`)
212+
- **Prelude**: Uses Relude (not base Prelude)
213+
- **Imports**: Explicit qualified imports preferred
214+
- **Extensions**: Many enabled by default (see `pslua.cabal` common stanza)
215+
216+
### Formatting Configuration
217+
218+
- **Fourmolu** (`fourmolu.yaml`):
219+
- 2-space indentation
220+
- 80-character column limit
221+
- Leading commas and function arrows
222+
- Unicode always
223+
- Multi-line Haddock style
224+
225+
- **HLint** (`.hlint.yaml`):
226+
- Configured with project-specific extensions
227+
- Run with `--color --cpp-simple -XQuasiQuotes -XImportQualifiedPost`
228+
229+
### Section Comments
230+
231+
Use section-style comments to organize code:
232+
233+
```haskell
234+
--------------------------------------------------------------------------------
235+
-- Section Title ---------------------------------------------------------------
236+
237+
code here...
238+
```
239+
240+
Both lines should be exactly 80 characters. Helper functions go at the bottom after a "Helper Functions" or "Utility Functions" section.
241+
242+
## Testing Strategy
243+
244+
### Golden Tests
245+
246+
Golden tests are the primary integration testing mechanism:
247+
248+
1. PureScript test files in `test/ps/golden/Golden/*/Test.purs`
249+
2. Compiled to CoreFn with `spago build -u '-g corefn'`
250+
3. Test suite reads CoreFn, compiles to IR, generates Lua
251+
4. Compares against golden files:
252+
- `golden.ir` - Intermediate representation
253+
- `golden.lua` - Generated Lua code
254+
- `eval/golden.txt` - Execution output (if module has a `main` function)
255+
256+
To add a new golden test:
257+
1. Create `test/ps/golden/Golden/NewTest/Test.purs`
258+
2. Run `cabal test` - it will fail and create `actual.*` files
259+
3. Review the actual files
260+
4. Rename `actual.*` to `golden.*` if correct
261+
5. Commit the golden files
262+
263+
### Property-Based Tests
264+
265+
The project uses Hedgehog for property-based testing:
266+
- Generators in `test/Language/PureScript/Backend/IR/Gen.hs`
267+
- Tests in `test/Language/PureScript/Backend/IR/Spec.hs` and similar
268+
269+
## Development Workflow
270+
271+
1. Make code changes in `lib/` or `exe/`
272+
2. Format code: `fourmolu -i lib/ exe/ test/`
273+
3. Run HLint: `hlint lib/ exe/ test/`
274+
4. Run tests: `cabal test all --test-show-details=direct`
275+
5. If golden tests fail, inspect `actual.*` files in `test/ps/output/`
276+
6. Update golden files if changes are correct
277+
278+
## Debugging Tips
279+
280+
- The IR and Lua types have `Show` instances - use `pShowOpt` for pretty debug output
281+
- Golden test failures show diffs between expected and actual
282+
- Use `actual.*` files alongside `golden.*` files to debug compilation issues
283+
- Check `test/ps/output/Golden.*/` directories for generated IR and Lua
284+
- Lua evaluation errors are captured in the test output

0 commit comments

Comments
 (0)