π¦ πΊπΈ An interpreter (and a little bit of a compiler) β written in Rust β for TodePond's Gulf of Mexico (formerly DreamBerd), the perfect programming language.
The language spec is, ostensibly, a meme. We are nevertheless extremely serious about it.
cargo run --release -- examples/hello.gomHello, Gulf of America!
Be even bolder!
7
[debug] print(1 + 2*3) = undefined
The gulf binary also accepts subcommands:
| Command | What it does |
|---|---|
gulf <file> / gulf run <file> |
Lex, parse, and execute a .gom program. |
gulf check <file> |
Parse-only β report any diagnostics without running. |
gulf tokens <file> |
Dump the token stream (debugging aid). |
gulf parse <file> |
Dump the parsed AST (debugging aid). |
gulf --help / --version |
The usual. |
Diagnostics include source spans, file:line:column references, and explanatory notes β for example:
error[E0700]: cannot reassign `name`
--> hello.gom:3:1
|
3 | name = "Lu"!
| ^^^^ this variable was declared as `const const`, which forbids reassignment
= note: to allow reassignment, declare it as `var const` or `var var`.
Every example in the upstream README has a corresponding integration test in
tests/spec.rs. 78 of 78 upstream spec tests pass, plus 33 lexer/parser
unit tests, 11 std http tests, and a parser-recovery test.
| Spec section | Status | Notes |
|---|---|---|
Exclamation marks (!, !!!, ?) |
β | ? runs the statement and prints the source plus result. |
; as the not prefix |
β | |
const const / const var / var const / var var |
β | Reassignment + mutation rules enforced with friendly diagnostics. |
const const const (eternal) |
β | Parsed and tagged; honours an eternal flag on the binding. |
Unicode names, naming numbers (const const 5 = 4!) |
β | Numeric literal evaluation consults the binding table first. |
| Arrays starting at -1 | β | |
Float index insertion (scores[0.5] = 4) |
β | |
when watchers |
β | Re-checked after every statement; rising-edge fires the body. |
Lifetimes β <N>, <Ns>, <Infinity>, <-N> (hoisting) |
β | Lines/seconds expiry; negative lifetimes hoist. |
Three-valued booleans (true, false, maybe) |
β | maybe matches anything in ==. |
| Whitespace-significant arithmetic precedence | β | 1 + 2*3 = 7, 1+2 * 3 = 9. |
Number names (one + two = 3) |
β | zero through twelve. |
Four equality levels (=, ==, ===, ====) |
β | ==== is identity-aware: pi ==== pi true, 3.14 ==== pi false. |
All "function" prefixes (f, fn, fun, func, functi, function) |
β | |
Divide by zero β undefined |
β | Same for modulo. |
| Strings with any number of matching quotes (incl. zero) | β | Bareword strings: an undeclared identifier evaluates to its own name as a string. |
Currency-symbol interpolation (${}, Β£{}, Β₯{}, {}β¬, {a$b}) |
β | Cape Verdean escudo form lowers to member access. |
| Type annotations | β | Parsed and ignored. |
File separators (=====+, optional name) |
β | Each section runs with a fresh global scope. |
One-instance-per-class; factory-class workaround |
β | Diagnostic wording matches the README verbatim. |
delete (primitives, names) |
β | Tombstone is checked at literal evaluation and on arithmetic results. |
Overload priorities via !-count and Β‘ |
β | Lookup picks the highest-priority live binding. |
| Parentheses are whitespace | β | (add (3, 2))!, add 3, 2! and add)3, 2(! all work. |
previous / next / current |
β | current is now; previous is the value before the last reassignment; next peeks at the next assignment in the file. |
| Async functions (line-interleaved execution) | β | Un-await-ed calls queue a task that ticks one statement per main-thread statement; await runs synchronously and returns the result. |
Signals (use(0), destructured pairs) |
β | [get, set] = use(initial) materialises a getter/setter pair sharing one cell. A non-destructured signal is itself callable: sig() reads, sig(v) writes. |
reverse! |
β | Reverses the remaining statements in the file. |
import / export to |
β | export <name> to "file.gom"! deposits a binding for import <name>! in the named =====-separated file. import <name>! also resolves built-in std packages (currently http) when no user export is in scope. |
| DBX (HTML-in-source) | β | |
| AI features (Lu Wilson auto-completion) | β | We unfortunately do not have Lu's email. |
import <name>! falls back to a small built-in stdlib registry when no
user-level export matches. User exports always win, so existing programs
keep their semantics.
| Package | Surface |
|---|---|
http |
http.get(url), http.post(url, body), http.request({method, url, body, headers}), http.serve(addr, handler), http.serve_once(addr, handler). Plain HTTP/1.1 over TCP β no TLS. Successful responses are {ok: true, status, reason, body, headers}. Connection / parse failures come back as data: {ok: false, error, status: 0, body: "", headers: {}} so user code can branch instead of aborting. The client decodes Transfer-Encoding: chunked. Handlers receive {method, path, body, headers} and may return a string body or a {status, body, headers, reason} object. |
import http!
function handle(req) => { return {status: 200, body: "hi " + req.path}! }
http.serve_once("127.0.0.1:8765", handle)!
gulf-lsp is a Language Server for .gom files. It is feature-gated so the
core interpreter stays dependency-free:
cargo build --release --features lsp --bin gulf-lsp
# or:
cargo install --path . --features lspThen point your editor at the gulf-lsp binary as the language server for
gulf / dreamberd / *.gom. Capabilities in v0:
- Diagnostics on open/change/save β lex + parse errors, with codes and notes preserved. The parser recovers past statement-level errors so a single broken line doesn't hide every other one in the file.
- Hover for keywords, builtins, and the
httpstd package. - Document symbols for top-level
function/class/lets, with class fields and methods nested as children. - Goto-definition for identifiers that resolve to a binding declared at top level, inside a function body, or as a class method/field.
Completion, semantic tokens, and formatting are intentionally out of scope for the first cut.
src/
βββ source.rs SourceFile + Span: byte offsets β (line, col)
βββ diagnostic.rs codespan-style error rendering (no deps)
βββ token.rs TokenKind incl. run-length tokens (Bang(n), Eq(n), β¦)
βββ lexer.rs hand-rolled lexer; multi-quote strings, currency
β interpolation, whitespace tracking
βββ ast.rs AST: every quirk of the language is represented
βββ parser/
β βββ expr.rs Pratt-style with whitespace-significant precedence
β βββ stmt.rs declarations, control flow, classes, no-paren calls
βββ value.rs runtime values + per-allocation InstanceId for `====`
βββ env.rs scope chain with overload-priority + lifetime expiry
βββ interpreter.rs tree-walking evaluator
βββ interpreter/
β βββ builtins.rs default globals (`print`, β¦)
β βββ stdlib/ packages reachable via `import <name>!`
β βββ http.rs HTTP/1.1 client + server primitives
βββ lsp.rs Language Server (feature = "lsp")
βββ bin/
β βββ gulf-lsp.rs thin stdio entry point for `gulf-lsp`
βββ main.rs CLI: run / check / tokens / parse subcommands
The implementation is zero-dependency by design β only std. It compiles
under #![forbid(unsafe_code)].
cargo test # 122: 33 unit + 89 spec
cargo test --features lsp # adds 14 lsp tests (= 136 total)
cargo clippy --all-targetsEvery code block in the upstream README has a matching #[test] in
tests/spec.rs, with the expected output taken straight from the // note
that the README pairs with the example.
MIT. See LICENSE.