Lake

Directives

Directives are compiler attributes that declare runtime functions. They appear at the top of a file, before any machine.

§@rt — runtime function

@rt(name) binds a name to a built-in runtime function. After the directive, name is callable from any branch. Runtime calls are inlined — they execute synchronously in the current process and do not consume reductions on their own.

lake
@rt(rt_write)
@rt(rt_exit)

Each runtime function gets its own @rt(...).

§Available runtime functions

These are wired in the current native compiler. Others may be parsed but will not produce useful behaviour at link time.

FunctionArgumentsBehaviour
rt_writefd str sizewrite(fd, ptr, size) — direct syscall, no buffering
rt_readfd buf sizeread(fd, ptr, size)
rt_allocatesizeallocate size bytes on the heap, returns a fat pointer
rt_exitcodeterminate the process with status code
rt_mmapaddr size prot flags fd offraw mmap syscall

rt_write takes the size in bytes, not characters or codepoints. "hello\n" is six bytes; "привет" is twelve.

§Writing to stdout

@rt(rt_write)

main is {
  _ -> {
    rt_write(1 "hello, lake!\n" 13)
  }
}

The first argument is a file descriptor (1 is stdout, 2 is stderr).

§Exiting with a status

@rt(rt_write)
@rt(rt_exit)

main is {
  _ -> {
    rt_write(1 "bye\n" 4)
    rt_exit(7)
  }
}

The wrapping _start would normally exit with 0. rt_exit overrides this, so the OS sees the value you pass.

§Allocating a buffer

@rt(rt_write)
@rt(rt_allocate)

main is {
  _ -> {
    let buf i64 = rt_allocate(64)
    rt_write(1 "allocated 64 bytes\n" 19)
  }
}

rt_allocate takes a size in bytes and returns a fat pointer to a fresh region of heap memory. Use rt_store and rt_load_u64 to read and write into it.

§Placement

Directives precede any machine:

lake
@rt(rt_write)

main is {
  _ -> {
    rt_write(1 "ok\n" 3)
  }
}

Empty lines between directives and the first machine are optional but conventional.