Pseudocode-syntax AffineScript. Write code that looks like pseudocode. Get affine resource guarantees and typed WASM out.
Status: alpha — the syntax, type checker, and WASM output are solid. Effects runtime is in progress upstream.
PseudoScript is AffineScript with its pseudocode face pre-selected. If you write pseudocode, you already know most of the syntax.
The compiler checks that your resources (files, sockets, tokens) are used exactly as many times as you say — and proves it at compile time. No null pointer exceptions. No use-after-free. No silent data races.
FUNCTION greet(name: STRING) RETURNS STRING
RETURN "Hello, " + name + "!"
END FUNCTION
FUNCTION main() RETURNS VOID
LET msg = greet("world")
PRINT(msg)
END FUNCTIONSave as hello.pseudo and run:
pseudo eval hello.pseudoBecause pseudocode programmers deserve a sound type system.
PseudoScript is pseudocode’s more dangerous cousin — same comfortable whitespace-delimited blocks, but with teeth: the compiler tracks ownership of every value so you cannot accidentally drop, duplicate, or outlive a resource. You never null-check. You never wonder if a function consumes its argument or borrows it. The type says so.
The mascot is a pseudocode.
| Pseudocode surface | What it means in PseudoScript |
|---|---|
|
function declaration |
|
|
|
unit |
|
|
` / |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Run pseudo preview-pseudocode-transform <file> to see the canonical
AffineScript that the preprocessor generates from any .pseudo file.
git clone --recurse-submodules https://github.com/hyperpolymath/pseudoscript
cd pseudoscript
# Build affinescript compiler + pseudo wrapper in one step
just bootstrap
# Optional: install pseudo to ~/.cargo/bin
just installpseudo check hello.pseudo # type-check
pseudo eval hello.pseudo # run
pseudo compile hello.pseudo # compile to WASM
pseudo fmt hello.pseudo # format
pseudo lint hello.pseudo # static analysis
# See what the pseudocode preprocessor produces
pseudo preview-pseudocode-transform hello.pseudo.pseudo files are detected automatically — no --face flag needed.
You can also use .pyaff if you prefer the AffineScript naming.
# @linear means: use this exactly once.
FUNCTION send_token(@linear token: AuthToken) RETURNS Receipt:
network.submit(token) # consumes token — compiler verifies this
END FUNCTION
# Option instead of None crashes.
FUNCTION safe_divide(a: Int, b: Int) RETURNS Option[Int]:
IF b == 0:
NONE
ELSE:
SOME(a / b)
END IF
END FUNCTION
FUNCTION show(result: Option[Int]) RETURNS VOID:
MATCH result:
SOME(n) => IO.println(Int.to_string(n))
NONE => IO.println("no result")
END MATCH
END FUNCTIONThe compiler rejects any program that drops a @linear value unused,
uses it more than once, or tries to treat NONE as a value.
PseudoScript is AffineScript. Same compiler, same type system, same WASM
output. The difference is --face pseudocode is the default and the entry
point is pseudo instead of affinescript.
The affinescript compiler lives in the affinescript/ submodule of this
repo. just update-affinescript bumps the pointer to the latest upstream
release — all language improvements flow through automatically.
Face logic: affinescript/lib/pseudocode_face.ml (syntax transform) and
affinescript/lib/face.ml (error vocabulary). No compiler internals change
when the face changes.
-
Effects runtime:
effect/handlesyntax is type-checked but not yet executed at runtime. The effects story is coming in a near-upstream release. -
Closures and linear captures: lambda-body linear capture tracking is conservative in the current release.
-
Span fidelity: error locations refer to the transformed canonical source, not the original
.pseudoline numbers. Source-map remapping is planned.
This repo is a thin distribution layer. Language bugs and features go upstream: hyperpolymath/affinescript.
PseudoScript-specific contributions (branding, examples, tutorials, the
pseudo CLI wrapper) are welcome here.
PMPL-1.0-or-later. See LICENSE.