A Python-like scripting language, implemented in Go. Faster than CPython on every benchmark, shipped as one static binary, with async/await, structs, modules and a batteries-included standard library.
Download v0.2.0 Quick start See examplesPython's feel, without significant whitespace: blocks open with a keyword
and close with end. Indentation is purely cosmetic.
# fib.cobra
def fib(n)
if n < 2
return n
end
return fib(n - 1) + fib(n - 2)
end
for i in range(8)
print(fib(i))
end
$ cobra fib.cobra # tree-walking engine $ cobra --vm fib.cobra # bytecode VM (the fast one) $ cobra # REPL, multi-line aware $ cobra build fib.cobra # compile to fib.cobrac $ cobra fib.cobrac # run the compiled bytecode
0 1 1 2 3 5 8 13
Built layer by layer โ lexer, Pratt parser, tree-walking evaluator, then a bytecode compiler and stack VM โ with one rule throughout: every feature lands in both engines, verified to behave identically, tested at every layer.
The VM beats CPython 3.14 on all six benchmarks โ register-cached dispatch, zero-allocation calls, small-int interning, superinstructions, and a custom dict with cached string hashes.
No interpreter install, no venv, no dependency hell. Copy a ~7 MB binary
anywhere and run .cobra source or compiled .cobrac bytecode.
Python's GIL concurrency model with none of the event-loop ceremony:
every blocking stdlib call releases the lock, so I/O-bound tasks genuinely
overlap. await works at the top level.
Undefined variables, assignment to builtins and misplaced break
are compile-time errors. Runtime errors carry line numbers โ identical
messages from both engines.
Nine native modules: fs, math, os,
json, time, re, http
(client and server), proc, net.
All failures catchable.
Multi-line REPLs on both engines, an LSP server (cobra lsp)
with live diagnostics from the real parser and compiler, and a VS Code
extension with highlighting and IntelliSense.
Six equivalent workload pairs, timed against CPython 3.14 on an Apple M-series laptop (wall clock, best of 3). The harness refuses to report numbers unless all three engines print byte-identical output.
| benchmark | workload | Cobra VM | python3 | ratio |
|---|---|---|---|---|
| fib | fib(32), recursive calls | 0.15s | 0.15s | 0.97x |
| loop | 10M-iteration while + arithmetic | 0.37s | 0.53s | 0.7x |
| lists | build + iterate 2M elements | 0.13s | 0.17s | 0.8x |
| dicts | 1M inserts + lookups | 0.18s | 0.29s | 0.6x |
| strings | 200k concat/join/split | 0.04s | 0.04s | 0.8x |
| structs | 1M objects + method calls | 0.20s | 0.21s | 0.96x |
| total | 1.06s | 1.39s | 0.8x |
Honest caveats: these are microbenchmarks; Python + NumPy wins anything numeric, and PyPI's ecosystem is thirty years deep. Cobra's lane is fast hermetic scripts, embedded scripting in Go applications, and being small enough to fully understand.
One binary, every mode. Compiled .cobrac files are detected
by content, so they run with the plain form too.
| command | what it does |
|---|---|
cobra app.cobra | run a program on the tree-walking engine |
cobra --vm app.cobra | run on the bytecode VM โ the fast engine |
cobra / cobra --vm | interactive REPL (multi-line aware, state persists) on either engine |
cobra build app.cobra [out.cobrac] | compile ahead-of-time to a bytecode file |
cobra app.cobrac | run compiled bytecode โ no parsing, no compiling |
cobra app.cobra arg1 arg2 | extra arguments reach the program via os.args() |
cobra --ast app.cobra | print the parsed AST instead of running |
cobra --tokens app.cobra | print the raw token stream from the lexer |
cobra lsp | speak the Language Server Protocol over stdio (any LSP editor) |
cobra --version | print the stamped release version |
$ cobra build todo.cobra # compile next to the source wrote todo.cobrac $ cobra build todo.cobra dist/todo # or pick the output path wrote dist/todo $ cobra todo.cobrac :9000 100 # run it โ args flow through to os.args() $ cobra --ast todo.cobrac cobra: todo.cobrac is a compiled file; --ast needs source
$ make test # go vet + the full ~500-test suite
$ make build # optimized binary for this machine -> dist/cobra
$ make install # build and place cobra on your PATH
$ make release # test, then cross-compile: macOS arm64/amd64,
# linux amd64/arm64, windows amd64 (~7 MB each, static)
$ make bench # the benchmark suite against python3
Every snippet below is parsed by Cobra's real parser as part of this page's build check.
let declares, plain = reassigns. Truthiness, operator
precedence and and/or returning the deciding operand all follow Python.
let name = "cobra"
let level = 3
if level > 5 and name != "viper"
print("expert")
elif level > 1
print("getting there")
else
print("new")
end
let total = 0
for i in range(1, 11)
if i % 2 == 0
continue
end
total = total + i
end
print(total) # 25 โ odd numbers 1..9
while total > 0
total = total - 10
end
First-class functions with real closures โ inner functions capture and mutate enclosing variables.
def make_counter()
let count = 0
def inc()
count = count + 1
return count
end
return inc
end
let tick = make_counter()
tick()
tick()
print(tick()) # 3
Reference types with deep equality, negative indexing, insertion-ordered dicts, and Python-named methods.
let scores = [70, 95, 80]
scores.sort()
scores.push(60)
print(scores[-1], scores.contains(95)) # 60 true
let ages = {"ada": 36, "alan": 41}
ages["grace"] = 85
print(ages.get("linus", 0)) # 0 โ no KeyError surprises
for pair in ages.items()
print(pair[0], pair[1])
end
let log = "2026-06-12 ERROR disk full"
print(log.split(" ")[1]) # ERROR
print(log.upper().startswith("2026")) # true
print(" | ".join(["a", "b", "c"])) # a | b | c
print(" padded ".strip().replace("a", "@"))
init is the constructor, self is implicit, fields are
open. Instances print themselves and type() returns the struct name.
struct Point
def init(x, y)
self.x = x
self.y = y
end
def length()
return self.x * self.x + self.y * self.y
end
def scale(f)
self.x = self.x * f
self.y = self.y * f
end
end
let p = Point(3, 4)
p.scale(2)
print(p) # Point{x: 6, y: 8}
print(p.length(), type(p)) # 100 Point
Throw any value; runtime errors are caught as their message string. Errors unwind
through calls to the nearest handler; finally always runs.
def divide(a, b)
if b == 0
throw {"code": 400, "reason": "division by zero"}
end
return a / b
end
try
divide(1, 0)
catch err
print(err["code"], err["reason"])
finally
print("cleanup always runs")
end
A module is just a file, run once and cached; its top-level names become members.
Native modules (fs, json, โฆ) resolve the same way.
import "geometry.cobra" import "geometry.cobra" as geo # cached: same module object print(geometry.circle_area(2)) print(geo.Rect(3, 4).area())
import "fs"
import "json"
let config = json.parse(fs.read("config.json"))
config["retries"] = 3
fs.write("config.json", json.stringify(config, 2))
for line in fs.read_lines("server.log")
if line.contains("ERROR")
print(line)
end
end
import "http"
import "json"
import "re"
let resp = http.get("https://api.github.com/repos/golang/go")
if resp["status"] == 200
let repo = json.parse(resp["body"])
print(repo["stargazers_count"])
end
let emails = re.find_all("\w+@\w+\.\w+", fs.read("contacts.txt"))
print(re.replace("(\w+)-(\w+)", "left-right", "$2-$1"))
Handlers are plain Cobra functions. A raised error becomes a 500 and the server keeps running.
import "http"
import "json"
let todos = []
def handler(req)
if req["path"] == "/api/todos" and req["method"] == "POST"
todos.push(json.parse(req["body"]))
return {"status": 201, "body": "created"}
end
if req["path"] == "/api/todos"
return {"status": 200,
"body": json.stringify(todos, 2),
"headers": {"Content-Type": "application/json"}}
end
return {"status": 404, "body": "not found"}
end
http.serve(":8080", handler)
Calling an async def returns a task immediately. The GIL releases
during every blocking call, so these three fetches take as long as the slowest one,
not the sum.
import "http"
async def fetch(url)
return http.get(url)["status"]
end
let tasks = [
fetch("https://example.com"),
fetch("https://example.org"),
fetch("https://example.net")
]
for status in await tasks
print(status)
end
try
await fetch("not a url")
catch err
print("caught:", err) # task errors surface at the await
end
import "proc"
import "net"
let r = proc.shell("git log --oneline | head -3")
print(r["stdout"], r["code"])
let conn = net.connect("example.com:80")
net.send(conn, "HEAD / HTTP/1.0\r\n\r\n")
print(net.recv_line(conn))
net.close(conn)
A tree-walking evaluator is the readable reference; the bytecode VM is the performance engine. They share one implementation of every value operation, and the test suite requires identical output and identical error messages from both.
Register-cached dispatch loop, frames in a preallocated value array (zero
allocation per call), CPython-style small-int interning, an
OpLocalConstBin superinstruction, and an open-addressing dict with
hashes cached on string objects.
~500 tests across 12 Go packages: exact-message error tests, cross-engine equivalence on every example file, live-socket server tests, race-detector runs on the async paths, and benchmarks gated on output equality with Python itself.
Cobra v0.2.0 โ one static binary per platform. No installer, no runtime,
no dependencies: download, chmod +x, run.
| platform | file | size | md5 |
|---|---|---|---|
| macOS (Apple Silicon) | cobra-0.2.0-darwin-arm64 | 6.7 MB | 9feaeaaa8db656ea810873020f1b8016 |
| macOS (Intel) | cobra-0.2.0-darwin-amd64 | 7.0 MB | 61d595cd66cfe1989c5ddad2a25321d5 |
| Linux (x86-64) | cobra-0.2.0-linux-amd64 | 6.8 MB | 1aaf2996660cc063f9365d46c19dd5bb |
| Linux (arm64) | cobra-0.2.0-linux-arm64 | 6.5 MB | 971abc564efc91c131c9331b1d6a7484 |
| Windows (x86-64) | cobra-0.2.0-windows-amd64.exe.zip | 2.9 MB | 4b2a36a501148b0ca884bfd2c7907ca7 |
| VS Code extension | cobra-lang-0.1.0.vsix | 9.7 KB | 46a8eceaa8eae1d58ecea36eba851496 |
Install the extension with code --install-extension cobra-lang-0.1.0.vsix โ
highlighting and snippets work standalone; diagnostics light up when cobra is on your PATH.
All hashes in one file: MD5SUMS.
$ curl -LO https://cobralang.baltavista.com/releases/cobra-0.2.0-darwin-arm64 $ md5 cobra-0.2.0-darwin-arm64 # macOS โ on Linux: md5sum $ chmod +x cobra-0.2.0-darwin-arm64 $ sudo mv cobra-0.2.0-darwin-arm64 /usr/local/bin/cobra $ cobra --version cobra 0.2.0