Compile-time Code

Refingere can run ordinary code while the program compiles and put the result into the program: lookup tables, parsed configuration, a registry of every function with some attribute. It's the same language, so the code can run at either time - you choose when with #comptime(...). To generate code rather than values, write a macro.

Constants

#comptime(...) around a const's value makes the compiler evaluate it and write the result into the program. The program then starts with the finished value, as if it had been typed in by hand. The functions it calls are ordinary ones, which can also be called at run time.

Here the table is the 256 entries CRC-32 checksums are computed with - the checksum zip files, PNG images and Ethernet use. crcOfByte is @pure, so it can run at either time, and the test computes the checksum it expects while compiling too.

Single expressions

#comptime(expression) works on any expression anywhere in the code, not only a constant's value. A test can compute its expected value while compiling, and only compare at run time.

Functions

A function isn't marked to run early - a call to it is, by putting the call in #comptime(...). The same function can then be called at run time elsewhere. The call's arguments have to be known while compiling.

What can run early

Code that runs while compiling can depend on what's known then - its arguments, constants, and other compile-time values - and on the file system. The natural fit is @pure code, whose result depends only on its arguments.

&fs is always there while compiling: it's the compiler's own file system, so compile-time code can read data files, templates or schemas and put what it makes of them into the program, or write files of its own. A relative path is read from the folder of the file the code is in, the way a source file refers to its neighbours, so the build doesn't depend on where the compiler was started. Every path it hands &fs is an input of the build, the same as a source file: the compiler notes it, and --watch builds again when one changes. &env, &stdout and &stderr are the compiler's too. The rest of the outside world - the clock, randomness, the program's arguments and input - belongs to the running program, and reading it while compiling is an error.

#runtime(...) is the opposite of #comptime: it makes sure an expression is evaluated only when the program runs, even where the compiler could have worked it out early.

Looking at the program itself

#comptime.definitionsWith("name") is every definition in the project marked @name, as a constant list. Each result has the definition's name, kind and file, the attribute's arguments, and the definition itself as a value - here, the function to call. Adding a command is only a matter of marking a function, with nothing to register. See Querying the program.

#comptime.typeof gives the type the compiler worked out for an expression, and #runtime.typeof the type of the value it holds when the code runs. They differ when the static type is an interface, a union or a possible error. #comptime.program() gives the whole program's syntax tree, for code that needs to look further.