Module system
Work in progress
The module system is incomplete — both the implementation and this documentation. Use at your own risk.
Gofra has two ways to inject code from other files:
#include— the C/C++ preprocessor way. See Reference: preprocessor.import— the module system. This page.
The module system is enabled with the FEATURE_ALLOW_MODULES
flag in libgofra.
What is a module
A module is a single .gof file, compiled separately into its own object
file. The root module is the one passed to gofra.
Example:
// a.gof — root module
import b
func void main[]
call b.hello_world
end
// b.gof — dependency
#include "std"
pub func void hello_world[]
"Hello, World!" call println
end
Compiling a.gof:
- The compiler parses
a.gofand findsimport b. - It locates and compiles
b.gofintob.o. - It compiles
a.gofintoa.o, resolvingb.hello_worldagainstb.o. - It links
a.oandb.ointoa.
Import syntax
import a // a.gof as `a`
import a as my_module // a.gof as `my_module`
import "my_file.gof" as m // my_file.gof as `m`
Visibility
By default, all symbols are private. Mark exposed symbols with pub:
| Visibility | Meaning |
|---|---|
pub |
Available in the module and its dependants |
| private (default) | Available only within the module |
Calling a private symbol from another module is a compile error.
Search order
Module search follows the same rules as #include:
- The directory of the current file.
- The toolchain's working directory.
- Include paths (
-isd/--include-search-dir).
Dependency order
Dependencies are resolved from source order. A DAG topological sort is applied before codegen:
a -> b -+-> c
|
+-> d
Dependencies are compiled first, then the root module.
Type checking and optimization
Every module is type-checked independently. Each function symbol is re-checked on compilation.
The optimizer runs in two phases:
- Dependencies first, in topological order.
- Root module last, over already-optimized dependencies.
This lets the root module apply expensive, high-level optimizations over dependencies without re-optimizing them.