Documentation/Chapters

Modules, projects and packages

On this page

Modules keep related declarations together. A project describes which source is the entry point and which local packages it depends on. Neither requires importing code to execute hidden initialization.

Split a program into files

Save this module as greetings.bas beside the entry file:

PUBLIC FUNCTION Greeting(name AS STRING) AS STRING
    RETURN "Hello, " + name
END FUNCTION

Save the entry as main.bas:

IMPORT "greetings.bas" AS greetings
PRINT greetings.Greeting("Ada")

Build main.bas. Quoted paths resolve relative to the importing file. Without AS, the filename stem supplies the alias. Identifiers are case-insensitive; Linux file paths are case-sensitive. Imports do not inject names unqualified or re-export another module’s imports. Use the alias explicitly.

Only the designated entry source has executable statements. Imported modules contain declarations and imports. Cyclic source imports and cross-file recursion are supported because declarations are discovered before body checking. Unused user/imported bodies are still checked; emission prunes unreachable code.

Design a module boundary

PUBLIC exposes supported declarations; PRIVATE hides them from qualified external access. Unmodified module declarations are public for compatibility. A public signature cannot expose a private type or private error contract. Entry locals are not mutable module globals; PUBLIC DIM does not create global storage.

Save this module as counter.bas:

OPAQUE TYPE Counter
    PUBLIC Label AS STRING
    Value AS INTEGER
END TYPE
PUBLIC FUNCTION Create(label AS STRING) AS Counter
    RETURN Counter { Label=label, Value=0 }
END FUNCTION
PUBLIC FUNCTION Current(counter AS Counter) AS INTEGER = counter.Value
PUBLIC SUB Increment(BYREF counter AS Counter)
    counter.Value += 1
END SUB

This entry uses the public operations and reads the public label:

IMPORT "counter.bas" AS counters
counter = counters.Create("Visits")
counters.Increment(BYREF counter)
PRINT counter.Label
PRINT counters.Current(counter)

Clients can read Label but cannot assign it, borrow it BYREF or transfer it out. Value and construction remain module-controlled. The module’s own code can mutate when ordinary exclusion rules allow it. Read-only public fields do not imply that all aliases throughout the program are globally immutable.

Create a project manifest

A basic.toml in the project directory can select an entry and dependencies:

manifest-version = 1
[project]
name = "hello"
version = "0.1.0"
[toolchain]
compiler = ">=0.1.0, <0.2.0"
[target]
kind = "executable"
root = "main.bas"
build/selfhost/stage3 build --manifest basic.toml
build/selfhost/stage3 run --manifest basic.toml -- --help

Manifest paths are relative to the declaring manifest. Arguments after -- go to the application; run preserves its terminal I/O and exit status. help build works without loading a project. The manifest reader supports the documented schema-specific TOML subset, not a general-purpose TOML parser.

Depend on local packages

[dependencies]
Utilities = {path = "../utilities", version = "0.1.0"}
Graphics = {path = "../graphics/build/graphics.blib", version = "0.1.0"}

IMPORT Utilities resolves the declared alias. A source dependency names a project directory; a prebuilt dependency names a .blib. Compiler version ranges are checked transitively, and dependency versions can assert an exact package version. Remote fetching, lockfiles and shared incremental analysis caches are not implemented. Imports do not download dependencies.

For a library, set kind = "library". A .blib contains checked interfaces, precompiled nongeneric code, generic source templates, transitive native inputs and notices. The internal BASIL ABI is versioned; rebuild source when a package is incompatible with the consuming toolchain. Renaming a file does not make its ABI compatible.

Declared system native libraries use the explicit hosted linker bridge and matching SDK inputs. Ordinary static programs use the integrated emitter/linker. See the project reference for archive declarations, dependency identity, output paths and current debugging boundaries.

INTERNAL visibility is deferred and is not supported syntax. Use the implemented PUBLIC/PRIVATE module boundaries rather than relying on an intermediate package-visible access level.

From the BASIL documentation. View source ↗

Find your way around BASIL

Type to search the manual.