https://github.com/aiez/luk
luk is the .luk language:
Lua plus fn, ^ for return, :=
locals, !=, and Python-style comprehensions. Blocks stay
pure Lua (then/do/else/end), so any Lua is (almost) valid
luk. One ~70-line module, luk.lua, does whole-source
transpilation and installs a require() hook for
.luk modules.
git clone https://github.com/aiez/luk && cd luk
./luk fft.luk # transpile + run, args pass through
./luk -d fft.luk > fft.lua # dump generated Lua
make fft.lua # same, via MakefileFor the optimizer shipped with luk (fft.luk) see fft.md.
Sections: NAME | SYNOPSIS | LANGUAGE REFERENCE | PERFORMANCE | FILES | VIM SUPPORT | SEE ALSO | LICENSE | AUTHOR
Files: luk.lua | fft.luk | lib.luk | fft.lua | lib.lua | fft.md | luk.rc | luk.vim
NAME
luk - .luk-to-Lua transpiler (single-file, no deps)
SYNOPSIS
./luk FILE.luk [args...] # transpile + run
./luk -d FILE.luk # dump generated Lua
lua -e 'io.write(require"luk"(io.read"*a"))' <IN.luk >OUT.lua
-- or programmatically:
-- local lua_src = require("luk")(luk_src)
Requiring luk also installs a require()
hook: require"xx" loads xx.luk if present
(transpiled, with real error line numbers), else falls back to plain
Lua. require"xx.luk" forces the .luk
version.
LANGUAGE REFERENCE
Blocks are pure Lua: if c then ... else ... end,
for ... do ... end, while ... do ... end.
Everything below is whole-source token rewriting; strings and comments
are hidden first, so sigils inside them are safe.
Keywords
fn -> function
!= -> ~= (Lua's not-equal)
Return
^ EXPR -> return EXPR
^ means return only at a statement start: start of line,
or after ;, then, do,
else, or a fn(...) parameter list. Infix
exponentiation a^b is untouched.
double := fn(z) ^ z * 2 end
pick := fn(b) if b then ^ "yes" else ^ "no" end end
Local declarations
NAME := EXPR -> local NAME = EXPR
A, B := X, Y -> local A, B = X, Y
Comprehensions (may span lines; no nesting)
[EXPR for V in ITER] -- list
[EXPR for V in ITER if COND]
{K, V for K, V in ITER} -- dict
{K, V for K, V in ITER if COND}
ITER auto-wrapping:
- 1 var, no "(" in ITER -> ipairs(ITER)
- 2 vars, no "(" in ITER -> pairs(ITER)
- else passed through as-is
Limit: a dict comprehension's key expression must not contain a bare comma.
Misc
- No compound assignment: write
x = x + 1, notx += 1. - No shebang line in
.lukfiles (load() rejects#). - Long strings/comments
[[...]]pass through untouched.
PERFORMANCE
Runtime, default mode (depth=4, 16 trees built):
file rows fft.py fft.lua fft.luk (transpile+run)
-------- ----- ------ ------ -----------------------
auto93 398 0.080s 0.038s 0.035s
SS-N 53663 9.18s 5.86s 6.15s
Lua 1.5x-2.5x faster than Python. Transpile is whole-source gsub, ~1ms for a 250-line file: negligible on any real workload.
FILES
luk.lua .luk -> .lua transpiler + require() hook
luk runner: transpile + run (./luk FILE.luk)
lib.luk "battery" helpers (argmin, sum, csv, of, ...)
fft.luk example: multi-objective regression tree
Makefile rule: %.lua: %.luk luk.lua
sandbox/ retired v0.1 indentation-based dialect (luk2)
VIM SUPPORT
syntax: luk.vim (Lua syntax + luk overlay)
shell with .luk-aware vi: make fsh (see luk.rc)
SEE ALSO
fft.md help page for the fft.lua app
https://github.com/aiez/fft Python sibling project
https://github.com/aiez/optimiz example CSVs
https://github.com/aiez/konfig shared Makefile
LICENSE
MIT. (c) 2026 Tim Menzies.
AUTHOR
Tim Menzies <timm@ieee.org>
built by gistsite