Skip to content
hekla

hek, the language tool

The compiler's own CLI, for editors, pre-commit hooks and answering whether an edit changed anything.

hek is heklang’s own tool: a separate binary from a separate crate, for editor integration, pre-commit and the digest.

You do not need it to run a hekla project. hekla check and hekla test run the same compiler and add the runtime’s own rules on top.

curl -fsSL https://hekla.tqwewe.com/install.sh | sh -s -- hek

or cargo install hek.

Usage

hek [check|test|fmt|digest] [--boundaries] [--check]
    [--packed|--hash|--json] [--tests] [path|-]

path is a directory or a single .hk file, and defaults to the current directory. Every file under it is one module of one program, and declaration order across them does not matter.

With no subcommand, hek does both check and test.

Subcommands

hek check parse every .hk file under path as one program
hek test the same, then run every test declaration in it
hek fmt rewrite every .hk file under path canonically
hek digest print what that program does, with everything else taken away

Flags

Flag With
--boundaries check print what each command guards, transitively
--check fmt name what would change and write nothing
--packed digest print the canonical form the hash covers
--hash digest print only the hash
--json digest print the form as JSON
--tests digest include the test declarations

--packed, --hash and --json are three ways of reading one digest, so passing more than one is an error rather than a precedence puzzle. Any of the four digest flags on another subcommand is an error too.

Reading from stdin

hek fmt -

Formats one module from stdin onto stdout, which is what format-on-save wants. It fails rather than printing nothing when the module does not parse, so a broken buffer is never silently emptied.

hek digest - reads one module the same way.

check

hek check
commands/connect-shop.hk:11:18 [unknown-member] no method `trm` on String
commands/connect-shop.hk:18:8 [not-declared] event @shop.reconneced is not declared
commands/ship-order.hk:8:6 [type-mismatch] expected Bool, found String

3 errors

Every mistake is reported, not only the first. See Diagnostics for the full code list and how to read an extent.

--boundaries

hek check --boundaries

One line per command naming what it guards, transitively through every guard it reaches. This is the way to see what a deep guard chain actually costs before it shows up as a 409.

fmt

Formatting changes whitespace and nothing else, and that is checked by re-lexing both sides. It reads the tree-sitter grammar rather than the parser.

The rules are few enough to state: a group is one line or one line each, blank lines are yours, and a comment is a line.

What it deliberately does not do: remove parentheses, break a dot chain, break a boolean condition, hug a last argument, re-wrap a comment, align columns. And there are no options at all, which is the point.

hek fmt --check

names what would change and writes nothing. That is the pre-commit gate.

digest

The digest is a deterministic rendering of what a program does, with everything that does not affect behaviour taken away. const, refusal, guard and secret are inlined and do not appear.

hek digest --hash

Two programs with the same hash do the same thing, so this answers “was that refactor actually a no-op” cheaply, before running anything expensive.

--tests includes the test declarations, which are otherwise their own section and outside the hash.

Exit status

0 read fine and passed
1 read fine and did not pass, or could not read at all

The distinction between those two 1s is visible in where the output goes: a program that did not pass reports diagnostics on stdout, and a failure to look at all goes to stderr with a hek: prefix.

digest is the exception. Its stdout carries the document and nothing else, so that --packed | sha256sum matches --hash, and its diagnostics go to stderr unprefixed.

hek check does not fail on a failing test, because a test that fails is a program that parsed. hek test does.

Editor support

There is no language server yet. The tree-sitter grammar is what editors use for highlighting, and hek fmt - is what they use to format on save.

Editor setup has the configuration for Helix, Neovim, Zed, VS Code and Emacs.

In CI

hek fmt --check
hekla check .
hekla test .

Note the last two are hekla, not hek. The two tools have different rule sets, and a program can pass hek check and fail hekla check. See hekla, the runtime.