Skip to content

Commit 229be1a

Browse files
fix docs imprecisions and dedup redundant sections
1 parent 2a4866d commit 229be1a

18 files changed

Lines changed: 51 additions & 51 deletions

File tree

docs/content/getting-started/what-it-is.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ These parse for syntactic compatibility. They raise at runtime, or don't exist:
3636
- **Async surface**: `async def` creates real coroutines, and the VM runs a cooperative scheduler. No `asyncio` module; the primitives are top-level builtins ([Async](/language/async)).
3737
- **Metaclasses, descriptor protocol, `__slots__`**: not modeled.
3838
- **Dynamic code**: no `exec`, no `eval`, no `compile`, no `__import__` (use the `import_module(name)` builtin to look up an already-imported module by alias).
39-
- **Reflection**: nothing beyond `type`, `id`, `hash`, `repr`, `callable`, `getattr`, `hasattr`, `vars`, `globals`, `locals`, `isinstance`, and `issubclass`. `dir` is absent.
39+
- **Reflection**: nothing beyond `type`, `id`, `hash`, `repr`, `callable`, `getattr`, `setattr`, `hasattr`, `delattr`, `vars`, `globals`, `locals`, `isinstance`, and `issubclass`. `dir` is absent.
4040
- **Relative imports**: `from . import x` is not supported; use quoted relative specs or `packages.json` aliases ([Imports](/reference/imports)).
4141

4242
## Design philosophy

docs/content/implementation/design.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ description: "Compiler architecture, dispatch model, and runtime layout."
77

88
The release build is around 200 KB on `wasm32-unknown-unknown` (`panic=abort`, `opt-level=z`, `lto=true`, `codegen-units=1`). The pipeline: LUT-driven lexer -> single-pass Pratt parser emitting SSA-versioned bytecode directly -> peephole constant-folding optimiser -> token-threaded interpreter with two layers of adaptive specialisation.
99

10-
No AST, no IR. Bytecode is the only intermediate representation. Around 17,000 lines of Rust. Production deps are `hashbrown` and `itoa` (SHA-256 in-tree). The WASM build adds `lol_alloc` for a single-threaded free-list allocator.
10+
No AST, no IR. Bytecode is the only intermediate representation. Around 17,000 lines of Rust. Production deps are `hashbrown`, `itoa`, and `libm` (SHA-256 in-tree). The WASM build adds `lol_alloc` for a single-threaded free-list allocator.
1111

1212
Classes support single and multiple inheritance (C3 MRO), `super()`, full dunder dispatch, `@property` / `@x.setter`. Multi-paradigm: composition preferred, monomorphic dispatch optimised via instance-dunder IC.
1313

@@ -23,7 +23,7 @@ Classes support single and multiple inheritance (C3 MRO), `super()`, full dunder
2323

2424
## Bytecode shape
2525

26-
Each `Instruction` is 4 bytes: 1-byte `OpCode` (`#[repr(u8)]` planned), 2-byte operand, 1 byte padding. Opcodes span 17 categories: load, store, arith, bitwise, compare, logic, identity, control flow, iter, build, container, comprehension, function, ssa (Phi), yield, side effects, unsupported (raises at runtime). Around 40 specialised `Call*` variants cover hot builtins. `LoadAttr + Call(0)` pairs fuse into `CallMethod + CallMethodArgs` after first dispatch.
26+
Each `Instruction` is 4 bytes: 1-byte `OpCode` (`#[repr(u8)]`), 2-byte operand, 1 byte padding. Opcodes span 17 categories: load, store, arith, bitwise, compare, logic, identity, control flow, iter, build, container, comprehension, function, ssa (Phi), yield, side effects, unsupported (raises at runtime). Around 40 specialised `Call*` variants cover hot builtins. `LoadAttr + Call(0)` pairs fuse into `CallMethod + CallMethodArgs` after first dispatch.
2727

2828
```text
2929
OpCode::LoadConst -> operand = constant index

docs/content/implementation/fuzzing.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ description: "Coverage-guided fuzzing of the lex, parse, and VM pipeline with ca
77

88
The fuzzer drives the full `lex -> parse -> VM` pipeline against mutated input. It looks for panics and memory faults. It lives in [`compiler/fuzz-afl/`](https://github.com/dylan-sutton-chavez/edge-python/tree/main/compiler/fuzz-afl) and is built on [cargo-afl](https://github.com/rust-fuzz/afl.rs) (AFL++). On stable it instruments through rustc's own LLVM SanitizerCoverage (`-sanitizer-coverage-trace-pc-guard`) and links the AFL++ runtime. So it runs on **stable Rust**, no nightly toolchain required. (AFL++'s own LLVM passes, CMPLOG and IJON, are an optional, nightly-only path the project does not use.)
99

10-
The target runs the VM under the sandbox profile. Runaway loops and allocations become a `VmErr` instead of a hang. Any crash the harness aborts on is a genuine bug, not resource exhaustion. Its panic hook filters one class out: arithmetic-overflow panics are a debug-only artifact of `overflow-checks`, so it ignores them and aborts on everything else. The harness tightens one field: `Limits { ops: 100_000, ..Limits::sandbox() }`. The default 100M-op budget is bounded, but takes long enough that AFL would flag a legitimately-terminating loop (or wide recursion) as a hang. The smaller budget keeps each execution inside AFL's hang timeout while still reaching deep into the language. It also sets `strict_input = true`, so `input()` raises instead of blocking on real stdin, which AFL feeds through shared memory. See [Limits and errors](/reference/limits-and-errors).
10+
The target runs the VM under the sandbox profile. Runaway loops and allocations become a `VmErr` instead of a hang. Most crashes are genuine bugs, not resource exhaustion; the exception is arithmetic-overflow panics a debug-only artifact of `overflow-checks` (off in the release VM) — which are triaged out by hand, not filtered automatically. The harness tightens one field: `Limits { ops: 100_000, ..Limits::sandbox() }`. The default 100M-op budget is bounded, but takes long enough that AFL would flag a legitimately-terminating loop (or wide recursion) as a hang. The smaller budget keeps each execution inside AFL's hang timeout while still reaching deep into the language. It also sets `strict_input = true`, so `input()` raises instead of blocking on real stdin, which AFL feeds through shared memory. See [Limits and errors](/reference/limits-and-errors).
1111

12-
The build runs `--release`. `[profile.release]` sets `debug = "line-tables-only"` for `file:line` backtraces without the `dev` profile's heavier debuginfo. (cargo-afl forces `opt-level=3`, `debug-assertions`, and `overflow-checks` into `RUSTFLAGS` regardless of profile. `debug-assertions` are what surface real bugs; `overflow-checks` only add the arithmetic-overflow panics the hook filters out.)
12+
The build runs `--release`. `[profile.release]` sets `debug = "line-tables-only"` for `file:line` backtraces without the `dev` profile's heavier debuginfo. (cargo-afl forces `opt-level=3`, `debug-assertions`, and `overflow-checks` into `RUSTFLAGS` regardless of profile. `debug-assertions` are what surface real bugs; `overflow-checks` only add arithmetic-overflow panics, triaged out by hand since the release VM runs without them.)
1313

1414
## Running it
1515

docs/content/implementation/lexical.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ The token set tracks Python 3.13.12 closely. Categories implemented:
2020
- **Wildcard**: Underscore (`_`) gets its own `Underscore` token. The parser distinguishes wildcard from name use.
2121
- **Operators**: 1-, 2-, and 3-character operator forms (`+`, `==`, `**=`, `//=`, etc.).
2222
- **Delimiters**: `( ) [ ] { } : , ; .`.
23-
- **Literals**: `Name`, `Int`, `Float`, `String`, `Bytes`. There is no `Complex` token. A trailing `j` / `J` is **not** lexed as a complex suffix. `1j` tokenises as `Int(1)` followed by `Name("j")`.
23+
- **Literals**: `Name`, `Int`, `Float`, `String`, `Bytes`. There is no `Complex` token; a trailing `j` / `J` is **not** lexed as a complex suffix (see Numeric literals).
2424
- **F-string segments**: `FstringStart`, `FstringMiddle`, `FstringEnd`.
2525
- **Whitespace and structure**: `Comment`, `Newline`, `Indent`, `Dedent`, `Nl`, `Endmarker`.
2626

docs/content/implementation/syntax.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Each instruction is a tagged 4-byte record:
2323

2424
```rust
2525
pub struct Instruction {
26-
pub opcode: OpCode, // 1 byte (with #[repr(u8)] planned)
26+
pub opcode: OpCode, // 1 byte (#[repr(u8)])
2727
pub operand: u16, // 2 bytes
2828
}
2929
```
@@ -54,7 +54,7 @@ Operands and the constant pool, name table, and instruction stream per chunk are
5454
```text
5555
Name -> name() (handles assignment, walrus, calls)
5656
String -> emit Str constant; concatenate adjacent String tokens
57-
Int / Float -> emit numeric constant; literal beyond 2⁴⁷ is a parse error
57+
Int / Float -> emit numeric constant; ints widen i64->i128, beyond ±2¹²⁷ is a parse error
5858
True/False/None/Ellipsis -> emit dedicated load opcode
5959
FstringStart -> fstring()
6060
Lbrace -> brace_literal() (dict, set, comprehension)
@@ -65,7 +65,7 @@ Lambda -> parse_lambda()
6565

6666
After an atom, `postfix_tail()` handles trailers (subscript, attribute, call), iterating until none apply. So `fns[0](-3)`, `obj.method()`, `(lambda x: x)(3)`, and `compose(f, g)(x)` all parse uniformly.
6767

68-
`*args` / `**kwargs` are accepted in **call** position only. Starred unpacking inside literals (`[*a, *b]`, `{**d1, **d2}`, `(1, *xs, 2)`) is not supported.
68+
`*args` / `**kwargs` are accepted in **call** position. Starred unpacking also works in list (`[*a, *b]`), set (`{*s}`), and dict (`{**d1, **d2}`) literals, lowering via `ListExtend` / `SetUpdate` / `DictUpdate`; tuple-literal unpacking (`(1, *xs, 2)`) is not.
6969

7070
## Operator precedence
7171

@@ -88,7 +88,7 @@ Each binary operator declares `(l_bp, r_bp, OpCode)` in `binding_power`. Higher
8888

8989
`infix_bp` handles comparison chaining (`a < b < c`). When a comparison opcode is followed by another comparison token, the parser:
9090

91-
- stores the middle value in a synthetic `__cmp__N` slot
91+
- stores the middle value in a synthetic `#cmp_N` slot
9292
- emits the first comparison
9393
- short-circuits on false
9494
- reuses the stored value for the next comparison
@@ -219,7 +219,7 @@ Free variables (non-parameters with no local binding) are looked up in the outer
219219

220220
Parameter slots: `Normal`, `Star` (`*args`), `DoubleStar` (`**kwargs`). Lone `*` separator marks following params as keyword-only. Defaults live in `HeapObj::Func.defaults` and bind to the `=`-marked params in source order, so a default before `*args` and keyword-only defaults both apply correctly. Annotations (`x: T`, `-> T`) parse and drain to `chunk.annotations` (tooling-only).
221221

222-
`compile_body` checks impurity opcodes (`StoreItem`, `StoreAttr`, `CallPrint`, `CallInput`, `Global`, `Nonlocal`, `Import`, `Raise`, `Yield`, `LoadAttr`) to set `body.is_pure`, the flag that gates template memoisation ([Design](/implementation/design#concepts)). This static gate is complemented at runtime by an observed-impurity check that propagates through calls, so a side-effecting builtin reached as a first-class value (e.g. `print` passed to a wrapper) disqualifies the caller too.
222+
`compile_body` checks impurity opcodes (`StoreItem`, `DelItem`, `StoreAttr`, `DelAttr`, `CallPrint`, `CallInput`, `Global`, `Nonlocal`, `Raise`, `RaiseFrom`, `Yield`, `LoadAttr`) to set `body.is_pure`, the flag that gates template memoisation ([Design](/implementation/design#concepts)). This static gate is complemented at runtime by an observed-impurity check that propagates through calls, so a side-effecting builtin reached as a first-class value (e.g. `print` passed to a wrapper) disqualifies the caller too.
223223

224224
## Type annotations
225225

@@ -257,7 +257,7 @@ FormatValue 0
257257
LoadConst ", age "
258258
LoadName age_v
259259
FormatValue 0
260-
BuildString 5
260+
BuildString 4
261261
```
262262

263263
`FormatValue`'s 16-bit operand is a small flags field:

docs/content/language/async.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ b step 2
101101

102102
## gather
103103

104-
`gather(*coros)` runs each concurrently and returns a list of results in argument order. If any raises, the first error (in argument order) propagates after all peers terminate. Survivors are not auto-cancelled.
104+
`gather(*coros)` runs each concurrently and returns a list of results in argument order. If any raises, an error propagates after all peers terminate — currently the last one raised, not necessarily the first in argument order. Survivors are not auto-cancelled.
105105

106106
```python
107107
async def fetch(name, delay):

docs/content/language/classes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ True
108108
| `instance.attr` | instance `__dict__` first, then class |
109109
| `instance.method()` | bound method, `self` prepended |
110110

111-
`setattr` / `delattr` work on instances. They do not modify the class object.
111+
`setattr` / `delattr` work on instances and on class objects (the latter mutating the class's members).
112112

113113
## Class decorators
114114

docs/content/language/control-flow.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ Pre-bound exception classes (with their parent links so `except <Parent>:` match
322322
2. Call `__enter__`.
323323
3. Bind the result to `as`.
324324

325-
On exit, `__exit__(exc_type, exc_value, traceback)` runs: `(None, None, None)` on normal completion, live exception info on raise. A truthy return suppresses the exception; a falsy one propagates it. See [`/language/dunders`](/language/dunders).
325+
On exit, `__exit__` runs; a truthy return suppresses the exception, a falsy one propagates it. See [Dunders](/language/dunders#context-managers) for the `__exit__` signature and exception info.
326326

327327
```python
328328
class Resource:

docs/content/language/data-types.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,12 +321,12 @@ print({1, 2} <= {1, 2, 3}) # subset
321321
```
322322

323323
```text Output
324-
{2, 3, 4, 1}
324+
{2, 4, 1, 3}
325325
4
326326
set()
327327
<class 'dict'>
328-
{3, 1, 4, 2}
329-
{3, 2}
328+
{2, 4, 1, 3}
329+
{2, 3}
330330
True
331331
```
332332

@@ -460,7 +460,7 @@ False
460460
True
461461
['a', 'b', 'c']
462462
(1, 2, 3)
463-
{2, 1}
463+
{1, 2}
464464
```
465465

466466
## Truthy and falsy

docs/content/language/dunders.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ print(V(3) == V(3))
2323
True
2424
```
2525

26-
Dunders are looked up on the class chain. The instance dict is skipped. Subclasses inherit and may override. Operator overloading composes with [inheritance](/language/classes#inheritance-and-super). Monomorphic sites (same class for both operands) promote through the IC after 4 hits, then bypass lookup entirely.
26+
Dunders are looked up on the class chain. The instance dict is skipped. Subclasses inherit and may override. Operator overloading composes with [inheritance](/language/classes#inheritance-and-super). Monomorphic sites (keyed on the receiver's class) promote through the IC after 4 hits, then bypass lookup entirely.
2727

2828
## Arithmetic
2929

@@ -88,7 +88,7 @@ The bitwise and shift operators follow the same forward/reflected protocol.
8888

8989
`bool(x)` (and any boolean context) consults:
9090

91-
1. `__bool__` if defined -> cast to bool.
91+
1. `__bool__` if defined (must return `bool`, else `TypeError`).
9292
2. `__len__` if defined -> `False` when length is 0, else `True`.
9393
3. Default `True`.
9494

@@ -237,7 +237,7 @@ Existing attributes bypass `__getattr__`. Only misses trigger it.
237237

238238
## Context managers
239239

240-
`with cm() as x:` invokes `__enter__`. Its return binds to `as`. On exit, `__exit__(exc_type, exc_value, traceback)` runs: `(None, None, None)` for normal exit, live exception info on raise. A truthy return suppresses the exception; a falsy one propagates it.
240+
`with cm() as x:` invokes `__enter__`. Its return binds to `as`. On exit, `__exit__(exc_type, exc_value, traceback)` runs: `(None, None, None)` for normal exit; on a raise, the exception type and value with `traceback` always `None`. A truthy return suppresses the exception; a falsy one propagates it.
241241

242242
```python
243243
class Suppress:
@@ -265,7 +265,7 @@ Parsed for compatibility but never invoked on user classes:
265265

266266
- `__init_subclass__`, `__set_name__`, descriptors (`__get__` / `__set__` / `__delete__`)
267267
- `__new__`, VM constructs the instance; `__init__` runs user logic
268-
- Augmented-assignment dunders (`__iadd__`, ...), `a += b` desugars to `a = a + b`, so `__add__` covers it. Exception: when `a` is a name bound to a `list`, `+=` extends it in place (alias-visible), matching CPython's `list.__iadd__`
268+
- Augmented-assignment dunders (`__iadd__`, ...), `a += b` desugars to `a = a + b`, so `__add__` covers it. Exception: list `+=` extends in place (alias-visible); see [Data types](/language/data-types#list)
269269
- Async dunders (`__aenter__` / `__aexit__` / `__aiter__` / `__anext__`), `async with` / `async for` use the sync paths
270270

271271
For class basics (constructors, inheritance, properties), see [Classes](/language/classes).

0 commit comments

Comments
 (0)