Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ Tests validate both successful compilation and error handling:

## Language Implementation Notes

**Type System**: Currently supports `int` (32-bit) and `long` (64-bit). Type conversions follow two's complement truncation.
**Type System**: Currently supports `int`/`unsigned int` (32-bit) and `long`/`unsigned long` (64-bit), with the usual arithmetic conversions. Narrowing truncates (two's complement); widening sign-extends signed sources and zero-extends unsigned ones; same-width signed/unsigned conversions reinterpret the bits.

**Integer Arithmetic**: Deterministic wrapping behavior (non-standard C extension). Shift amounts are masked to prevent undefined behavior.
**Integer Arithmetic**: Signed overflow wraps deterministically (non-standard C extension; standard C makes it UB); unsigned wraps mod 2^N (standard). Shift amounts are masked to prevent undefined behavior. The `-Woverflow` warning fires only for signed overflow.

**Evaluation Order**: Left-to-right (non-standard, eliminates UB).

Expand Down
32 changes: 19 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A (**N**ot **C**ompletely) **C** compiler written in Rust, inspired by Sandler's
NCC is a full pipeline compiler, going from lexing all the way down to x86-64 machine code emission and linking.
Machine code is encoded directly using [iced-x86](https://github.com/icedland/iced) and emitted to ELF/Mach-O
object files via the [object](https://github.com/gimli-rs/object) crate—no external assembler required.
A substantial subset of C is supported, including `int` and `long` types, functions, static variables, all control
A substantial subset of C is supported, including `int`, `long`, `unsigned int`, and `unsigned long` types, functions, static variables, all control
flow statements, and bitwise operations. Additionally, NCC supports developer-friendly warnings and pretty-printing
of each compiler pass.
Runs on Linux and macOS.
Expand Down Expand Up @@ -213,7 +213,7 @@ The compiler currently implements a subset of C with the following grammar:
<variable-declaration> ::= { <specifier> }+ <identifier> [ "=" <exp> ] ";"
<function-declaration> ::= { <specifier> }+ <identifier> "(" <param-list> ")" ( <block> | ";" )
<param-list> ::= "void" | <type> <identifier> { "," <type> <identifier> }
<type> ::= "int" | "long"
<type> ::= { "int" | "long" | "signed" | "unsigned" }+
<specifier> ::= <type> | "static" | "extern"
<block> ::= "{" { <block-item> } "}"
<block-item> ::= <statement> | <declaration>
Expand All @@ -235,7 +235,7 @@ The compiler currently implements a subset of C with the following grammar:
| ";"
<exp> ::= <factor> | <exp> <binop> <exp> | <exp> <assign-op> <exp>
| <exp> "?" <exp> ":" <exp> | <exp> "++" | <exp> "--"
<factor> ::= <int> | <long> | <identifier> | <unop> <factor> | "++" <factor> | "--" <factor>
<factor> ::= <int> | <long> | <uint> | <ulong> | <identifier> | <unop> <factor> | "++" <factor> | "--" <factor>
| "(" <type> ")" <factor> | "(" <exp> ")"
| <identifier> "(" [ <argument-list> ] ")"
<argument-list> ::= <exp> { "," <exp> }
Expand All @@ -246,6 +246,8 @@ The compiler currently implements a subset of C with the following grammar:
<identifier> ::= ? An identifier token ?
<int> ::= ? An integer constant token ?
<long> ::= ? A long integer constant token (suffix 'l' or 'L') ?
<uint> ::= ? An unsigned int constant token (suffix 'u' or 'U') ?
<ulong> ::= ? An unsigned long constant token (suffix combining 'u'/'U' and 'l'/'L') ?
```

### Supported Features
Expand All @@ -261,7 +263,7 @@ The compiler supports:
functions
- **Compound statements (blocks)**: `{ ... }` with proper scoping
- **Variable scoping**: Block-local variables with shadowing support
- **Type system**: `int` (32-bit) and `long` (64-bit) with implicit conversions and explicit casts
- **Type system**: `int`/`unsigned int` (32-bit) and `long`/`unsigned long` (64-bit), with the usual arithmetic conversions, implicit conversions, and explicit casts
- **Integer arithmetic**: addition, subtraction, multiplication, division, modulo
- **Bitwise operations**: AND (`&`), OR (`|`), XOR (`^`), complement (`~`), left/right shift (`<<`, `>>`)
- **Logical operations**: AND (`&&`), OR (`||`), NOT (`!`) with short-circuit evaluation
Expand All @@ -287,13 +289,16 @@ NCC provides several safety features and guarantees to help developers write mor

#### Guaranteed Behaviors

- **Deterministic integer overflow**: Integer arithmetic uses two's complement wrapping (`int`: 32-bit, `long`: 64-bit).
For example, `INT_MAX + 1` reliably wraps to `INT_MIN`.
- **Deterministic integer overflow**: Signed integer arithmetic uses two's complement wrapping (`int`: 32-bit,
`long`: 64-bit) instead of being undefined — e.g. `INT_MAX + 1` reliably wraps to `INT_MIN`. Unsigned arithmetic
(`unsigned int`, `unsigned long`) already wraps modulo 2^N per the C standard.
- **Left-to-right evaluation**: Binary operations are evaluated left to right, eliminating undefined behavior from
evaluation order.
- **Type conversions**: Converting `long` to `int` truncates to the lower 32 bits using two's complement representation,
equivalent to repeatedly subtracting 2^32 until the value fits in an `int` range.
For example, `2147483650L` (INT_MAX + 3) converts to `-2147483646`.
- **Type conversions**: Narrowing (e.g. `long` to `int`) truncates to the lower 32 bits using two's complement
representation, equivalent to repeatedly subtracting 2^32 until the value fits in an `int` range.
For example, `2147483650L` (INT_MAX + 3) converts to `-2147483646`. Widening is value-preserving — signed sources
sign-extend, unsigned sources zero-extend — and same-width signed/unsigned conversions reinterpret the bits
(e.g. `(unsigned)-1` is `UINT_MAX`).
- **Shift masking**: Left and right shifts mask the shift amount to prevent undefined behavior. For `int` types,
the shift amount is masked with `& 31` (modulo 32); for `long` types, masked with `& 63` (modulo 64).
For example, `1 << 32` evaluates to `1 << 0 = 1`, matching x86 hardware behavior.
Expand All @@ -313,10 +318,11 @@ NCC provides several safety features and guarantees to help developers write mor
requires a constant expression (such as a static initializer) is a hard error instead
- **Out-of-range shift count** (`-Wshift-count-overflow`, `-Wshift-count-negative`): Warns when a `<<` or `>>`
(including `<<=` / `>>=`) has a constant shift count that is negative or `>=` the width of the left operand's type
(32 for `int`, 64 for `long`), e.g. `1 << 32` or `1 << -1`
- **Integer overflow in a constant expression** (`-Woverflow`): Warns when folding a constant expression in a static
initializer or case label leaves the result type (e.g. `int x = 2147483647 + 1;`). NCC wraps deterministically
(two's complement) rather than treating it as undefined, so this flags non-portable code instead of erroring
(32 for 32-bit types, 64 for 64-bit types), e.g. `1 << 32` or `1 << -1`
- **Integer overflow in a constant expression** (`-Woverflow`): Warns when folding a *signed* constant expression in a
static initializer or case label overflows the result type (e.g. `int x = 2147483647 + 1;`). NCC wraps
deterministically (two's complement) rather than treating it as undefined, so this flags non-portable code instead of
erroring. Unsigned wraparound is well-defined and is *not* warned (matching gcc/clang)
- **Constant changed by an implicit conversion** (`-Wconstant-conversion`): Warns when a constant initializer is
implicitly narrowed to a type that can't hold it (e.g. `int x = 2147483648;`, which truncates to `-2147483648`).
An explicit cast (`int x = (int)2147483648;`) silences it
Expand Down
Loading
Loading