Cobra

A small, fast scripting language with its own simple syntax β€” blocks close with end, not indentation. Shipped as one static binary, with no-GIL parallel async/await, structs, modules, slicing and a batteries-included standard library β€” and it compiles to native machine code.

Download v0.13.0 Quick start See examples cobranum (scientific)
two verified-identical engines compiles to native code ~7 MB static binary REPL Β· LSP Β· VS Code

Sixty seconds of Cobra

Simple syntax, 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 --native fib.cobra # compile to machine code, run it
$ 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

Why Cobra

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.

Compiles to native code

cobra build --native turns a program into a standalone machine-code executable β€” no interpreter, no bytecode. Whole-program type inference unboxes your integers and floats and lays structs out flat, so the binary runs the numeric benchmarks faster than PyPy's JIT and needs nothing installed to run.

A fast bytecode VM

For everything else, the VM runs the benchmark suite ~4x faster than the tree-walker β€” registerized dispatch, fused superinstructions, allocation arenas, small-int interning, and a custom dict with cached string hashes.

One static binary

No interpreter install, no venv, no dependency hell. Copy a ~7 MB binary anywhere and run .cobra source or compiled .cobrac bytecode.

async / await, no GIL

Real multi-core parallelism: async tasks run on every core at once β€” real speedup on CPU-bound work, not just I/O overlap. Guard shared state with Mutex, pass values over Channel. await works at the top level.

Errors before runtime

Undefined variables, assignment to builtins and misplaced break are compile-time errors. Runtime errors carry line numbers β€” identical messages from both engines.

Batteries included

Eleven native modules: fs, math, os, json, time, re, http (client and server), proc, net, test (a built-in unit-test framework) and runtime (GC + memory stats). All failures catchable.

Real tooling

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.

Benchmarks

Six workloads, run on both of Cobra's engines on an Apple M-series laptop (wall clock, best of 3). The harness refuses to report numbers unless both engines print byte-identical output β€” the benchmarks double as a cross-engine correctness check. The VM runs the suite ~4x faster than the tree-walker.

benchmarkworkloadtree-walkVMspeedup
fibfib(32), recursive calls1.40s0.13s10.6x
loop10M-iteration while + arithmetic1.21s0.34s3.6x
listsbuild + iterate 2M elements0.32s0.12s2.7x
dicts1M inserts + lookups0.30s0.18s1.7x
strings200k concat/join/split/endswith0.07s0.03s2.1x
structs1M instantiations + method calls0.65s0.16s4.2x
total3.95s0.95s4.1x

These are microbenchmarks. Cobra's lane is fast hermetic scripts, embedded scripting, and being small enough to fully understand.

Native compilation β€” past the JIT

The same programs, compiled to machine code with cobra build --native, against the fastest ways to run the equivalent Python: CPython 3.14 and PyPy's tracing JIT. Five classic numeric benchmarks plus a dictionary workload, written identically in both languages. Every runtime prints identical output; timings are wall clock, best of 3, and exclude the one-off native build (which is cached).

benchmarkCPython 3.14PyPy (JIT)Cobra VMCobra native
n-body, 100k steps0.24s0.04s0.41s0.01s
binary-trees, depth 150.71s0.12s0.60s0.12s
spectral-norm, n=3000.30s0.02s0.29s0.01s
mandelbrot, 1000×10003.98s0.18s2.31s0.09s
word frequency, 300k0.09s0.03s0.07s0.03s
total (with mandelbrot 400²)5.99s0.44s4.05s0.28s

Native compilation covers Cobra's type-stable core (functions, structs, collections, loops, math). A program that uses closures, async, try/catch or inheritance is reported as not natively compilable β€” naming what blocks it β€” and runs unchanged on the VM, which is itself ahead of CPython here.

Parallelism β€” real multi-core speedup

The table above is single-threaded. The bigger story is what happens when work is split across cores: four CPU-bound tasks, run sequentially vs. all at once. With no GIL, Cobra's async tasks run on every core at once, so CPU-bound work scales with the number of cores β€” measured here at ~3.6× speedup on a tight arithmetic loop, not just overlapped I/O.

The command line

One binary, every mode. Compiled .cobrac files are detected by content, so they run with the plain form too.

commandwhat it does
cobra app.cobrarun a program on the tree-walking engine
cobra --vm app.cobrarun on the bytecode VM β€” the fast engine
cobra --native app.cobracompile to machine code and run it (cached by source hash)
cobra / cobra --vminteractive REPL (multi-line aware, state persists) on either engine
cobra build app.cobra [out.cobrac]compile ahead-of-time to a bytecode file
cobra build --native app.cobra [out]compile to a standalone native executable
cobra app.cobracrun compiled bytecode β€” no parsing, no compiling
cobra app.cobra arg1 arg2extra arguments reach the program via os.args()
cobra --ast app.cobraprint the parsed AST instead of running
cobra --tokens app.cobraprint the raw token stream from the lexer
cobra lspspeak the Language Server Protocol over stdio (any LSP editor)
cobra --versionprint the stamped release version

Build workflow

$ 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

Three ways to ship a program

Source is always the input; you choose the output. Native compilation reads the source directly β€” it never goes through .cobrac, because the type inference it performs needs the syntax tree that bytecode has already discarded.

outputhow it runsneeds on the target
cobra build app.cobra β†’ .cobracbytecode, on the VMthe cobra binary
cobra build --exe app.cobrabytecode + VM in one filenothing
cobra build --native app.cobramachine codenothing
$ cobra build --native mandelbrot.cobra
built native executable mandelbrot
$ file mandelbrot
mandelbrot: Mach-O 64-bit executable arm64      # ~2 MB, fully static

$ ./mandelbrot                                  # no cobra, no runtime, no JIT warm-up

Building a native binary needs the Go toolchain on the build machine β€” Cobra uses it as its code generator, the way other languages use LLVM. Running one needs nothing, and no other Cobra workflow (running, the REPL, .cobrac, --exe) touches Go at all.

Building Cobra itself

$ make test       # static checks + 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 (tree-walker vs VM)

The language by example

Every snippet below is parsed by Cobra's real parser as part of this page's build check.

Variables, control flow, loops

let declares, plain = reassigns. Truthiness, operator precedence and and/or returning the deciding operand all work as you'd expect. For one-liners there's a C-style ternary, cond ? a : b.

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 tier = level > 1 ? "active" : "idle"   # ternary: cond ? a : b
print(tier)

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

Functions and closures

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

Lists and dicts

Reference types with deep equality, negative indexing, insertion-ordered dicts, and familiar method names.

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

Strings

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", "@"))

Structs β€” simple classes

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

Errors: try / catch / finally / throw

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

Modules

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())

File I/O and JSON

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

HTTP client and regex

import "http"
import "json"
import "re"

let resp = http.get("https://api.github.com/repos/cobra-lang/cobra")
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"))

An HTTP server, routed with @route

Routes register themselves with a decorator; handlers are plain Cobra functions. A raised error becomes a 500 and the server keeps running. Requests run in parallel, so shared state gets a Mutex.

import "http"
import "json"

let routes = {}
def route(method, path)
    def register(f)        # the decorator: record the route, return f
        routes[method + " " + path] = f
        return f
    end
    return register
end

let todos = []
let lock = Mutex()

@route("POST", "/api/todos")
def create(req)
    lock.lock()
    todos.push(json.parse(req["body"]))
    lock.unlock()
    return {"status": 201, "body": "created"}
end

@route("GET", "/api/todos")
def list(req)
    lock.lock()
    let body = json.stringify(todos, 2)
    lock.unlock()
    return {"status": 200, "body": body,
            "headers": {"Content-Type": "application/json"}}
end

def handler(req)
    let h = routes.get(req["method"] + " " + req["path"])
    if h == null
        return {"status": 404, "body": "not found"}
    end
    return h(req)
end

http.serve(":8080", handler)

async / await β€” no GIL

Calling an async def returns a task immediately. There is no GIL: tasks run in parallel across every core, so these three fetches take as long as the slowest one, not the sum β€” and CPU-bound tasks get real multi-core speedup too. Shared state is guarded explicitly with Mutex() and Channel().

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

Mutex and Channel

Because tasks run in true parallel, shared state needs synchronizing. A Mutex guards a shared counter; a Channel hands values from producers to a consumer.

let m = Mutex()
let total = 0

async def add(n)
    let i = 0
    while i < n
        m.lock()
        total = total + 1     # read-modify-write β€” guarded
        m.unlock()
        i = i + 1
    end
end

await [add(1000), add(1000), add(1000)]
print(total)                  # 3000, every time

let ch = Channel()
async def produce(x)
    ch.send(x * x)
end
for i in range(1, 6)
    produce(i)
end
let sum = 0
for _ in range(5)
    sum = sum + ch.recv()
end
print(sum)                    # 55

Processes and sockets

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)

Built-in unit tests

import "test"

@test.it
def addition()
    test.assert_eq(2 + 2, 4)
end
@test.it
def value_equality()
    test.assert_eq([1, [2]], [1, [2]])   # deep, like ==
end

let summary = test.run()                 # prints a report
print(summary["ok"])                     # true

Under the hood

Two engines, one semantics

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.

A finely tuned VM

Registerized dispatch loop, frames in a preallocated value array (zero allocation per call), eleven fused superinstructions (compare-and-jump loop conditions, compute-and-store assignments, field access), per-VM allocation arenas, small-int interning, and an open-addressing dict with hashes cached on string objects.

Type inference, then machine code

The native backend proves one concrete type per variable across the whole program β€” a fixpoint over the syntax tree, promoting int to float the way the language does β€” then emits unboxed arithmetic and flat structs. Float operations carry a rounding barrier so the result is bit-identical to the interpreter's, never merely close.

Honest engineering

~500 tests across 12 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 between the two engines.

Download

Cobra v0.13.0 β€” one static binary per platform. No installer, no runtime, no dependencies: download, chmod +x, run.

platformfilesizemd5
macOS (Apple Silicon) cobra-0.13.0-darwin-arm64 9.7 MB7ad8a7e22afda5cecd8b08483a07ac39
macOS (Intel) cobra-0.13.0-darwin-amd64 7.7 MB8dc2ffb9634c7a7201cbe44b32fed190
Linux (x86-64) cobra-0.13.0-linux-amd64 7.5 MB5ad5c54949dfcfeb78ccaca140b41ef4
Linux (arm64) cobra-0.13.0-linux-arm64 7.2 MB2bda0eb9f0430bb423c263a905fbfe65
Windows (x86-64) cobra-0.13.0-windows-amd64.exe 7.7 MB0ddea5879df1c5ee2c20f2fe1ecf6dbd
VS Code extension cobra-lang-0.13.0.vsix 14 KB94ec460f4792921fe6adcffcd508beef

Install the extension with code --install-extension cobra-lang-0.13.0.vsix β€” highlighting (including triple-quoted strings and ${…} interpolation), snippets and debugging work once cobra is on your PATH.

All hashes in one file: MD5SUMS.

Plugins

cobranum β€” a NumPy-style numeric-array library (dense 1-D/2-D arrays, vectorized elementwise ops, reductions, broadcasting and linear algebra). On Apple Silicon it is backed by Accelerate (BLAS/LAPACK) + ARM NEON and beats NumPy on most benchmarks.
πŸ“ cobranum guide & scientific examples β†’

platformpluginneeds binarymd5
macOS (Apple Silicon) cobranum-0.11.1-darwin-arm64.zip the standard cobra-0.13.0-darwin-arm64 065b87bcc172f723ffb52321381b03d0
Linux (x86-64) cobranum-0.11.0-linux-amd64.zip cobra-0.11.0-linux-amd64-plugin (glibc β‰₯ 2.34) 1afff174c7191edb2cf84e1982150003

Unzip to get cobranum.so, put it next to your program (or on the import path), and import "cobranum.so". See the cobranum guide for usage and scientific examples.

About the binaries. Cobra plugins are Go plugins: they only load into a plugin-capable (cgo) cobra binary. The macOS/arm64 release binary is already plugin-capable, so use it directly. On Linux, the default release binary is fully static (no plugins), so a separate dynamically-linked plugin binary (cobra-0.11.0-linux-amd64-plugin) is provided for plugin users. The Linux plugin pair currently tracks 0.11.0; rebuild against a 0.11.1 plugin binary (make build && make plugin PLUGIN=./plugins/cobranum) to pick up the 0.11.1 fixes on Linux.

Other platforms (Intel Mac, Linux arm64, Windows): Go plugins cannot be cross-compiled and are unsupported on Windows, so build from source against your own cobra β€” one command, no extra tooling:

$ make build                                  # a plugin-capable cobra binary
$ make plugin PLUGIN=./plugins/cobranum       # -> cobranum.so
$ ./dist/cobra your_program.cobra             # with cobranum.so on the path

Kitap β€” Cobra Programlama Dili (TΓΌrkΓ§e)

Dili sıfırdan, üretim düzeyinde anlatan 148 sayfalık Türkçe kitap (PDF): temeller, koleksiyonlar, fonksiyonlar, OOP, eşzamanlılık, standart kütüphane, dilin iç yapısı (lexer'dan sanal makineye), yerel makine koduna derleme ve cobranum ile sayısal hesaplama.

πŸ“˜ KitabΔ± indir (PDF, 1.8 MB)

Book β€” The Cobra Programming Language (English)

The full 162-page book in English: the language from the ground up β€” basics, collections, functions, OOP, concurrency, the standard library, the internals (lexer to VM), compiling to native machine code, numerical computing with cobranum, and a web framework and ORM (Venom).

πŸ“— Download the book (PDF, 1.8 MB)

$ curl -LO https://cobralang.baltavista.com/releases/cobra-0.13.0-darwin-arm64
$ md5 cobra-0.13.0-darwin-arm64           # macOS β€” on Linux: md5sum
$ chmod +x cobra-0.13.0-darwin-arm64
$ sudo mv cobra-0.13.0-darwin-arm64 /usr/local/bin/cobra
$ cobra --version
cobra 0.13.0

Changelog

v0.13.0 β€” 2026-07-14

Native compilation, and a substantially faster VM.

v0.12.1 β€” 2026-07-02

Bug-fix release hardening the 0.12.0 features after a differential and adversarial review. No breaking changes.

v0.12.0 β€” 2026-07-01

New string and expression syntax, a live-reload runner, richer time helpers, and call-stack traces for uncaught VM errors. All additive.

v0.11.1 β€” 2026-07-01

Correctness and robustness fixes for the bytecode VM, found by deep differential testing against the tree-walker. No breaking changes.

v0.11.0 β€” 2026-06-28

String interpolation, lambdas, multi-value assignment, a source formatter, new standard-library modules, and a NumPy-style numeric-array plugin.

v0.10.0 β€” 2026-06-27

A module system, augmented assignment, slicing, property setters, and standalone executables.

v0.9.6 β€” 2026-06-27

VS Code: @test.it snippets and a test-module catalog.

v0.9.5 β€” 2026-06-26

A built-in unit-test framework and a runtime/memory module.

v0.9.4 β€” 2026-06-15

A ternary conditional operator.

v0.9.3 β€” 2026-06-13

A debugger, native plugins, and stdin input.

v0.9.2 β€” 2026-06-13

A source-level debugger.

v0.9.1 β€” 2026-06-13

v0.9.0 β€” 2026-06-13

Higher-order list builtins.

v0.8.0 β€” 2026-06-13

Immutable bindings with const.

v0.7.0 β€” 2026-06-13

Richer structs: class constants, properties, and static methods.

v0.6.1 β€” 2026-06-13

v0.6.0 β€” 2026-06-13

Decorators β€” @ above a function.

v0.5.0 β€” 2026-06-13

Wait on many channels at once with select.

v0.4.0 β€” 2026-06-13

More concurrency tools, and a sturdier runtime under races.

v0.3.0 β€” 2026-06-13

The GIL is gone β€” tasks now run with true multi-core parallelism.

v0.2.0 β€” 2026-06-12

v0.1.0 β€” 2026-06-11