c-shell 0.1.1

An interactive shell for C: type an expression, a statement or a function and see what your real compiler does with it
# c-shell

An interactive shell for the C language. To check how a piece of syntax
behaves, skip the new-file / write-`main` / compile / run loop — just type it
at the prompt.

```
c-shell 0.1.0  ·  cc (GCC) 16.1.1 (default std gnu23)
In [1]: int x = 41;
In [2]: x + 1
Out[2]: 42
In [3]: 3 / 2
Out[3]: 1
In [4]: 3.0 / 2
Out[4]: 1.5
In [5]: -1 > 0u
<input>:1:4: warning: comparison of integer expressions of different signedness
    1 | -1 > 0u
      |    ^
Out[5]: 1
```

## Not an interpreter

c-shell assembles your input into a complete C program and hands it to the
**real compiler on your machine**. That is deliberate: integer promotion,
bit-field layout, evaluation order, undefined behavior — the things most worth
testing interactively — only have trustworthy answers from the compiler you
actually use. An interpreter's answer speaks only for the interpreter.

## Installation

Requires a Rust toolchain to build, and any C compiler (gcc / clang / MSVC)
at runtime.

```sh
cargo install --path .
```

On startup the compiler is resolved in the order `--cc` flag → `$CC` → PATH
(`cc`, `gcc`, `clang`, `tcc`; on Windows `gcc`, `clang`, `cc`, `clang-cl`,
`cl`). The first candidate that passes every check wins. Capabilities are
probed by trial compilation, never derived from version strings — version
numbers lie (`gcc` on macOS is actually clang, and distros backport
features).

```sh
c-shell                              # auto-detect a compiler
c-shell --cc clang --std c23         # pin compiler and standard
c-shell --timeout 30                 # seconds before a runaway program is killed
c-shell -e 'sizeof(long)'            # evaluate and exit: bare value on stdout,
                                     # diagnostics on stderr, exit code on failure
c-shell --script demo.csh            # run inputs from a file, then exit
c-shell --quiet                      # interactive, but skip the banner
echo '1 + 1' | c-shell               # piped input: transcript output, no banner
```

Terminal handling is automatic: when stdout is not a terminal (or
`TERM=dumb`, or `NO_COLOR` is set) colors are off; when stdin is not a
terminal the banner, prompts and farewell are suppressed and input is read
in batch mode — which also accumulates multi-line definitions, so scripts
and pipes can contain full function bodies.

The language standard defaults to **whatever your compiler defaults to**
(gnu23 for gcc 16, gnu17 for clang 22; the banner shows what was detected) —
the behavior you see here is the behavior a plain `gcc foo.c` would give you.
Pin a standard with `--std c17`, or switch mid-session with `%std c17` /
`%std default`. One exception: when the compiler's default mode is too old to
compile `_Generic` (MSVC without `/std:` is C89), the standard is auto-raised
to c17/c11 and the banner says so. **C11 is a hard floor**: a compiler that
cannot reach it in any mode is skipped, and if no candidate qualifies,
c-shell reports an error and exits.

## Usage

| Input | Behavior |
|---|---|
| `x + 1` | evaluated, value printed as `Out[n]` |
| `x + 1;` | trailing `;` runs it silently (as in IPython) |
| `int x = 41;` | declaration, visible to later inputs |
| `int f(int a) { ... }` | function definition, hoisted to file scope |
| `#include <time.h>` | likewise |

Common headers (stdio/stdlib/string/math/stdbool/stdint/limits/ctype) are
pre-included and `-lm` is linked by default. `-Wall -Wextra` is on by
default: the warning is often exactly the thing you came to check.

### Commands

`%`-prefixed, in the spirit of IPython:

```
%help      %quit      %reset     %history
%src       %undo      %cc        %std
```

`%src` prints the complete C program the session assembles — when you want to
know exactly what the tool is doing on your behalf, look there.

Tab completes `%` commands, C keywords, stdlib staples and every name your
session has mentioned. Input history persists across sessions in
`~/.local/share/c-shell/history` (`%APPDATA%\c-shell\history` on Windows).

## How it works

**Accumulate and replay.** Every evaluation reassembles and reruns the whole
session. Session variables are therefore ordinary locals in `main` — no
global symbol table, no splitting declarations from initializers.

**Classification by trial compilation.** Whether a line is an expression, a
statement or a file-scope item is not decided by parsing C; it is wrapped
each way and compiled, and whichever form the compiler accepts is the answer.
The only judge that always agrees with the compiler is the compiler.

**Value printing.** `_Generic` selects a print *function*, which is then
called — selecting a call expression instead would type-check every
unselected branch against the wrong argument type and fail to compile.

**Only state-changing inputs are kept.** An expression at the prompt is
usually a question (`x + 1`, `sizeof(int)`); questions are answered and
forgotten, or the session would grow slower with every one. Only inputs that
may change state — assignment, `++`/`--`, function calls — are replayed.
`%src` shows what was actually kept.

**Diagnostics are remapped.** The compiler sees a generated file with a
prelude and all earlier statements above your input, so its line numbers are
meaningless at the prompt. Every location — including the line-number gutter
of gcc's source excerpts — is rewritten to input-relative lines. Locations
pointing into generated scaffolding are never presented as your code.

## Known limitations

- **Side effects replay.** Every evaluation reruns the session, so `scanf`,
  file writes and `time()` execute again each time. Pure syntax exploration
  never notices; be aware of it otherwise.
- **No redeclaration in the same scope.** Declaring `int x` twice is an
  error, because it is one in C. Use `%undo` to drop the previous one.
- **The purity heuristic is conservative.** Any expression containing a
  function call is kept, even if the function happens to be pure.
- **Crashing or timed-out inputs are not committed** — otherwise every later
  replay would crash again.
- No session save/load.
- MSVC must be started from a Developer Command Prompt, or `INCLUDE`/`LIB`
  are missing.

## Development

After cloning, enable the pre-commit gate (fmt + clippy + tests — the same
checks CI runs):

```sh
git config core.hooksPath .githooks
```

The toolchain is pinned by `rust-toolchain.toml` so local clippy and CI
clippy are the same version; new lints arrive when the pin is bumped, never
by surprise on push.

Before working on the code, read [DESIGN.md](DESIGN.md): it records the
architecture decisions, the open state-model question, and several traps you
need to know about before changing things.

## License

MIT