Library / SDK
vtereshkov/umka-lang avatar
vtereshkov/umka-lang

Umka: a statically typed scripting language you link into C

Umka: a statically typed embeddable scripting language

2,110 stars80 forksCBSD-2-Clause

At a glance

What is it?
Umka is a statically typed, embeddable scripting language written in C99, built as a bytecode compiler and virtual machine that a host program links as a static or shared library. Its build flags are gcc shaped, its documented downloads cover Windows and Linux, and its one performance figure is a chart produced on a 1.9 GHz laptop.
Who is it for?
Umka earns the build for a C program that needs a script layer whose types are checked before the script runs, and for little else. Arriving from a dynamically typed embeddable VM means paying for an extra compile step.
Can I use it commercially?
Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 17 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 6, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Embedding a type-checked script layer inside a C program

Umka exists for one job: letting a C program run scripts that are type checked before they run. The implementation is C99, and the whole language ships as a static or dynamic library with a simple C API, so a host embeds it like any other dependency and never has to be recompiled when a script changes. Its stated principle is explicit over implicit, and the syntax borrows from Go without borrowing its package model. Who this is for is answered by the project list. `tophat` is a 2D game framework; `buum` is a build system with Umka scripting; `kilocat` and `limeka` are ports of the kilo and lite text editors; SpaceSim draws an orbital rendezvous with a software renderer written in pure Umka; Umka OS is a proof of concept operating system written in C and Umka. What all of them share is a C binary that needs behaviour it cannot hardcode. Money, please! was designed and developed in 96 hours for GMTK Game Jam 2024, which is a fair measure of how much of the language a game prototype actually needs.

One source tree, two compile passes, three artifacts

The build is laid out in the repository root rather than hidden behind a package manager, and reading the Makefile tells you most of what you need to know. A wildcard over the C sources feeds two object sets, while the command line front end is filtered out of both library sets and stands alone:

bash
SRCS = $(filter-out src/umka.c,$(wildcard src/*.c))
OBJS_STATIC    = $(sort $(SRCS:src/%.c=$(OBJ_PATH)/%_static.o))
OBJS_DYNAMIC   = $(sort $(SRCS:src/%.c=$(OBJ_PATH)/%_dynamic.o))

Those are the same translation units compiled twice, once per link mode:

bash
STATIC_CFLAGS  = $(CFLAGS) -DUMKA_STATIC
DYNAMIC_CFLAGS = $(CFLAGS) -DUMKA_BUILD $(DYNAMIC_CFLAGS_EXTRA)

Three artifacts come out of the all target: the `umka` executable, `libumka.a` and `libumka.$(LIBEXT)`. That makes the executable the odd one out, since its object list holds a single file, `umka_static.o`, and the front end needs no second compile pass. Only the shared build adds `-shared` or `-dynamiclib` together with `-fvisibility=hidden`, a detail that matters when you embed the library and want the public surface to stay small.

Building the CLI and running the first script

Platform selection is derived rather than detected, and you can override it. Three cases are spelled out, and each picks its own library suffix:

bash
PLATFORM ?= $(shell uname -s)

BUILD_PATH ?= build

ifeq ($(PLATFORM), Linux)
	LDFLAGS               = -lm -ldl
	DYNAMIC_CFLAGS_EXTRA  = -shared -fvisibility=hidden
else ifeq ($(PLATFORM), Darwin)
	LDFLAGS               =
	RANLIB                = libtool -static -o
	LIBEXT                = dylib

From there, three targets cover the usual cases, and `install` copies everything under a prefix that defaults to `/usr/local` unless you change it:

bash
static:  $(UMKA_LIB_STATIC)
dynamic: $(UMKA_LIB_DYNAMIC)
exe:     $(UMKA_EXE)

A first script is four lines long. How the built executable takes a source file is not spelled out in the project's own instructions, which stop at the program text:

bash
fn main() {
    printf("Hello Umka!\n")
}

One gap sits between that build system and its documentation. Documented downloads cover Windows and Linux, with a Homebrew formula and an UmBox package, yet the tree also carries `build_darwin.sh` and the Darwin branch above. macOS is therefore a first class build target and a second class documented download, and the Homebrew formula is the only shortcut the project offers for it.

Constants that become types, interfaces written inline

The syntax tour shows how tightly declarations are bound to each other. A constant can set an array length, which puts a value into a type: `type Arr = [a]real` with `const a = 3` declared above it. Exported identifiers carry a star, as in `const b* = 2.38`. One parenthesised type statement packs the rest of the type system:

go
type (
    DynArr = []int                      // Dynamic array

    MyMap = map[str]real                // Map

    Quat = struct {                     // Structure
        q: [4]real
        normalized: bool
    }
)

Interfaces are declared in place, with their methods, rather than in a separate declaration list, and function types sit in the same block:

go
type (
    Printable = interface {             // Interface
        print(): int
    }

    ErrFn = fn(code: int)               // Function
)

Composite literals then need no type name at the point of use, so `q := Quat{{1, 0, 0, 0}, true}` works off inference alone. Fibers carry multitasking and a garbage collector covers memory, but the tour never says how either behaves at runtime, and `type IntPtr = ^uint16` stops at pointer syntax without a word on lifetimes. Those gaps belong to `doc/lang.md`.

One public header, two link modes, two example files

Compiler flags tell you what a host actually gets on disk:

bash
CFLAGS = -s -fPIC -O3 -Wall -Wno-format-security -malign-double -fno-strict-aliasing -DUMKA_EXT_LIBS

The -s flag asks the linker to strip what it writes, and `-fPIC` plus `-fno-strict-aliasing` are the price of shipping the same code as a library and an executable. A single header, `src/umka_api.h`, is named as the public API, and the install target prints the library, include and binary directories so a packager can set `PREFIX` and `DESTDIR` rather than writing its own copy step. The bundled embedding example is the clearest statement of the intended split: `3dcam.c` and `3dcam.um` sit side by side, C owns the window and the frame, and the script owns what the frame does. `raylib` has to be present to compile and run that example. Editor support is scattered the same way. `editors/Umka.sublime-syntax` is the only grammar inside this repository; Sublime aside, the VS Code extension, Vim, Kakoune, Emacs, Zed and the Unreal Engine plugin called `UEmka` all live in other people's trees.

The one benchmark is a chart from a 1.9 GHz laptop

A single performance figure exists in the repository, and it is a picture. The caption reads `400 x 400 matrix multiplication` on an AMD A4-3300M @ 1.9 GHz, Windows 7, and the image behind it is `resources/perf.png`. No table of timings, no comparator language, and no build flags for that particular run appear anywhere next to it. An embeddable language is judged against whatever the host already ships, and this chart names no such host. Read it as evidence that the raytracer example completes, not as a baseline you can plan around. The rest of the project list carries the same absence of measurement: SpaceSim, the Hover! maze demo, VDrift/Umka and TractorSim3D are described in one line each with no version, dependency or frame rate attached.

Build glue: an Umka script beside the Makefile rules

Inside a project like `tophat`, the real alternative to Umka is not another language but the glue already in the tree, a Makefile of explicit targets plus a shell script for whatever the pattern rules cannot express. `buum` takes that step, a build system with Umka scripting used in `tophat`, so the comparison has a concrete shape. In a Makefile every new kind of input file is another pattern rule; in an Umka script it is a structure the script loops over, and a mistyped flag name is rejected when the script compiles rather than at the end of a long build. Where Umka is the wrong tool is the reverse case, work that has to touch host memory directly, since `^uint16` is typed but the tour documents no lifetime rules, and pointer heavy code belongs in C where the compiler already enforces it. Another common shape for this role, a dynamically typed VM embedded through its own C API, checks types as the script runs; Umka compiles first, which moves failures earlier and costs a compile step the dynamic design never pays. Readers arriving from Go get a dedicated differences section linked from the repository, and it is worth reading before porting anything.

Platforms the bundled Makefile never reaches

Not every platform in the tree goes through the Makefile. Its CFLAGS string holds `-malign-double`, `-Wno-format-security` and `-fvisibility=hidden`, none of which the Microsoft toolchain accepts, and there is no `MINGW64_NT` branch for MSVC at all. Windows builds there run through `build_windows_msvc.bat`, with `build_windows_mingw.bat` and `build_linux_mingw.sh` covering the other two routes. The browser playground runs on its own chain, `build_playground.bat`, `build_playground_linux.sh`, `run_emscripten.bat` and `run_emscripten_linux.sh`, landing in the `index.html` and `playground/` directories, and UmBox packaging adds the `umbox_pre_build.sh` and `umbox_post_build.sh` hooks around a build. That is a lot of shell and batch files to hold in step with one C tree, and the repository documents no compatibility range for them. For maintenance, the licence is BSD-2-Clause, which lets you modify the tree and ship a closed source host as long as the notice and disclaimer stay with the copies, and the last push landed on 2026-10-06 with version 1.5.7 dated 2026-08-22, after 1.5.6 on 2026-04-06 and 1.5.5 on 2025-12-24.

Editorial conclusion

Umka earns the build for a C program that needs a script layer whose types are checked before the script runs, and for little else. Arriving from a dynamically typed embeddable VM means paying for an extra compile step. On macOS, build from the tree rather than hunting for a download. Check three things first: that a build script exists for your platform, that your host can link the library through src/umka_api.h, and that fiber based multitasking fits the work before you port a single line of existing glue.

Frequently asked questions

How do I build Umka from source on macOS?

The Makefile reads the platform from `uname -s` and has a Darwin branch that sets RANLIB to libtool and produces a dylib, so `build_darwin.sh` or a plain make run covers macOS. The documented download route lists Windows and Linux only, which is why macOS users end up building from the tree. The Homebrew formula is the other option the project offers.

How do I embed Umka in a C program?

Link either `libumka.a` or `libumka.$(LIBEXT)` and include `src/umka_api.h`, the only header the build names as the public API. The bundled example under examples/3dcam shows the intended split: `3dcam.c` holds the window and frame loop, `3dcam.um` holds the behaviour, and `raylib` must be present to compile and run it.

Does Umka have a garbage collector and real threads?

The feature list names garbage collection and multitasking based on fibers. The syntax tour covers neither fiber scheduling nor locking, and the language documentation is where those rules live. Plan around fibers, not around a thread pool, when sizing the runtime.

Can I run Umka in a browser?

Yes, the repository links a playground that is built for the web from the Emscripten scripts. The relevant files are `build_playground.bat`, `build_playground_linux.sh`, `run_emscripten_linux.sh` and the `index.html` plus `playground/` directories at the root. The Makefile targets do not build that path.

Where is the Umka language and standard library documentation?

Three separate documents cover the language, the standard library and the embedding API, at `doc/lang.md`, `doc/lib.md` and `doc/api.md`. The syntax tour covers declarations and types only, so anything about runtime semantics has to come from those files. The Makefile in the root does not read them either.

Official sources

  1. Issues
  2. License: BSD-2-Clause
  3. README
  4. Releases
  5. vtereshkov/umka-lang on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/vtereshkov-umka-lang.svg)](https://hysenlabs.com/projects/vtereshkov-umka-lang)

Was this useful?