|
| 1 | +// SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | +// SPDX-FileCopyrightText: 2024-2026 Jonathan D.A. Jewell (hyperpolymath) |
| 3 | += RattleScript |
| 4 | +:toc: |
| 5 | +:toc-placement: preamble |
| 6 | + |
| 7 | +Python-syntax AffineScript. |
| 8 | +Write code that looks like Python. |
| 9 | +Get affine resource guarantees, algebraic effects, and typed WASM out. |
| 10 | + |
| 11 | +--- |
| 12 | + |
| 13 | +== What it is |
| 14 | + |
| 15 | +RattleScript is AffineScript with its Python face pre-selected. |
| 16 | +If you write Python, you already know most of the syntax. |
| 17 | +The compiler checks that your resources (files, sockets, tokens) are used |
| 18 | +*exactly as many times as you say* — and proves it at compile time. |
| 19 | + |
| 20 | +[source,python] |
| 21 | +---- |
| 22 | +def greet(name: String) -> String: |
| 23 | + "Hello, " + name + "!" |
| 24 | +
|
| 25 | +def main() -> (): |
| 26 | + let msg = greet("world") |
| 27 | + IO.println(msg) |
| 28 | +---- |
| 29 | + |
| 30 | +That's a valid `.rattle` file. Run it: |
| 31 | + |
| 32 | +[source,bash] |
| 33 | +---- |
| 34 | +rattle eval hello.rattle |
| 35 | +---- |
| 36 | + |
| 37 | +== Why Python syntax? |
| 38 | + |
| 39 | +Because Python programmers deserve a sound type system. |
| 40 | +RattleScript is Python's more dangerous cousin — same comfortable whitespace- |
| 41 | +delimited blocks, but with teeth: no null pointer exceptions, no |
| 42 | +use-after-free, no data races, and no runtime surprises that the type |
| 43 | +checker already knows about. |
| 44 | + |
| 45 | +The mascot is a rattlesnake. Python's more dangerous cousin. |
| 46 | + |
| 47 | +== Surface mappings |
| 48 | + |
| 49 | +[cols="1,1"] |
| 50 | +|=== |
| 51 | +|Python surface |AffineScript equivalent |
| 52 | + |
| 53 | +|`def f(x: T) -> R:` |`fn f(x: T) -> R { ... }` |
| 54 | +|`True` / `False` |`true` / `false` |
| 55 | +|`None` |`()` |
| 56 | +|`and` / `or` / `not` |`&&` / `\|\|` / `!` |
| 57 | +|`class Name:` |`type Name` |
| 58 | +|`pass` |`()` |
| 59 | +|`import a.b` |`use a::b;` |
| 60 | +|`from a import b` |`use a::b;` |
| 61 | +|`if cond:` / `else:` |`if cond { ... } else { ... }` |
| 62 | +|`for x in e:` |`for x in e { ... }` |
| 63 | +|`match e:` |`match e { ... }` |
| 64 | +|`# comment` |`// comment` |
| 65 | +|=== |
| 66 | + |
| 67 | +See `rattle preview-python-transform <file>` to inspect the full transform |
| 68 | +for any file. |
| 69 | + |
| 70 | +== Installation |
| 71 | + |
| 72 | +=== From source (monorepo dev) |
| 73 | + |
| 74 | +[source,bash] |
| 75 | +---- |
| 76 | +# From nextgen-languages/affinescript root — build affinescript first: |
| 77 | +dune build |
| 78 | +
|
| 79 | +# Then build the rattle wrapper: |
| 80 | +cd distributions/rattlescript |
| 81 | +just build |
| 82 | +# Binary at: target/debug/rattle |
| 83 | +---- |
| 84 | + |
| 85 | +=== Installed release |
| 86 | + |
| 87 | +[source,bash] |
| 88 | +---- |
| 89 | +just install |
| 90 | +# Installs rattle to ~/.cargo/bin |
| 91 | +---- |
| 92 | + |
| 93 | +=== Standalone GitHub repo |
| 94 | + |
| 95 | +The standalone https://github.com/hyperpolymath/rattlescript[hyperpolymath/rattlescript] |
| 96 | +repo contains a pre-configured distribution with affinescript as a git |
| 97 | +submodule. Use that for a self-contained checkout. |
| 98 | + |
| 99 | +== Usage |
| 100 | + |
| 101 | +[source,bash] |
| 102 | +---- |
| 103 | +# Type-check a file |
| 104 | +rattle check hello.rattle |
| 105 | +
|
| 106 | +# Evaluate |
| 107 | +rattle eval hello.rattle |
| 108 | +
|
| 109 | +# Compile to WASM |
| 110 | +rattle compile hello.rattle |
| 111 | +
|
| 112 | +# See the canonical AffineScript generated by the preprocessor |
| 113 | +rattle preview-python-transform hello.rattle |
| 114 | +
|
| 115 | +# All affinescript subcommands work — --face python is pre-injected |
| 116 | +rattle lint hello.rattle |
| 117 | +rattle fmt hello.rattle |
| 118 | +---- |
| 119 | + |
| 120 | +== Relationship to AffineScript |
| 121 | + |
| 122 | +RattleScript *is* AffineScript — same compiler, same type system, same WASM |
| 123 | +output. The only difference is `--face python` is the default. |
| 124 | + |
| 125 | +The Python face lives in |
| 126 | +link:../../lib/python_face.ml[`lib/python_face.ml`] and the error vocabulary |
| 127 | +in link:../../lib/face.ml[`lib/face.ml`]. No compiler internals change when |
| 128 | +the face changes. |
| 129 | + |
| 130 | +== Sync strategy |
| 131 | + |
| 132 | +This directory (`distributions/rattlescript/`) lives inside the affinescript |
| 133 | +monorepo. The standalone https://github.com/hyperpolymath/rattlescript[rattlescript] |
| 134 | +GitHub repo uses affinescript as a git submodule and contains only: |
| 135 | + |
| 136 | +* This `distributions/rattlescript/` content (copied at release time) |
| 137 | +* Branding assets |
| 138 | +* CI workflows |
| 139 | + |
| 140 | +`just update-affinescript` advances the submodule pointer after each |
| 141 | +affinescript release. All language changes, face changes, and bug fixes |
| 142 | +come through that single submodule bump — no duplication. |
0 commit comments