diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 98782cf..265051a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -105,4 +105,41 @@ jobs:
path: build/cpp-repl
retention-days: 7
+ release:
+ name: Automatic Release
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ needs: build
+ runs-on: ubuntu-24.04
+ permissions:
+ contents: write
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Download build artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: cpp-repl-ubuntu-24.04-llvm22
+ path: ./artifact
+
+ - name: Prepare release archive
+ run: |
+ ls -lh ./artifact/cpp-repl
+ chmod +x ./artifact/cpp-repl
+ tar czf cpp-repl-auto-${{ github.sha }}-linux-x86_64.tar.gz -C ./artifact cpp-repl
+ sha256sum cpp-repl-auto-${{ github.sha }}-linux-x86_64.tar.gz | tee SHA256SUMS
+ cat SHA256SUMS
+
+ - name: Create Automatic GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ tag_name: auto-${{ github.sha }}
+ name: Automatic Release ${{ github.sha }}
+ target_commitish: ${{ github.sha }}
+ files: |
+ cpp-repl-*.tar.gz
+ SHA256SUMS
+ generate_release_notes: true
+ fail_on_unmatched_files: false
+
# Fast sanity job for PRs without full LLVM install (doc/lint only) could be added here
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 486c7c6..f148d8d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -2,6 +2,8 @@ name: Release
on:
push:
+ branches:
+ - main
tags:
- 'v*.*.*'
workflow_dispatch:
@@ -35,24 +37,32 @@ jobs:
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang-22 -DCMAKE_CXX_COMPILER=clang++-22 -DLLVM_DIR=/usr/lib/llvm-22/lib/cmake/llvm
cmake --build build -j $(nproc)
./build/cpp-repl --version
- tar czf cpp-repl-${{ github.ref_name }}-linux-x86_64.tar.gz -C build cpp-repl
- sha256sum cpp-repl-${{ github.ref_name }}-linux-x86_64.tar.gz | tee SHA256SUMS
+ if [[ "${{ github.ref }}" == refs/tags/* ]]; then
+ TAG="${{ github.ref_name }}"
+ else
+ TAG="auto-${{ github.sha }}"
+ fi
+ echo "TAG=$TAG" >> $GITHUB_ENV
+ tar czf cpp-repl-${TAG}-linux-x86_64.tar.gz -C build cpp-repl
+ sha256sum cpp-repl-${TAG}-linux-x86_64.tar.gz | tee SHA256SUMS
- name: Create GitHub Release
- if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2
with:
+ tag_name: ${{ env.TAG }}
+ name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || format('Automatic Release {0}', github.sha) }}
+ target_commitish: ${{ github.sha }}
files: |
cpp-repl-*.tar.gz
SHA256SUMS
generate_release_notes: true
fail_on_unmatched_files: false
- - name: Upload artifact (manual dispatch)
- if: ${{ !startsWith(github.ref, 'refs/tags/') }}
+ - name: Upload artifact (manual dispatch fallback)
+ if: failure()
uses: actions/upload-artifact@v4
with:
- name: cpp-repl-release-${{ github.ref_name || inputs.tag || 'manual' }}
+ name: cpp-repl-release-${{ env.TAG || github.ref_name || inputs.tag || 'manual' }}
path: |
cpp-repl-*.tar.gz
SHA256SUMS
diff --git a/.gitignore b/.gitignore
index 5710deb..eb60faf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ compile_commands.json
.DS_Store
.cache/
ccache/
+Testing/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f4a7d36..6202906 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -109,6 +109,7 @@ add_executable(cpp-repl
src/interpreter/interpreter.cpp
src/utils/version_detector.cpp
src/utils/bigint.cpp
+ src/utils/highlight.cpp
src/repl/session.cpp
src/cli/cli.cpp
)
diff --git a/README.md b/README.md
index 00e91fc..f9691ef 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,81 @@
-# cpp-repl
+# cpp-repl — C++ REPL like Python
-[](https://github.com/sergiorandria/cpp-repl/actions/workflows/ci.yml)
-[](https://github.com/sergiorandria/cpp-repl/actions/workflows/format.yml)
-[](https://github.com/sergiorandria/cpp-repl/actions/workflows/codeql.yml)
-[](https://opensource.org/licenses/MIT)
-[](https://en.cppreference.com)
-[](https://llvm.org)
-[](https://github.com/sergiorandria/cpp-repl/commits/main)
+
+
+
+ Python-like incremental REPL for C++ — no main(), just raw C++
+
-Low-level C++ REPL (like Python REPL) built on LLVM VM concept.
+
+
+
+
+
+
+
+
+
-- **VM core**: `llvm::orc::LLJIT` – in-process JIT, no optimization initially
-- **Frontend**: `clang::Interpreter` (incremental parser + ORC executor) for real C++ parsing
-- **REPL**: incremental state, value printing, diagnostics, `:commands`
+
+ Low-level C++ REPL built on the LLVM VM. • LLJIT + clang::Interpreter • O0 correctness-first • auto -std • BigInt • hot -I/-L/-l
+
-## Architecture (scalable)
+---
+
+## ⚡️ Why cpp-repl?
+
+| Traditional C++ | **cpp-repl** |
+|---|---|
+| Need `int main(){}` + recompile | **Just type raw C++** — `int x = 42; x+1` |
+| `g++ file.cpp && ./a.out` | **`./build/cpp-repl file.cpp`** or `:load file.cpp` |
+| Manual `-std=c++20` for concepts | **Auto-detects** `concept`/`requires`/`import` → switches to C++20/23 |
+| No Python-like ints | **`cpp_int` / `bigint`** arbitrary precision out of the box |
+| Absolute paths are painful | **`-I ./inc -I /abs/path -L ./lib -l mylib`** cmdline **and** interactive `:I`/`:L`/`:lib` |
+
+> Think `python` but for C++. Incremental state, value printing `(type) value`, diagnostics on stderr, history + undo.
+
+## 🎬 30-second demo
+
+```bash
+./build/cpp-repl
+cpp> int x = 42;
+cpp> x + 1
+(int) 43
+
+cpp> #include
+cpp> std::cout << "hi" << std::endl;
+hi
+
+cpp> auto f = [](double v){ return v*v; };
+cpp> f(234.23423948934894)
+(double) 54865.278...
+
+cpp> cpp_int a = cpp_int("123456789012345678901234567890");
+cpp> a * a
+123456789012345678901234567890 * 123456789012345678901234567890 = 152415...
+```
+
+Pipes & scripts work like Python:
+
+```bash
+echo 'int x=42; x+1' | ./build/cpp-repl
+./build/cpp-repl --no-interactive -e 'int x=5; x*2' -e 'x+10'
+./build/cpp-repl examples/hello.cpp --no-interactive
+echo 'cpp_int a=cpp_int("12345678901234567890"); a*a' | ./build/cpp-repl
+```
+
+## ✨ Features
+
+- **🧠 LLVM VM core** — `llvm::orc::LLJIT` in-process JIT, `LLVMContext`/`Module`/`IRBuilder`, pluggable `core::VM` interface (swap to `RemoteJIT`)
+- **🩺 Clang frontend** — `clang::Interpreter` incremental parser + ORC executor, real C++ parsing (not a toy)
+- **🔄 Incremental REPL** — `repl::Session` with `cpp>` / `...>` multiline, history, `:undo`, `:reset`, `:dump`, `:clear`
+- **🎯 Auto C++ version** — `C++17` default; upgrades to `C++20` on `concept`/`requires`/`co_await`/`<=>`/`consteval`, to `C++23` on `import`/`module`
+- **🔢 BigInt** — `boost::multiprecision::cpp_int` (`bigint` alias) + `mpz_int` if GMP present; strings wrapped as `cpp_int("...")` to avoid literal overflow
+- **📁 Includes & libs** — absolute + relative `-I`/`-L`/`-l`/`-D` on cmdline **and** live `:I ` / `:L ` / `:lib `; survives version upgrades
+- **🎨 Precise floats** — `float 9`, `double 17`, `long double 21` digits (`max_digits10`) instead of truncated `%6g`/`%8g`
+- **🧪 No optimizations** — always `-O0 -g -fno-exceptions -fno-rtti`, diagnostics → stderr, `llvm::Error` handling
+
+## 🏗️ Architecture (scalable)
```
User Input (raw C++ like Python, no main)
@@ -25,13 +86,13 @@ User Input (raw C++ like Python, no main)
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
-│ utils::VersionDetector (src/utils) │ ← auto-detects C++17/20/23 via keywords
+│ utils::VersionDetector (src/utils) │ ← auto-detects C++17/20/23
│ import, concept, requires, co_await │
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ interpreter::Interpreter (src/interpreter) │ ← wraps clang::Interpreter + ORC JIT
-│ + utils::BigIntSupport preamble │ auto -std=c++17/20/23, bigint
+│ + utils::BigIntSupport preamble │ auto -std, bigint, high-prec floats
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
@@ -40,18 +101,15 @@ User Input (raw C++ like Python, no main)
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
-│ repl::Session (src/repl) │ ← prompts, multiline, :commands, history
+│ repl::Session (src/repl) │ ← prompts, multiline, :commands
└─────────────────────────────────────────┘
```
-Scalable: each layer is an isolated module with interface, easy to swap VM (e.g. RemoteJIT),
-add new frontends, or extend utils (BigInt, version).
+Each layer is an isolated module with a stable interface — swap `LLJIT` for `RemoteJIT`, add a new frontend, or extend `utils` without touching the rest.
-No optimizations (`O0`) – correctness first.
+## 🚀 Build
-## Build
-
-Requires LLVM 22 + Clang 22 (with `clangInterpreter`).
+**Requires:** LLVM 22 + Clang 22 (with `clangInterpreter`)
```bash
cmake -B build -DCMAKE_BUILD_TYPE=Debug
@@ -59,7 +117,9 @@ cmake --build build -j
./build/cpp-repl
```
-## CLI (interpreter-like, no main)
+> Debug is default (`O0`). Release: `-DCMAKE_BUILD_TYPE=Release`.
+
+## 💻 CLI (interpreter-like, no `main`)
```bash
cpp-repl [options] [file ...]
@@ -77,7 +137,7 @@ cpp-repl [options] [file ...]
execute raw C++ file as script (like python script.py)
```
-Examples (no `int main()` required, just raw C++ like Python):
+**Examples — no `int main()` needed:**
```bash
./build/cpp-repl # interactive REPL, raw code
@@ -86,18 +146,22 @@ Examples (no `int main()` required, just raw C++ like Python):
./build/cpp-repl -e 'int x=5; x*2' --no-interactive # -e raw code
echo 'int x=42; x+1' | ./build/cpp-repl # pipe like python
echo 'cpp_int a = cpp_int("12345678901234567890"); a*a' | ./build/cpp-repl
+
# absolute & relative includes + libraries (cmdline)
./build/cpp-repl -I ./include -I /abs/path -L ./lib -l mylib -e '#include "myheader.h"'
./build/cpp-repl -I ./rel_include --library m --no-interactive -e 'extern "C" int mylib_add(int,int); mylib_add(2,3)'
```
-## REPL Commands (inside `cpp>`)
+## ⌨️ REPL Commands
+
+Inside `cpp>`:
```
:help :h show help
:quit :exit :q exit REPL
:dump dump history + current C++ version
:reset reset interpreter state
+:clear :cls :c clear output buffer / terminal screen
:load load raw C++ file
:lib load dynamic library (abs/rel, e.g. :lib ./lib/mylib.so)
:I add include search path (abs or rel, like -I)
@@ -106,7 +170,9 @@ echo 'cpp_int a = cpp_int("12345678901234567890"); a*a' | ./build/cpp-repl
:version show current -std version
```
-## Include & Library (absolute/relative, cmdline & interactive)
+Multiline: unbalanced `{ ( [` keeps buffering with `...>` prompt.
+
+## 📂 Include & Library (absolute/relative, cmdline & interactive)
Both **absolute** (`/usr/local/include/mylib.h`) and **relative** (`./include/mylib.h`, `../common/header.h`) are supported.
@@ -125,7 +191,7 @@ cpp-repl -I ./include -L ./lib -l mylib --library gmp -D DEBUG=1
cpp-repl -I ./include -L ./lib -l mylib examples/use_mylib.cpp --no-interactive
```
-**Interactive (inside REPL):**
+**Interactive:**
```cpp
cpp> :I ./rel_include // relative
@@ -140,10 +206,9 @@ cpp> extern "C" int mylib_add(int,int);
cpp> mylib_add(7,8) // (int) 1015
```
-Paths are stored and survive `C++` version upgrades (e.g. adding `:I` then `concept` → still keeps includes).
+> Paths are stored and survive C++ version upgrades (e.g. `:I` then `concept` → still keeps includes).
-
-## C++ Version Support (auto-detect)
+## 🧬 C++ Version Support (auto-detect)
Interpreter auto-detects required `-std`:
@@ -151,54 +216,52 @@ Interpreter auto-detects required `-std`:
- **C++20** if code contains `concept`, `requires`, `co_await`, `co_yield`, `char8_t`, `<=>`, `consteval`
- **C++23** if code contains `import`, `module`/`export`
-Example:
-
```cpp
cpp> template concept Addable = requires(T a,T b){ a+b; };
cpp> Addable auto x = 42; // auto-detects C++20, re-inits to -std=c++20
cpp> import std; // C++23
```
-No manual `-std` needed.
+No manual `-std` needed — upgrades are replay-safe (history is re-executed on new `CompilerInstance`).
-## BigInt – Very Large Numbers (GMP/mpz)
+## 🔢 BigInt — Very Large Numbers (GMP/mpz)
Like Python's arbitrary large ints, via `boost::multiprecision`:
```cpp
cpp> cpp_int a = cpp_int("1234567890123456789012345678901234567890");
cpp> cpp_int b = cpp_int("987654321098765432109876543210");
-cpp> cpp_int c = a * b; c // prints big int
+cpp> cpp_int c = a * b; c // prints big int via std::cout
cpp> bigint x = cpp_int("999999999999999999999999999999"); // alias
cpp> x * x
```
-`bigint` is `boost::multiprecision::cpp_int` (header-only). If GMP is installed,
-also `boost::multiprecision::mpz_int` / `gmp.hpp` is available and linked via `-lgmp -lgmpxx`.
+`bigint` is `boost::multiprecision::cpp_int` (header-only). If GMP is installed, also `mpz_int` / `gmp.hpp` is available and linked via `-lgmp -lgmpxx`. Large literals are auto-wrapped as `cpp_int("...")` to avoid `integer literal is too large`.
Demo: `examples/bigint.cpp`
-
-## Examples
+## 📚 Examples
See `examples/`:
-- `hello.cpp` – basic I/O and value printing
-- `functions.cpp` – incremental function definitions (persistence)
-- `class.cpp` – structs, classes, templates
+- `hello.cpp` — basic I/O and value printing
+- `functions.cpp` — incremental function definitions (persistence)
+- `class.cpp` — structs, classes, templates
+- `bigint.cpp` — big integers
+- `version_concept.cpp` — C++20 concepts
Load with `:load examples/hello.cpp` or `./build/cpp-repl examples/class.cpp`
-## Low-level VM detail
+## 🔬 Low-level VM detail
-- `include/cpp-repl/core/vm.h` + `src/core/vm.cpp` – raw `llvm::orc::LLJIT` wrapper via `core::VM` interface (pluggable, scalable). Manually builds LLVM IR (`LLVMContext`, `Module`, `IRBuilder`) and JIT-executes without Clang.
-- `include/cpp-repl/interpreter/interpreter.h` – `interpreter::Interpreter` wraps `clang::Interpreter` (incremental) + `VersionDetector` + `BigIntSupport` preamble. Handles raw code without `main`.
+- `include/cpp-repl/core/vm.h` + `src/core/vm.cpp` — raw `llvm::orc::LLJIT` wrapper via `core::VM` interface (pluggable). Manually builds LLVM IR (`LLVMContext`, `Module`, `IRBuilder`) and JIT-executes without Clang.
+- `include/cpp-repl/interpreter/interpreter.h` — `interpreter::Interpreter` wraps `clang::Interpreter` + `VersionDetector` + `BigIntSupport` preamble. Handles raw code without `main`.
- Legacy `src/vm.h` / `src/repl.h` kept as wrappers for backward compat.
-No optimizations: all builds are `-O0 -g -fno-exceptions -fno-rtti` to stay close to the VM.
+All builds are `-O0 -g -fno-exceptions -fno-rtti` to stay close to the VM.
-## Project Structure (scalable)
+## 🗂️ Project Structure (scalable)
```
include/cpp-repl/
@@ -209,24 +272,28 @@ include/cpp-repl/
cli/cli.h
src/
core/vm.cpp
- interpreter/interpreter.cpp
+ interpreter/interpreter.cpp # high-precision float printing (9/17/21 digits)
utils/version_detector.cpp, bigint.cpp
repl/session.cpp
cli/cli.cpp
- main.cpp (scalable, delegates to modules)
+ main.cpp (delegates to modules)
examples/
hello.cpp, functions.cpp, class.cpp, bigint.cpp, version_concept.cpp
```
-## CI & Quality
+## ✅ CI & Quality
-- **CI** (`ci.yml`): Ubuntu 24.04, LLVM 22, `Debug` + `Release`, smoke tests (`-e`, pipe, examples, BigInt, C++20 concepts)
+- **CI** (`ci.yml`): Ubuntu 24.04, LLVM 22, `Debug` + `Release`, smoke tests (`-e`, pipe, examples, BigInt, C++20 concepts, float precision)
- **Format** (`format.yml`): `clang-format-22` + whitespace + `cppcheck`
- **CodeQL** (`codeql.yml`): weekly security analysis (C++)
- **Release** (`release.yml`): builds tarball on `v*` tags via `softprops/action-gh-release`
Enable branch protection on `main` to require `Build (ubuntu-24.04, Debug, LLVM 22)` before merge.
-## License
+## 🤝 Contributing
+
+See [CONTRIBUTING.md](CONTRIBUTING.md). PRs welcome — run `clang-format` and `cmake --build build -j` before pushing.
+
+## 📄 License
MIT — see [LICENSE](LICENSE)
diff --git a/capture.png b/capture.png
new file mode 100644
index 0000000..a30581c
Binary files /dev/null and b/capture.png differ
diff --git a/include/cpp-repl/cli/cli.h b/include/cpp-repl/cli/cli.h
index 833603c..b13f7c9 100644
--- a/include/cpp-repl/cli/cli.h
+++ b/include/cpp-repl/cli/cli.h
@@ -5,23 +5,47 @@
namespace cpprepl {
namespace cli {
+/**
+ * @file cli.h
+ * @brief Command-line parsing for cpp-repl.
+ */
+
+/**
+ * @brief Parsed command-line options.
+ */
struct Options {
- bool showScaffold = false;
- bool noInteractive = false;
- bool showHelp = false;
- bool showVersion = false;
- std::vector execCodes;
- std::vector files;
- // Include / library support (absolute & relative)
- std::vector includePaths; // -I
- std::vector libraryPaths; // -L
- std::vector libraries; // -l
- std::vector defines; // -D
- std::vector extraArgs; // -- extra clang args
+ bool showScaffold = false; ///< Show low-level VM IR demo.
+ bool noInteractive = false; ///< Exit after -e/file execution.
+ bool showHelp = false; ///< Print help.
+ bool showVersion = false; ///< Print version.
+ bool noColor = false; ///< Disable colored prompt/timing.
+ std::vector execCodes; ///< Code from -e flags.
+ std::vector files; ///< Input files.
+ std::vector includePaths; ///< Include paths (-I).
+ std::vector libraryPaths; ///< Library search paths (-L).
+ std::vector libraries; ///< Libraries to link (-l).
+ std::vector defines; ///< Macro definitions (-D).
+ std::vector extraArgs; ///< Extra clang args.
};
+/**
+ * @brief Parse command-line arguments.
+ * @param argc Argument count.
+ * @param argv Argument values.
+ * @param err Output error message.
+ * @return Parsed options.
+ */
Options parse(int argc, char **argv, std::string &err);
+
+/**
+ * @brief Print help to stdout.
+ * @param prog Program name (argv[0]).
+ */
void printHelp(const char *prog);
+
+/**
+ * @brief Print version to stdout.
+ */
void printVersion();
} // namespace cli
diff --git a/include/cpp-repl/core/vm.h b/include/cpp-repl/core/vm.h
index 69b685d..12d1f18 100644
--- a/include/cpp-repl/core/vm.h
+++ b/include/cpp-repl/core/vm.h
@@ -7,20 +7,58 @@
namespace cpprepl {
namespace core {
-// Abstract VM interface – scalable, pluggable backends.
+/**
+ * @file vm.h
+ * @brief Low-level VM abstraction for JIT execution.
+ */
+
+/**
+ * @brief Abstract VM interface for pluggable JIT backends.
+ *
+ * Each backend implements module addition and symbol lookup.
+ * The default backend is LLJITVM.
+ */
class VM {
public:
virtual ~VM() = default;
+
+ /**
+ * @brief Initialize the VM and native target.
+ * @param err Output error message on failure.
+ * @return true on success.
+ */
virtual bool init(std::string &err) = 0;
+
+ /**
+ * @brief Add an LLVM IR module to the JIT.
+ * @param M LLVM module to add.
+ * @param Ctx LLVM context owning the module.
+ * @param err Output error message on failure.
+ * @return true on success.
+ */
virtual bool addModule(std::unique_ptr M,
std::unique_ptr Ctx,
std::string &err) = 0;
+
+ /**
+ * @brief Lookup a JIT symbol by name.
+ * @param name Symbol name as in IR.
+ * @return Executor address or error.
+ */
virtual llvm::Expected lookup(const std::string &name) = 0;
+
+ /**
+ * @brief Get the underlying LLJIT instance if available.
+ * @return Pointer to LLJIT or nullptr.
+ */
virtual llvm::orc::LLJIT *getLLJIT() = 0;
};
-// Low-level LLJIT implementation – the default VM.
-// No optimizations, O0, pure execution.
+/**
+ * @brief LLJIT-based VM implementation.
+ *
+ * Uses llvm::orc::LLJIT with O0, no optimizations, for correctness.
+ */
class LLJITVM : public VM {
public:
LLJITVM() = default;
diff --git a/include/cpp-repl/fix_np_headers.hpp b/include/cpp-repl/fix_np_headers.hpp
index 826ba89..de4661a 100644
--- a/include/cpp-repl/fix_np_headers.hpp
+++ b/include/cpp-repl/fix_np_headers.hpp
@@ -1,7 +1,10 @@
+/**
+ * @file fix_np_headers.hpp
+ * @brief Force-included fixes for Numpy-C-API headers.
+ *
+ * Handles NZERO/PZERO clashes and ProxyBase compatibility without modifying upstream headers.
+ */
#pragma once
-// Comprehensive fix for Numpy-C-API headers in cpp-repl
-// Force-included via -include before any user code
-// Handles NZERO/PZERO, vector, ProxyBase without modifying headers
// 1. NZERO/PZERO clash
#ifdef NZERO
diff --git a/include/cpp-repl/fix_proxy.hpp b/include/cpp-repl/fix_proxy.hpp
index c896c15..b1b602a 100644
--- a/include/cpp-repl/fix_proxy.hpp
+++ b/include/cpp-repl/fix_proxy.hpp
@@ -1,6 +1,9 @@
+/**
+ * @file fix_proxy.hpp
+ * @brief Patch for np::ProxyBase to support convert_to and operators.
+ */
#pragma once
-// Fix for np::ProxyBase with boost::multiprecision and other types
-// Force-included via -include before any user code to patch ProxyBase
+/** @brief Force-included fix for ProxyBase with boost::multiprecision. */
#include
#include
diff --git a/include/cpp-repl/interpreter/interpreter.h b/include/cpp-repl/interpreter/interpreter.h
index 4c01e70..f7ac165 100644
--- a/include/cpp-repl/interpreter/interpreter.h
+++ b/include/cpp-repl/interpreter/interpreter.h
@@ -12,8 +12,18 @@ class Interpreter;
namespace cpprepl {
namespace interpreter {
-// Scalable interpreter wrapper around clang::Interpreter.
-// Handles C++ version auto-detection and BigInt preamble injection.
+/**
+ * @file interpreter.h
+ * @brief Scalable interpreter wrapper around clang::Interpreter.
+ */
+
+/**
+ * @brief High-level REPL interpreter with version and BigInt support.
+ *
+ * Wraps clang::Interpreter (incremental parsing + ORC JIT), auto-detects
+ * C++ standard, injects BigInt preamble, and handles include/library paths.
+ * Supports incremental eval without a main function, like Python REPL.
+ */
class Interpreter {
public:
Interpreter();
@@ -22,56 +32,135 @@ class Interpreter {
Interpreter(const Interpreter &) = delete;
Interpreter &operator=(const Interpreter &) = delete;
- // Init with explicit version and include/lib options (absolute & relative)
+ /**
+ * @brief Init with explicit version and include/lib options.
+ * @param version Initial C++ standard.
+ * @param includePaths Include search paths (-I).
+ * @param defines Macro definitions (-D).
+ * @param err Output error.
+ * @return true on success.
+ */
bool init(utils::StdVersion version, const std::vector &includePaths,
const std::vector &defines, std::string &err);
+ /**
+ * @brief Init with full options.
+ * @param version Initial C++ standard.
+ * @param includePaths Include paths.
+ * @param defines Defines.
+ * @param libraryPaths Library search paths (-L).
+ * @param libraries Libraries to link (-l).
+ * @param err Output error.
+ * @return true on success.
+ */
bool init(utils::StdVersion version,
const std::vector &includePaths,
const std::vector &defines,
const std::vector &libraryPaths,
const std::vector &libraries,
std::string &err);
+ /** @brief Init with version only. */
bool init(utils::StdVersion version, std::string &err);
+ /** @brief Init with C++23 default. */
bool init(std::string &err) { return init(utils::StdVersion::Cpp23, err); }
+ /** @brief Init with includes/defines and C++23 default. */
bool init(const std::vector &includePaths,
const std::vector &defines, std::string &err) {
return init(utils::StdVersion::Cpp23, includePaths, defines, err);
}
- // Dynamic include/library handling (interactive :I, :L)
+ /**
+ * @brief Add an include search path at runtime.
+ * @param path Absolute or relative path.
+ * @param err Output error.
+ * @return true on success, re-inits and replays history.
+ */
bool addIncludePath(const std::string &path, std::string &err);
+ /**
+ * @brief Add a library search path at runtime.
+ * @param path Absolute or relative path.
+ * @param err Output error.
+ * @return true on success.
+ */
bool addLibraryPath(const std::string &path, std::string &err);
+ /**
+ * @brief Load a library by name or path.
+ * @param lib Library name or file path.
+ * @param err Output error.
+ * @return true on success.
+ */
bool addLibrary(const std::string &lib, std::string &err);
- // Auto-detect version from code and re-init if needed (scalable)
+ /**
+ * @brief Evaluate code with automatic version detection.
+ * @param code Raw C++ code.
+ * @param err Output error.
+ * @return true on success.
+ */
bool evalAuto(const std::string &code, std::string &err);
- // Core eval – python-like: no main needed, raw code
+ /**
+ * @brief Evaluate raw C++ code incrementally.
+ * @param code C++ source without main.
+ * @param err Output error.
+ * @return true on success.
+ */
bool eval(const std::string &code, std::string &err);
+ /**
+ * @brief Evaluate with incomplete-input detection.
+ * @param code Code buffer.
+ * @param err Output error.
+ * @param incomplete Set to true if braces/parens are unbalanced.
+ * @return true if handled.
+ */
bool eval(const std::string &code, std::string &err, bool &incomplete);
+ /** @brief Load and execute a raw C++ file. */
bool loadFile(const std::string &path, std::string &err);
+ /** @brief Load a dynamic library. */
bool loadLibrary(const std::string &path, std::string &err);
+ /**
+ * @brief Undo last N inputs.
+ * @param n Number of PTUs to remove.
+ * @param err Output error.
+ * @return true on success.
+ */
bool undo(unsigned n, std::string &err);
+ /** @brief Dump history and current version to stdout. */
void dump() const;
+ /** @brief Reset interpreter state. */
void reset(std::string &err);
+ /** @brief Print help text. */
void help() const;
+ /** @brief Number of history entries. */
size_t historySize() const { return history_.size(); }
+ /** @brief Current C++ standard. */
utils::StdVersion currentVersion() const { return currentVersion_; }
private:
+ /** @brief Ensure at least the needed C++ version, re-init if higher. */
bool ensureVersion(utils::StdVersion needed, std::string &err);
+ /** @brief Re-init with current options and replay history. */
bool reinitWithCurrentOptions(std::string &err);
+ /** @brief Sanitize include directives (e.g., strip trailing semicolon). */
std::string sanitizeIncludes(const std::string &code);
+ /** @brief Reject redefinition with different value. */
bool checkVariableRedefinition(const std::string &code, std::string &err);
+ /** @brief Track variable declarations for redefinition checks. */
void trackVariable(const std::string &code);
+ /** @brief Parse a declaration into type/name/value. */
bool parseDeclaration(const std::string &code, std::string &type,
std::string &name, std::string &value);
+ /** @brief Parse an assignment into name/value. */
bool parseAssignment(const std::string &code, std::string &name,
std::string &value);
+ /** @brief Normalize a value string for comparison. */
std::string normalizeValue(const std::string &v);
+ /** @brief Ensure standard library is available (bits/stdc++.h). */
+ bool ensureStdLib(std::string &err);
+ /** @brief Try to include bits/stdc++.h or fallback headers. */
+ bool tryIncludeStdLib();
std::unique_ptr interp_;
bool initialized_ = false;
std::vector history_;
@@ -80,10 +169,10 @@ class Interpreter {
std::vector defines_;
std::vector libraryPaths_;
std::vector libraries_;
- // Storage for compiler args c_str() lifetime
std::vector compilerArgsStorage_;
std::unordered_map> variables_;
std::vector>> varHistory_;
+ bool stdLibIncluded_ = false;
};
} // namespace interpreter
diff --git a/include/cpp-repl/repl/session.h b/include/cpp-repl/repl/session.h
index 0801be7..1e350a4 100644
--- a/include/cpp-repl/repl/session.h
+++ b/include/cpp-repl/repl/session.h
@@ -5,25 +5,75 @@
namespace cpprepl {
namespace repl {
-// Scalable REPL session – handles prompts, multiline, commands.
-// Like Python's REPL, but for C++ raw code.
+/**
+ * @file session.h
+ * @brief Interactive REPL session handling.
+ */
+
+/**
+ * @brief Manages the interactive prompt, history, and commands.
+ *
+ * Delegates execution to interpreter::Interpreter and provides Python-like
+ * REPL behavior for raw C++ input.
+ */
class Session {
public:
+ /**
+ * @brief Construct a session bound to an interpreter.
+ * @param interp Reference to initialized interpreter.
+ */
explicit Session(interpreter::Interpreter &interp);
~Session() = default;
- // Run interactive loop reading from stdin
+ /**
+ * @brief Run the interactive loop reading from stdin.
+ *
+ * Handles prompts cpp> / ...>, line buffering, and :commands.
+ */
void runInteractive();
- // Execute single line (used for -e and tests)
+ /**
+ * @brief Execute a single code snippet.
+ * @param code Raw C++ code.
+ * @param err Output error message.
+ * @return true on success.
+ */
bool exec(const std::string &code, std::string &err);
private:
+ /**
+ * @brief Handle a colon command like :help or :I.
+ * @param line Input line.
+ * @param err Output error.
+ * @return true if line was a command.
+ */
bool handleCommand(const std::string &line, std::string &err);
+ /**
+ * @brief Check if buffer has unbalanced braces/parens.
+ * @param buffer Current input buffer.
+ * @return true if more input is needed.
+ */
bool isIncomplete(const std::string &buffer) const;
- interpreter::Interpreter &interp_;
- std::string buffer_;
+ /** @brief Whether to use ANSI color for prompt. */
+ bool shouldUseColor(bool forReadline) const;
+ /** @brief Format duration in ms for display. */
+ std::string formatDuration(double ms) const;
+ /** @brief Build primary prompt string. */
+ std::string buildPrimaryPrompt(bool forReadline) const;
+ /** @brief Build continuation prompt string. */
+ std::string buildContinuationPrompt(bool forReadline) const;
+ /** @brief Print timing line after execution. */
+ void printTimingLine(bool success, double ms) const;
+ /** @brief Print highlighted echo of executed code (if color enabled). */
+ void printHighlightedEcho(const std::string &code) const;
+
+ interpreter::Interpreter &interp_; ///< Bound interpreter instance.
+ std::string buffer_; ///< Current multiline buffer.
+ int promptCount_ = 1; ///< Prompt counter for numbered prompts.
+ double lastDurationMs_ = 0.0; ///< Last execution time in ms.
+ bool lastSuccess_ = true; ///< Last execution success flag.
+ bool hasLastTiming_ = false; ///< Whether timing is available.
};
} // namespace repl
diff --git a/include/cpp-repl/utils/bigint.h b/include/cpp-repl/utils/bigint.h
index facd5b8..fb24ef5 100644
--- a/include/cpp-repl/utils/bigint.h
+++ b/include/cpp-repl/utils/bigint.h
@@ -4,22 +4,41 @@
namespace cpprepl {
namespace utils {
-// Big integer support – like Python's arbitrary large ints.
-// Uses boost::multiprecision::cpp_int (header-only) and GMP if available.
-// Scalable: header provides preamble to inject into REPL.
+/**
+ * @file bigint.h
+ * @brief Big integer support via boost::multiprecision.
+ */
+
+/**
+ * @brief Provides BigInt preamble and helpers for the REPL.
+ *
+ * Injects boost::multiprecision headers so users can write cpp_int / bigint
+ * directly without manual includes, similar to Python's big ints.
+ */
class BigIntSupport {
public:
- // Preamble injected at REPL init for transparent bigint support.
- // Users can then write: bigint x = 12345678901234567890_cpp_int; or use cpp_int directly.
+ /**
+ * @brief Preamble with cpp_int and bigint alias.
+ * @return C++ code injected at interpreter init.
+ */
static const char *preamble();
- // Alternative GMP preamble (uses mpz_int which wraps libgmp)
+ /**
+ * @brief GMP-specific preamble (mpz_int).
+ * @return C++ code for GMP backend.
+ */
static const char *gmpPreamble();
- // Check if boost headers are available (always true in our build)
+ /**
+ * @brief Check if boost headers are available.
+ * @return true if usable.
+ */
static bool isAvailable();
- // Example large number for testing
+ /**
+ * @brief Demo code for documentation and tests.
+ * @return Example snippet using big ints.
+ */
static std::string demoCode();
};
diff --git a/include/cpp-repl/utils/highlight.h b/include/cpp-repl/utils/highlight.h
new file mode 100644
index 0000000..484c8dc
--- /dev/null
+++ b/include/cpp-repl/utils/highlight.h
@@ -0,0 +1,22 @@
+#pragma once
+#include
+
+namespace cpprepl {
+namespace utils {
+
+/**
+ * @brief Simple C++ syntax highlighter for REPL output.
+ *
+ * Colors keywords, types, preprocessor, strings, comments, numbers.
+ * Respects NO_COLOR / --no-color via shouldUseColor check externally,
+ * but this function itself just checks useColor param.
+ */
+class Highlighter {
+public:
+ static std::string highlight(const std::string &code, bool useColor);
+ static std::string highlightType(const std::string &typeStr, bool useColor);
+ static std::string highlightValue(const std::string &valStr, bool useColor);
+};
+
+} // namespace utils
+} // namespace cpprepl
diff --git a/include/cpp-repl/utils/version_detector.h b/include/cpp-repl/utils/version_detector.h
index b69a6c2..e2c009c 100644
--- a/include/cpp-repl/utils/version_detector.h
+++ b/include/cpp-repl/utils/version_detector.h
@@ -5,20 +5,37 @@
namespace cpprepl {
namespace utils {
+/**
+ * @file version_detector.h
+ * @brief Automatic C++ standard detection.
+ */
+
+/**
+ * @brief Supported C++ standards.
+ */
enum class StdVersion {
- Cpp17 = 17,
- Cpp20 = 20,
- Cpp23 = 23
+ Cpp17 = 17, ///< C++17
+ Cpp20 = 20, ///< C++20
+ Cpp23 = 23 ///< C++23
};
+/**
+ * @brief Detects required C++ standard from source keywords.
+ */
class VersionDetector {
public:
- // Auto-detect required C++ standard from code keywords.
- // Like python's future imports, but for C++.
+ /**
+ * @brief Detect standard needed for given code.
+ * @param code Source snippet to inspect.
+ * @return Required StdVersion (minimum is Cpp17).
+ */
static StdVersion detect(const std::string &code);
+ /** @brief Convert version to compiler flag, e.g. -std=c++20. */
static std::string toFlag(StdVersion v);
+ /** @brief Convert version to human string, e.g. C++20. */
static std::string toString(StdVersion v);
+ /** @brief Human description with features. */
static std::string describe(StdVersion v);
private:
@@ -26,7 +43,9 @@ class VersionDetector {
static bool containsWord(const std::string &code, const std::string &word);
};
-// Helper to get current interpreter version as string for diagnostics
+/**
+ * @brief Return the maximum of two versions.
+ */
StdVersion maxVersion(StdVersion a, StdVersion b);
} // namespace utils
diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp
index df546fb..867eb19 100644
--- a/src/cli/cli.cpp
+++ b/src/cli/cli.cpp
@@ -1,3 +1,7 @@
+/**
+ * @file cli.cpp
+ * @brief CLI parsing implementation for cpp-repl.
+ */
#include "cpp-repl/cli/cli.h"
#include
@@ -29,6 +33,10 @@ Options parse(int argc, char **argv, std::string &err) {
opts.showScaffold = false;
else if (arg == "--no-interactive")
opts.noInteractive = true;
+ else if (arg == "--no-color" || arg == "--nocolor" || arg == "--color=never" || arg == "--color=none")
+ opts.noColor = true;
+ else if (arg == "--color" || arg == "--color=always" || arg == "--color=auto")
+ opts.noColor = false;
else if (arg == "-e") {
if (i + 1 >= argc) { err = "-e requires argument"; return opts; }
opts.execCodes.push_back(argv[++i]);
@@ -103,6 +111,8 @@ void printHelp(const char *prog) {
" -v, --version show version\n"
" --scaffold show low-level VM scaffold\n"
" --no-interactive exit after file/-e execution\n"
+ " --no-color disable colored prompt & timing\n"
+ " --color[=when] force color (always/auto/never)\n"
" -e execute raw C++ code\n"
" -I , -I add include search path (absolute or relative)\n"
" -L , -L add library search path\n"
@@ -114,6 +124,8 @@ void printHelp(const char *prog) {
"REPL: :help :dump :reset :load :lib :I :L :version :quit\n"
" :I add include path (interactive)\n"
" :L add library path\n"
+ "Prompt: colored cpp:C++17/20/23 [n] (time ✓/✗)> with execution time\n"
+ " honor NO_COLOR / CPP_REPL_NO_COLOR=1 and --no-color, TERM=dumb\n"
"C++ versions: auto-detects C++20/23 keywords "
"(concept/requires/import)\n"
"BigInt: boost::multiprecision::cpp_int / bigint\n"
diff --git a/src/core/vm.cpp b/src/core/vm.cpp
index db74c28..7ede638 100644
--- a/src/core/vm.cpp
+++ b/src/core/vm.cpp
@@ -1,3 +1,7 @@
+/**
+ * @file vm.cpp
+ * @brief LLJITVM implementation - thin wrapper over llvm::orc::LLJIT.
+ */
#include "cpp-repl/core/vm.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/TargetSelect.h"
diff --git a/src/interpreter/interpreter.cpp b/src/interpreter/interpreter.cpp
index c594743..243ef02 100644
--- a/src/interpreter/interpreter.cpp
+++ b/src/interpreter/interpreter.cpp
@@ -1,17 +1,165 @@
+/**
+ * @file interpreter.cpp
+ * @brief Interpreter implementation with high-precision float printing and BigInt handling.
+ */
#include "cpp-repl/interpreter/interpreter.h"
#include "cpp-repl/utils/bigint.h"
+#include "cpp-repl/utils/highlight.h"
#include "cpp-repl/utils/version_detector.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Interpreter/Interpreter.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
+#include "clang/AST/Type.h"
#include
#include
#include
#include
#include
#include
+#include
+#include
+#include
+#ifndef _WIN32
+#include
+#endif
+
+namespace {
+/**
+ * @brief Helper for high-precision floating point printing.
+ *
+ * Replaces clang's default %.6g/%.8g which truncates to 6-8 digits.
+ * Uses max_digits10 (9 for float, 17 for double, 21 for long double)
+ * so printed values round-trip and preserve input precision.
+ */
+static std::string formatFloatHighPrec(float v) {
+ std::string out;
+ llvm::raw_string_ostream ss(out);
+ if (std::isnan(v) || std::isinf(v)) {
+ ss << llvm::format("%g", v);
+ } else if (v == static_cast(static_cast(v))) {
+ ss << llvm::format("%.1f", v);
+ } else {
+ ss << llvm::format("%#.9g", v);
+ }
+ ss << 'f';
+ return ss.str();
+}
+static std::string formatDoubleHighPrec(double v) {
+ std::string out;
+ llvm::raw_string_ostream ss(out);
+ if (std::isnan(v) || std::isinf(v)) {
+ ss << llvm::format("%g", v);
+ } else if (v == static_cast(static_cast(v))) {
+ ss << llvm::format("%.1f", v);
+ } else {
+ ss << llvm::format("%#.17g", v);
+ }
+ return ss.str();
+}
+static std::string formatLongDoubleHighPrec(long double v) {
+ std::string out;
+ llvm::raw_string_ostream ss(out);
+ if (std::isnan(v) || std::isinf(v)) {
+ ss << llvm::format("%Lg", v);
+ } else if (v == static_cast(static_cast(v))) {
+ ss << llvm::format("%.1Lf", v);
+ } else {
+ constexpr int prec = std::numeric_limits::max_digits10;
+ std::string fmt = "%#." + std::to_string(prec) + "Lg";
+ ss << llvm::format(fmt.c_str(), v);
+ }
+ ss << 'L';
+ return ss.str();
+}
+
+static void highPrecisionDump(const clang::Value &V) {
+ if (!V.isValid() || V.isVoid())
+ return;
+ std::string typeStr;
+ {
+ llvm::raw_string_ostream ts(typeStr);
+ V.printType(ts);
+ }
+ std::string dataStr;
+ bool handled = false;
+ // Try direct Kind first – covers most prvalues
+ switch (V.getKind()) {
+ case clang::Value::K_Float:
+ dataStr = formatFloatHighPrec(V.getFloat());
+ handled = true;
+ break;
+ case clang::Value::K_Double:
+ dataStr = formatDoubleHighPrec(V.getDouble());
+ handled = true;
+ break;
+ case clang::Value::K_LongDouble:
+ dataStr = formatLongDoubleHighPrec(V.getLongDouble());
+ handled = true;
+ break;
+ default:
+ break;
+ }
+ if (!handled) {
+ // Fallback: check QualType for reference / object cases where Kind is
+ // K_PtrOrObj but the underlying type is a builtin floating type.
+ clang::QualType qt = V.getType();
+ clang::QualType nonRef = qt.getNonReferenceType();
+ const clang::Type *canon = nonRef.getCanonicalType().getTypePtr();
+ if (auto *bt = llvm::dyn_cast(canon)) {
+ if (bt->getKind() == clang::BuiltinType::Float ||
+ bt->getKind() == clang::BuiltinType::Double ||
+ bt->getKind() == clang::BuiltinType::LongDouble) {
+ // Value is stored as a pointer (reference / object)
+ if (V.getKind() == clang::Value::K_PtrOrObj && V.getPtr()) {
+ void *p = V.getPtr();
+ if (bt->getKind() == clang::BuiltinType::Float) {
+ float fv = *static_cast(p);
+ dataStr = formatFloatHighPrec(fv);
+ handled = true;
+ } else if (bt->getKind() == clang::BuiltinType::Double) {
+ double dv = *static_cast(p);
+ dataStr = formatDoubleHighPrec(dv);
+ handled = true;
+ } else {
+ long double ldv = *static_cast(p);
+ dataStr = formatLongDoubleHighPrec(ldv);
+ handled = true;
+ }
+ }
+ }
+ }
+ }
+ if (!handled) {
+ llvm::raw_string_ostream ds(dataStr);
+ V.printData(ds);
+ }
+ // Keyword highlight: colorize type and value when tty and color enabled
+ bool useColor = false;
+#ifndef _WIN32
+ useColor = isatty(STDOUT_FILENO) && !getenv("NO_COLOR") && !getenv("CPP_REPL_NO_COLOR") && !getenv("NO_COLOUR");
+ if (useColor) {
+ const char *term = getenv("TERM");
+ if (term && std::string(term)=="dumb") useColor=false;
+ }
+ if (getenv("FORCE_COLOR") || getenv("CLICOLOR_FORCE")) useColor = true;
+#else
+ useColor = false;
+#endif
+ if (getenv("CPP_REPL_NO_COLOR") || getenv("NO_COLOR")) useColor = false;
+ if (useColor) {
+ std::string colType = cpprepl::utils::Highlighter::highlightType(typeStr, true);
+ std::string colVal = cpprepl::utils::Highlighter::highlightValue(dataStr, true);
+ // Use llvm::outs with ANSI: wrap
+ llvm::outs() << "(\033[36m" << typeStr << "\033[0m) " << colVal << "\n";
+ // Note: colType already contains color, but we use direct for simplicity
+ (void)colType;
+ } else {
+ llvm::outs() << "(" << typeStr << ") " << dataStr << "\n";
+ }
+}
+} // namespace
namespace cpprepl {
namespace interpreter {
@@ -45,6 +193,7 @@ bool Interpreter::init(utils::StdVersion version,
libraryPaths_ = libraryPaths;
libraries_ = libraries;
currentVersion_ = version;
+ stdLibIncluded_ = false;
clang::IncrementalCompilerBuilder builder;
compilerArgsStorage_.clear();
@@ -150,6 +299,9 @@ bool Interpreter::init(utils::StdVersion version,
llvm::handleAllErrors(std::move(e2), [&](llvm::ErrorInfoBase &EIB) {});
#endif
}
+ // Auto-include standard library (bits/stdc++.h) by default for STL support
+ // If keyword not found, eval will retry with this. Pre-include avoids extra round-trip.
+ tryIncludeStdLib();
// Load libraries requested via -l / --library (absolute, relative, or -l name)
for (auto &lib : libraries_) {
auto tryLoad = [&](const std::string &path) -> bool {
@@ -209,6 +361,7 @@ bool Interpreter::reinitWithCurrentOptions(std::string &err) {
history_.clear();
variables_.clear();
varHistory_.clear();
+ stdLibIncluded_ = false;
if (!init(currentVersion_, includePaths_, defines_, libraryPaths_, libraries_, local)) {
err = local;
return false;
@@ -285,6 +438,7 @@ bool Interpreter::ensureVersion(utils::StdVersion needed, std::string &err) {
history_.clear();
variables_.clear();
varHistory_.clear();
+ stdLibIncluded_ = false;
if (!init(needed, includePaths_, defines_, libraryPaths_, libraries_, local)) {
err = local;
return false;
@@ -298,6 +452,46 @@ bool Interpreter::ensureVersion(utils::StdVersion needed, std::string &err) {
return true;
}
+bool Interpreter::tryIncludeStdLib() {
+ if (stdLibIncluded_ || !initialized_ || !interp_) return stdLibIncluded_;
+ clang::Value V;
+ // Try bits/stdc++.h first (covers everything)
+ auto e = interp_->ParseAndExecute("#include \n", &V);
+ if (!e) { stdLibIncluded_ = true; return true; }
+ llvm::handleAllErrors(std::move(e), [&](llvm::ErrorInfoBase &EIB){});
+ // Fallback: include common STL headers individually
+ const char *fallback = R"(
+#include
+#include
+#include
+#include
+#include
+#include