LEVIATHAN v962456e · 962456eee1

Metaprogramming

import() — comptime file inclusion

std::import(path) reads a project file at compile time and folds to its contents as a comptime string.

since 0.1.0-alpha.1linuxwindowswasm

Description

std::import(path) reads a file while the program is being compiled and evaluates to its contents as a string. It is meant for a comptime initializer, so the text of a template, a table or a script becomes part of the program without being read at run time:

Including a template

comptime string tpl = import("views/index.html");

bool hasPlaceholder(string s) => s.contains("{{name}}");
comptime bool ok = hasPlaceholder(tpl);

console.writeln(tpl.length());
console.writeln(ok);
not run — reads a file at compile time

The file becomes a declared build input of the program. It has the same standing as a .lev source file: the same inputs always give the same result.

A file that your project depends on has to be named in the project manifest. For a project built with trident, list the files in assets in trident.toml:

name = "shop"
version = "0.1.0"
sources = ["src/*.lev"]
assets = ["views/index.html", "views/footer.html"]

import("views/index.html") in that project reads the declared file, and a path that is not in the list is a compile error that names assets.

Rules

  • A path is plain, project-relative and /-separated. It has no leading /, no \, no empty text, and no . or .. segment. These are checked on the text before the file system is touched, so a path that is shaped like an escape is refused as written and never normalized.
  • A build with a manifest looks the path up in the assets of the module that contains the importing file. A dependency's import() sees only its own declared assets, never those of the app that uses it, and the app never sees the dependency's. A file compiled by itself, with no manifest, resolves the path relative to the source file's own directory.
  • A file is read once per compile. Two comptime sites that import the same path see identical text even if the file changes while the compiler runs.
  • leviathan --assets file.lev lists every file an import() consumed, with its size, hash and module.
  • There is no size limit on the file. The comptime step budget (see lang.comptime) bounds a runaway loop that imports in each iteration.
  • A call to import() that is reached at run time, not folded at compile time, throws a catchable RuntimeException. The behaviour is the same on every engine.
  • A function named import that you declare yourself is never affected: only the standard library's import is folded.
  • A file that cannot be read is a compile error.

Examples

At run time import() has nothing to fold, so it throws. This program catches the exception:

import() at run time throws

try {
    string s = import("views/index.html");
    console.writeln(s);
} catch (RuntimeException e) {
    console.writeln("import() only works at compile time");
}
import() only works at compile time

Your own function named import is an ordinary function:

A user function named import

int import(string name) => name.length();

console.writeln(import("views/index.html"));
16

Notes

  • Compile-time evaluation is otherwise hermetic (see lang.comptime); import() is the one declared file input.
  • comptime string results can feed other compile-time code, such as a function that parses a template.

See also

  • comptime — run the language at compile time — comptime variables, expressions and if statements evaluate ordinary code during compilation and fold the result into the program.
  • Compile-time metaprogramming — A map of the four compile-time layers, procedural macros, and the --expand flag that shows what a rule produced.
  • import — Read a file at compile time and return its content as a string.