packages feed

aihc-cpp-2.0.0.0: CHANGELOG.md

# Changelog

All notable changes to `aihc-cpp` will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [2.0.0.0] - 2026-09-10

### Performance

- Preprocessing the whole Stackage `lts-24.58` corpus is about 3.5x faster —
  one sweep over 5,802 CPP-using modules went from 3.07 s to 0.87 s on the
  same machine, with byte-identical output and diagnostics for every module.
  Net of the file reads every tool pays for, that is 4.5x faster than `cpphs`,
  against 1.12x before. The changes:
  - Macro expansion now scans byte offsets and copies nothing until a macro
    actually expands, so a line that names no macro is returned as the very
    `ByteString` that came in. Previously every line of every module was
    rebuilt one character at a time through a `Builder`.
  - A 64-bit first-byte filter over the macro names rejects an identifier
    that can name no macro without walking the macro map, which is the
    outcome for nearly every identifier in a Haskell module.
  - Token pasting in a function-like macro body no longer appends to the end
    of a list once per token, which was quadratic in the body length and the
    single largest source of allocation.
  - The GCC string-continuation check tests the two trailing bytes of a line
    before scanning it for quote state, removing a full scan from every line;
    and a module that defines no function-like macro skips the multi-line
    call lookahead entirely.
  - Splitting the input into lines uses `memchr` rather than a byte-at-a-time
    walk that allocated a cursor per byte of the input.
  - The line scanner works on byte offsets rather than a cursor per byte, and
    a line that opens no comment — nearly every line — is recognised by a
    loop over unboxed arguments and returned as a single span, skipping the
    accumulator-threading scanner entirely.
  - Loop-carried `where` bindings that only some branches use were thunks
    allocated once per byte and once per identifier scanned; they are now
    forced or pushed into the branch that needs them.

### Changed

- **Breaking:** the preprocessor is now agnostic to the source encoding.
  `resultOutput` is a `ByteString` rather than `Text`, and `configMacros`
  is keyed by `ByteString`. Bytes the preprocessor did not generate itself
  are copied from input to output verbatim, so a module in any encoding —
  or in no consistent encoding — passes through unchanged. Nothing but
  `Diagnostic` message text is ever decoded.

  To migrate, decode at the boundary if you want `Text`:
  `Data.Text.Encoding.decodeUtf8With Data.Text.Encoding.Error.lenientDecode (resultOutput r)`.

### Fixed

- `preprocess` no longer throws an impure exception on source that is not
  valid UTF-8 (for example a Latin-1 encoded module containing byte `0xa9`,
  as shipped in Ebnf2ps). Previously `Data.Text.Encoding.decodeUtf8` raised
  from inside a pure function, escaping the `Diagnostic` mechanism the API
  otherwise uses; such bytes now simply pass through. GHC accepts an
  undecodable byte in a comment and rejects one where a token must be
  lexed, so this leaves the encoding decision to the compiler front-end
  instead of failing modules that genuinely compile.
- Whitespace and identifier classification is now ASCII-only. Using
  `Data.Char.isSpace` on a byte treated `0xA0` — an ordinary UTF-8
  continuation byte — as whitespace, which could split a multi-byte
  character in half.
- Macro arguments are now expanded before substitution, so a function-like
  macro invocation produced by an expansion is rescanned and expanded, matching
  GHC's C preprocessor and `cpphs`. The C standard's non-recursive-expansion
  rule is honoured, so a macro is never expanded inside its own expansion.
- A pragma nested inside a Haskell block comment no longer terminates that
  comment. `{-#` is treated as a pragma delimiter only outside a comment;
  inside one it counts as an ordinary nested `{-`, balancing the `-}` of the
  closing `#-}`. Previously each such pragma decremented the comment depth,
  making CPP directives in the rest of the commented-out region live —
  producing spurious `unmatched #endif` warnings and, with a commented-out
  `#if 0`, silently dropping the comment's contents
  ([#1](https://github.com/ai-haskell-compiler/aihc-cpp/issues/1)).

## [1.0.0.3] - 2026-07-26

### Changed

- Moved development to the standalone
  [`ai-haskell-compiler/aihc-cpp`](https://github.com/ai-haskell-compiler/aihc-cpp)
  repository, including the full test and compatibility CI configuration.

## [1.0.0.2] - 2026-05-27

### Fixed

- Removed the internal `cpp-progress` executable from the published Cabal
  package so Hackage lists `aihc-cpp` as library-only.
- Included the CPP progress fixtures in the source distribution so Hackage can
  run the package test suite and report coverage.

## [1.0.0.1] - 2026-05-27

### Fixed

- Marked the internal `cpp-progress` executable as private so Hackage lists
  `aihc-cpp` as a library-only package.

## [1.0.0.0] - 2026-05-27

### Added

- Initial stable release of the pure Haskell CPP package.
- Public preprocessing API with deterministic configuration, diagnostics,
  include continuations, and preprocessing results.
- Support for object-like and function-like macros, includes, conditionals,
  diagnostics, line directives, token pasting, stringification, predefined
  macro handling, and comment-aware scanning.
- Oracle-backed progress suite against cpphs with the current `46/46`
  implemented baseline.