The SOF Module System
Each SOF source code file ending in .sof is a separate module.
Sub-folders can be used to define sub-modules, where all the modules in the folder are considered children of the module with the same name as the folder.
Errors
It is an error to import a module that is already currently executing. In other words, the module import graph must be acyclic.
Module names
The name of a module can consist of any Unicode code points from the following set:
- ASCII Alphabetic (code points
U+0041-U+005AandU+0061-U+007A) - ASCII Numeric (code points
U+0030-U+0039) - The characters
_and..
Absolute modules do not start with a dot .. They are resolved using library module resolution.
Relative modules start with a .. They are resolved using relative resolution.
Module resolution
The purpose of module resolution is to resolve a module name that has been specified in a source module to a path which should be loaded as the corresponding target module. The result of module resolution depends only on the interpreter’s library configuration, the current module, and the target module name. Interpreters MAY cache previously-loaded modules by path.
A module name can be resolved to a path by replacing all single dots except leading dots with the system path separator, and appending the .sof extension. Double dots are not replaced.
Note
The system path separator is
/on Unix-like systems, and\on Microsoft Windows. Other systems may have other separators, and/is used as a fallback if there is no concept of files, such as in WebAssembly.
Interpreters MAY allow alternative extensions for modules, but files with the .sof extension MUST take priority over any other files with the same base name during module resolution.
The resulting path is a relative path. Further resolution to a canonical absolute path depends on whether the module is relative or absolute.
Relative modules
Relative module names are resolved relative to the current module’s location. For this purpose, the relative path formed by the common module resolution procedure is appended to the directory of the current module.
In relative module imports only, double dots .. are used to import a directory higher. Each .. causes the remaining module path to be interpreted at the next-higher directory instead.
The highest directory possible is the directory of the root module of the program if the relative import chain originates from the root module, or the directory of the libraries if the relative import chain originates from a library module that was imported absolutely.
Absolute modules
Absolute modules import modules in the library directory. This is a runtime-constant directory. Interpreters SHOULD allow the user to change this directory at startup, but MUST NOT allow it to be changed during runtime.
The library directory contains the SOF standard library. It MAY also contain modules added manually by the user or by package managers.
Modules imported absolutely are allowed to perform relative imports.
Module primitive tokens
use, export, and dexport are the only primitive tokens that directly interact with the module system.
Exporting bindings from modules
Each module maintains an associated list, similar to a nametable, of exports.
When exporting a (name, value) binding from a module, this binding is stored in that list.
If a binding for the same name already exists, that binding is overwritten.
Importing module bindings
After a target module finishes executing, its exported bindings are imported into the source module that loaded it.
To import module bindings, all exported bindings are individually stored the source module’s global nametable. Existing bindings in the global nametable are overwritten with new bindings from the target module’s exports.
Note
This behavior is exactly the same as if every exported item was
globaldef’d in the source module.