Skip to content

Commit 8d21d77

Browse files
hyperpolymathclaude
andcommitted
refactor: switch to brand-surface architecture (no separate compiler)
Aligns rattlescript with the architectural decision that every face is .affine through the affinescript toolchain — not a separate compiler. Removed: - Cargo.toml, Cargo.lock, build.rs (no separate Rust binary) - src/main.rs (the rattle Rust wrapper; replaced by bin/rattle shim) - benches/ (no Rust to bench) - affinescript/ submodule + .gitmodules (depend on affinescript via PATH/opam, not vendor it) - examples/hello.rattle, examples/ownership.rattle (.rattle extension is gone; canonical .affine is the single source extension across all faces) - examples/SafeDOMExample.res (ReScript example doesn't belong here) - examples/web-project-deno.json (TypeScript-friendly Deno config; out of scope) - justfile (lower-case, cargo-driven; replaced by an affinescript-driven one) Replaced: - README.adoc — brand surface only; points compiler issues at affinescript - CONTRIBUTING.md — same routing - justfile — recipes call `affinescript … --face rattle` directly Added: - bin/rattle — bash shim that forwards args to affinescript with --face rattle injected after the subcommand. Source files use .affine and a `# face: rattlescript` pragma; the shim only saves typing. - examples/hello.affine — canonical .affine with the rattlescript pragma Kept (unchanged): all of .github/, .devcontainer/, LICENSE, Justfile (capital, RSR template master), Containerfile, ROADMAP.adoc, READINESS.md, all the other RSR governance scaffolding, and the docs/specs under src/{aspects, core, contracts, definitions, errors, interface} which are markdown/a2ml/ idris/zig — not Rust. Smoke-tested: `bin/rattle parse examples/hello.affine` produces a valid canonical AST through the affinescript toolchain prior to commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9e68ad3 commit 8d21d77

17 files changed

Lines changed: 152 additions & 782 deletions

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,7 @@ sync_report*.txt
104104
# Hypatia scan cache (local-only)
105105
.hypatia/
106106
.zig-cache/
107+
108+
# Allow the brand shim binary (a shell script, not a compiled artefact)
109+
!bin/
110+
!bin/*

.gitmodules

Lines changed: 0 additions & 3 deletions
This file was deleted.

CONTRIBUTING.md

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,42 @@
1-
<!-- SPDX-License-Identifier: PMPL-1.0-or-later -->
2-
# Contributing
1+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
2+
<!-- SPDX-FileCopyrightText: 2024-2026 Jonathan D.A. Jewell (hyperpolymath) -->
33

4-
1. Fork the repository
5-
2. Create a feature branch
6-
3. Ensure SPDX headers on all files
7-
4. Submit a pull request
4+
# Contributing to rattleScript
85

9-
**Author:** Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
6+
This is a **brand-surface repo**. The compiler, type checker, borrow checker,
7+
codegen, and face transformer all live in
8+
[affinescript](https://github.com/hyperpolymath/affinescript).
9+
10+
## Where to file what
11+
12+
| Issue | Repo |
13+
|---|---|
14+
| The face transformer mangles my code | [affinescript](https://github.com/hyperpolymath/affinescript/issues) |
15+
| The error message uses the wrong vocabulary for this face | [affinescript](https://github.com/hyperpolymath/affinescript/issues) — the face vocabulary lives in `lib/face.ml` |
16+
| New language feature needed (extern types, dependent types, etc.) | [affinescript](https://github.com/hyperpolymath/affinescript/issues) |
17+
| Brand README is unclear / wrong | this repo |
18+
| Example program doesn't compile | this repo (and probably also affinescript if it's a transformer bug) |
19+
| Tutorial / migration guide additions | this repo |
20+
| Add this face to my IDE / build tool | this repo, but expect it to depend on affinescript work first |
21+
22+
## Local workflow
23+
24+
```bash
25+
opam install affinescript # installs the compiler
26+
git clone https://github.com/hyperpolymath/rattlescript
27+
cd rattlescript
28+
just hello # smoke-test the example
29+
just check examples/hello.affine
30+
just preview examples/hello.affine
31+
```
32+
33+
## Pull requests
34+
35+
* SPDX header on every new file (`AGPL-3.0-or-later`).
36+
* Run `just hello` and any other examples added before opening the PR.
37+
* If you're touching the face transformer, open the PR against
38+
[affinescript](https://github.com/hyperpolymath/affinescript), not here.
39+
40+
## Code of conduct
41+
42+
See `CODE_OF_CONDUCT.md` (TBD; for now follow the affinescript code of conduct).

Cargo.lock

Lines changed: 0 additions & 7 deletions
This file was deleted.

Cargo.toml

Lines changed: 0 additions & 28 deletions
This file was deleted.

README.adoc

Lines changed: 59 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,175 +1,99 @@
1-
// SPDX-License-Identifier: PMPL-1.0-or-later
2-
// SPDX-FileCopyrightText: 2024-2026 Jonathan D.A. Jewell (hyperpolymath)
1+
// SPDX-License-Identifier: AGPL-3.0-or-later
2+
// SPDX-FileCopyrightText: 2024-2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
33
= RattleScript
4-
:toc:
5-
:toc-placement: preamble
4+
:toc: preamble
5+
:icons: font
66

7-
Python-syntax AffineScript.
8-
Write code that looks like Python.
9-
Get affine resource guarantees and typed WASM out.
10-
11-
**Status: alpha** — the syntax, type checker, and WASM output are solid.
12-
Effects runtime is in progress upstream.
13-
14-
---
7+
Python-syntax AffineScript. Write code that looks like Python, get affine resource guarantees and typed-wasm output.
158

169
== What it is
1710

18-
RattleScript is https://github.com/hyperpolymath/affinescript[AffineScript]
19-
with its Python face pre-selected. If you write Python, you already know
20-
most of the syntax.
11+
RattleScript is link:https://github.com/hyperpolymath/affinescript[AffineScript] with its `rattle` face pre-selected. If you write Python, you already know most of the syntax. The compiler checks that your resources (files, sockets, tokens, handles) are used *exactly as many times as you declare* — and proves it at compile time. No null pointer exceptions. No use-after-free. No silent data races. No GC overhead.
2112

22-
The compiler checks that your resources (files, sockets, tokens) are used
23-
*exactly as many times as you say* — and proves it at compile time.
24-
No null pointer exceptions. No use-after-free. No silent data races.
13+
This repo is a **brand surface only**. The compiler, type checker, borrow checker, and codegen all live in link:https://github.com/hyperpolymath/affinescript[affinescript]. This repo carries:
2514

26-
[source,python]
27-
----
28-
def greet(name: String) -> String:
29-
"Hello, " + name + "!"
15+
* Examples idiomatic to Python developers
16+
* Documentation aimed at the Python community
17+
* A `rattle` shim CLI that aliases `affinescript --face rattle`
18+
* Tutorial and migration guides for moving Python code into a strongly-typed, affine-typed, WASM-targeting world
3019

31-
def main() -> ():
32-
let msg = greet("world")
33-
IO.println(msg)
34-
----
20+
== Hello
3521

36-
Save as `hello.rattle` and run:
22+
`examples/hello.affine`:
3723

38-
[source,bash]
39-
----
40-
rattle eval hello.rattle
24+
[source,affine]
4125
----
26+
# face: rattlescript
4227
43-
== Why Python syntax?
44-
45-
Because Python programmers deserve a sound type system.
46-
47-
RattleScript is Python's more dangerous cousin — same comfortable
48-
whitespace-delimited blocks, but with teeth: the compiler tracks *ownership*
49-
of every value so you cannot accidentally drop, duplicate, or outlive a
50-
resource. You never null-check. You never wonder if a function consumes its
51-
argument or borrows it. The type says so.
52-
53-
The mascot is a rattlesnake.
54-
55-
== Surface mappings
56-
57-
[cols="1,1"]
58-
|===
59-
|Python surface |What it means in RattleScript
28+
effect IO:
29+
fn println(s: String) -> ();
6030
61-
|`def f(x: T) -> R:` |function declaration
62-
|`True` / `False` |`true` / `false`
63-
|`None` |unit `()` — not null, genuinely nothing
64-
|`and` / `or` / `not` |`&&` / `\|\|` / `!`
65-
|`class Name:` |`type Name` (algebraic data type)
66-
|`pass` |`()` (unit expression)
67-
|`import a.b` |`use a::b;`
68-
|`from a import b` |`use a::b;`
69-
|`if cond:` / `else:` |`if cond { ... } else { ... }`
70-
|`elif cond:` |`} else if cond {`
71-
|`for x in e:` |`for x in e { ... }`
72-
|`match e:` |`match e { ... }`
73-
|`# comment` |`// comment`
74-
|===
75-
76-
Run `rattle preview-python-transform <file>` to see the canonical
77-
AffineScript that the preprocessor generates from any `.rattle` file.
78-
79-
== Getting started
80-
81-
=== Prerequisites
31+
def main() -{IO}-> ():
32+
let greeting = "Hello, RattleScript!"
33+
println(greeting)
34+
----
8235

83-
* https://ocaml.org/[OCaml] + https://dune.build/[dune] (for the compiler)
84-
* https://www.rust-lang.org/[Rust] + cargo (for the `rattle` wrapper)
85-
* https://just.systems/[just] (task runner)
36+
Same logical program, written in Python style. Compiles to the same canonical AST and the same typed-wasm output as every other face.
8637

87-
=== Install from source
38+
== Install
8839

8940
[source,bash]
9041
----
91-
git clone --recurse-submodules https://github.com/hyperpolymath/rattlescript
42+
opam install affinescript
43+
git clone https://github.com/hyperpolymath/rattlescript
9244
cd rattlescript
93-
94-
# Build affinescript compiler + rattle wrapper in one step
95-
just bootstrap
96-
97-
# Optional: install rattle to ~/.cargo/bin
98-
just install
9945
----
10046

101-
=== Usage
47+
The `affinescript` binary does the work. The `bin/rattle` shim in this repo just defaults the `--face` flag.
48+
49+
== Use
10250

10351
[source,bash]
10452
----
105-
rattle check hello.rattle # type-check
106-
rattle eval hello.rattle # run
107-
rattle compile hello.rattle # compile to WASM
108-
rattle fmt hello.rattle # format
109-
rattle lint hello.rattle # static analysis
110-
111-
# See what the Python preprocessor produces
112-
rattle preview-python-transform hello.rattle
113-
----
114-
115-
`.rattle` files are detected automatically — no `--face` flag needed.
116-
You can also use `.pyaff` if you prefer the AffineScript naming.
53+
# Direct, via affinescript:
54+
affinescript eval --face rattle examples/hello.affine
55+
affinescript compile --face rattle examples/hello.affine -o hello.wasm
11756
118-
== Ownership in one minute
57+
# Or via the rattle shim (same thing):
58+
./bin/rattle eval examples/hello.affine
59+
./bin/rattle compile examples/hello.affine -o hello.wasm
11960
120-
[source,python]
121-
----
122-
# @linear means: use this exactly once.
123-
def send_token(@linear token: AuthToken) -> Receipt:
124-
network.submit(token) # consumes token — compiler verifies this
125-
126-
# Option instead of None crashes.
127-
def safe_divide(a: Int, b: Int) -> Option[Int]:
128-
if b == 0:
129-
None
130-
else:
131-
Some(a / b)
132-
133-
def show(result: Option[Int]) -> ():
134-
match result:
135-
Some(n) => IO.println(Int.to_string(n))
136-
None => IO.println("no result")
61+
# Or via the justfile:
62+
just run examples/hello.affine
63+
just preview examples/hello.affine # show the canonical lowering
13764
----
13865

139-
The compiler rejects any program that drops a `@linear` value unused,
140-
uses it more than once, or tries to treat `None` as a value.
66+
Source files use the canonical `.affine` extension. The face is selected by the `# face: rattlescript` pragma on the first comment line, or by the `--face rattle` flag.
67+
68+
== Different faces, same cube
14169

142-
== Relationship to AffineScript
70+
RattleScript is one of six established faces over the AffineScript core:
14371

144-
RattleScript *is* AffineScript. Same compiler, same type system, same WASM
145-
output. The difference is `--face python` is the default and the entry
146-
point is `rattle` instead of `affinescript`.
72+
* AffineScript — the canonical face
73+
* **RattleScript** — Python-style (this repo)
74+
* link:https://github.com/hyperpolymath/jaffascript[JaffaScript] — JavaScript / TypeScript-style
75+
* link:https://github.com/hyperpolymath/lucidscript[LucidScript] — PureScript / Haskell-style
76+
* link:https://github.com/hyperpolymath/cafescripto[CafeScripto] — CoffeeScript-style
77+
* link:https://github.com/hyperpolymath/pseudoscript[PseudoScript] — pseudocode-style
14778

148-
The affinescript compiler lives in the `affinescript/` submodule of this
149-
repo. `just update-affinescript` bumps the pointer to the latest upstream
150-
release — all language improvements flow through automatically.
79+
All six share the canonical `.affine` extension and lower to the same AST. Errors are reported in face-appropriate vocabulary.
15180

152-
Face logic: `affinescript/lib/python_face.ml` (syntax transform) and
153-
`affinescript/lib/face.ml` (error vocabulary). No compiler internals change
154-
when the face changes.
81+
== Why RattleScript
15582

156-
== Alpha caveats
83+
Python is the most popular programming language and has the largest scientific / data / web community on Earth. It also has well-documented limits: dynamic typing, packaging hell, the GIL, performance ceiling for browser deployment, no ownership semantics. RattleScript is a Python-shaped on-ramp to a language that fixes those limits without making you learn an alien syntax first.
15784

158-
* **Effects runtime**: `effect`/`handle` syntax is type-checked but not yet
159-
executed at runtime. The effects story is coming in a near-upstream release.
160-
* **Closures and linear captures**: lambda-body linear capture tracking is
161-
conservative in the current release.
162-
* **Span fidelity**: error locations refer to the transformed canonical source,
163-
not the original `.rattle` line numbers. Source-map remapping is planned.
85+
For a Python developer, the migration path looks like:
16486

165-
== Contributing
87+
1. Rename your `.py` files to `.affine` and add `# face: rattlescript` at the top.
88+
2. Type-annotate your function signatures (Python's optional type hints become required, but you can usually copy them over).
89+
3. Add `let` to bare variable assignments.
90+
4. Replace `import a.b` with explicit imports.
91+
5. Compile to typed-wasm, ship to browsers / WASI runtimes / native via the same build.
16692

167-
This repo is a thin distribution layer. Language bugs and features go
168-
upstream: https://github.com/hyperpolymath/affinescript[hyperpolymath/affinescript].
93+
== Status
16994

170-
RattleScript-specific contributions (branding, examples, tutorials, the
171-
`rattle` CLI wrapper) are welcome here.
95+
Alpha. The face transformer is implemented in `affinescript/lib/python_face.ml`. Known limitations are tracked in link:https://github.com/hyperpolymath/affinescript/blob/main/examples/faces/README.adoc[the affinescript faces README] under "Known transformer gaps".
17296

17397
== License
17498

175-
PMPL-1.0-or-later. See link:LICENSE[LICENSE].
99+
AGPL-3.0-or-later. See `LICENSE`.

affinescript

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)