# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with
code in this repository.

## Project Overview

MWrap is a MEX interface generator: from annotated `.mw` interface files it
produces a C MEX gateway (`-c out.c`) and MATLAB/Octave caller scripts
(`-m`/`-mb`) for wrapping C/C++/Fortran/CUDA functions. There are two
implementations that must produce **byte-identical output**:

- `src/` — the original C++ implementation (Flex/Bison)
- `python/` — a pure-Python port (Python 3.7+, no dependencies, no build step)

The processing pipeline in both is: Lexer → Parser → Type Checker → Code
Generator. File correspondence:

| C++ | Python |
|---|---|
| `src/mwrap.l` (flex) | `python/mwrap_lexer.py` |
| `src/mwrap.y.in` → `mwrap.y` (bison grammar + `main`) | `python/mwrap_parser.py`, `python/mwrap` |
| `src/mwrap-typecheck.cc` | `python/mwrap_typecheck.py` |
| `src/mwrap-cgen.cc` | `python/mwrap_cgen.py` |
| `src/mwrap-mgen.cc` | `python/mwrap_mgen.py` |
| `src/mwrap-ast.{h,cc}` | `python/mwrap_ast.py` |
| `src/mwrap-support.c` (runtime helpers) | `python/mwrap_support.c` |

## Critical invariants — read before editing

1. **Generator parity.** Any change to emitted text in `src/mwrap-cgen.cc` or
   `src/mwrap-mgen.cc` must be mirrored byte-for-byte in
   `python/mwrap_cgen.py` / `python/mwrap_mgen.py` (and vice versa), including
   whitespace. The oracle is:
   ```bash
   bash testing/test_python.sh ./mwrap python/mwrap
   ```
   All checks must pass after any generator or typecheck change.

2. **Support-code sync.** `src/mwrap-support.c` and `python/mwrap_support.c`
   must remain **byte-identical** (`diff` them after editing either). The C++
   build embeds this code via `src/mwrap-support.h`, which is **generated**
   (not tracked) by `src/stringify` from `mwrap-support.c` — never edit the
   `.h`. The Python port reads `python/mwrap_support.c` at runtime.

3. **Version single-sourcing.** The version lives in exactly two constants:
   `src/mwrap-version.h` (`MWRAP_VERSION`) and `python/mwrap_version.py`
   (`__version__`). Both the `--help` banner and the
   `/* Code generated by mwrap X.Y.Z */` stamp in every generated file derive
   from them; the doc title is substituted at build time from the header.
   Because the stamp is part of the generated output, the parity suite fails
   if the two constants ever disagree. A release bumps these two files plus
   the NEWS heading — nothing else.

4. **Grammar file generation.** `src/mwrap.y` is generated from
   `src/mwrap.y.in` (a `sed` substitutes `@ERROR_VERBOSE@` for the local Bison
   version). Edit `mwrap.y.in`, then run `make bin` to regenerate; both files
   are currently tracked, so commit them together. `src/mwrap.cc`,
   `src/lex.yy.c`, `src/mwrap.hh` are bison/flex outputs and gitignored.

5. **Typecheck error messages** are pinned by `testing/test_typecheck.ref` and
   must be identical in both implementations. To regenerate a `.ref` after an
   intentional change, run from the `testing/` directory (paths appear in the
   output):
   ```bash
   cd testing && ../mwrap -cppcomplex test_typecheck.mw 2> test_typecheck.ref
   ```
   The same applies to `test_syntax.ref` (note: Bison ≥ 3.6 prints
   "end of file" where the ref says `$end`; the CMake and Python harnesses
   normalize this).

## Build

### Makefile (primary for development)

```bash
make          # or `make bin`: builds ./mwrap (requires flex + bison)
make test     # builds mwrap, then generates, compiles (mkoctfile --mex),
              # and runs the test suite under Octave
```

`make.inc` selects the MEX backend (MATLAB `mex` vs Octave `mkoctfile --mex`)
and flags.

### CMake

```bash
mkdir -p build && cd build && cmake .. && cmake --build .
ctest                       # test_syntax + test_typecheck log tests
```

Options: `MWRAP_ENABLE_MATLAB_CLASSDEF` (adds `-DR2008OO` to MEX wrappers
compiled via `cmake/MwrapAddMex.cmake`), `MWRAP_BUILD_EXAMPLES`,
`MWRAP_COMPILE_MEX`, `MWRAP_MEX_BACKEND=MATLAB|OCTAVE|ALL`.
Complex support is not a build option — it is selected per run with the
`-c99complex` / `-cppcomplex` command-line flags.

### Python version

No build. Run directly: `python/mwrap -mex name -c out.c -m out.m input.mw`

## Tests

- `bash testing/test_python.sh ./mwrap python/mwrap` — the parity suite:
  reference-file checks (`test_syntax`, `test_typecheck`) plus byte-diff of
  C++ vs Python generated output for every feature area. Run this after any
  change to either implementation.
- `make test` (repo root) — Octave end-to-end: generates wrappers, compiles
  them with `mkoctfile --mex`, runs `testing/test_all.m`. Failures print
  `Failure: ...` lines (grep for them; the run itself does not exit nonzero
  on tassert failures).
- MATLAB: `testing/run_matlab_tests.m` (used by CI), or build the examples
  via CMake with `-DMWRAP_COMPILE_MEX=ON`.
- GPU tests (`testing/test_gpu*.mw`) need MATLAB + Parallel Computing Toolbox
  + CUDA, but `mwrap -gpu` **code generation** needs nothing special.

## Behavior contracts worth knowing

- mwrap exits nonzero and suppresses the C output file when any input file
  is unreadable or contains syntax or type errors; an unwritable `-m`/`-c`
  target is a hard error. Bison error recovery still reports every error in
  a file before failing.
- The generated MEX gateway trusts the generated `.m` wrappers to pass the
  right argument counts (no `nrhs` validation in stubs yet).
- Wrapped MEX files support both the pre-2018a split-complex API and the
  R2018a interleaved API (`MX_HAS_INTERLEAVED_COMPLEX`); generator changes
  must emit correct code for **both** halves of every `#if` pair. Octave
  compiles the old-API half only.
