Skip to content

Imports & libraries

When a program needs code from somewhere else, such as the d/Works widget toolkit or a module of your own, it uses IMPORT. This page explains how module names work, the two ways an imported library can be delivered (copied into your program, or bound to an exact installed library file), which standard libraries exist, how to build a library of your own, and the limits the compiler enforces.

An import answers one simple question: which reviewed implementation does this name mean? d/BASIC gives two honest answers: bake the library's source into the program, or bind the program to an exact compiled library.

A dotted module name

An import takes a line of its own:

IMPORT dworks.widgets

Module names are dotted identifiers, matched without regard to ASCII case. In the example, the public name dworks.widgets maps to the maintained library file dworks/widgets.bas. dworks is the prefix of the standard modules that come from d/Works, the office suite being written in d/BASIC (d/Write, d/Calc, d/Present).

dworks.* and gx (GX is d/BASIC's library for 2D games: scenes, sprites, tilemaps, cameras, collision) are protected standard module names. A project cannot replace either with a local file of the same name.

Baked imports

IMPORT dworks.widgets BAKED

BAKED copies the library's source into your program before parsing begins, so the result is one self-contained compiled program file, .dbc. You can write the word explicitly for a protected d/Works module:

IMPORT dworks.widgets BAKED

Use it when:

  • one portable file is worth more than shared storage;
  • the program and the library are being developed together;
  • no installation step should be needed; or
  • the machine that will run the program does not keep a store of shared libraries.

Expansion is deterministic and depth-first, and each file is expanded once, however many imports lead to it. A library may import another library; if the imports form a cycle, the compiler stops and says so instead of recursing forever.

Shared imports

IMPORT dworks.widgets SHARED

SHARED compiles the program against a shared library file, .dbl, and records an exact dependency on it:

  • the library's identity and version;
  • each exported name and its ordinal, its position in the library's export table;
  • the argument and result types of each exported procedure;
  • the shape of each exported class the program uses; and
  • the SHA-256 digest of the library file's bytes.

When the program is loaded, the d/OS runtime, the program that runs your compiled code, binds those exact bytes. (d/OS is the operating system that brings modern software to 8-bit home computers such as the Atari 800, the Commodore 64 and the Apple II.) If the library is absent, changed or incompatible, the runtime declines before the program starts and names the library it wanted. It never quietly bakes in a different copy and calls that close enough.

Use it when many programs share a large, stable library, or when a d/OS package installs the library alongside the programs that use it.

Every import is baked unless you say otherwise, including the protected modules. Write SHARED explicitly when a matching installed library is part of how the program is delivered:

IMPORT dworks.widgets SHARED

The default is deliberately simple: the source travels with the program unless you name an exact shared library. One program may bake a helper that is still changing and share a stable installed d/Works or GX library, but wherever a dependency on an installed library matters, SHARED is written down.

Baked and shared are delivery choices

The BASIC you write is the same either way:

DIM Save AS WorksButton
Save = NEW WorksButton("save", "Save")

With a baked import, the code behind WorksButton lives inside your program's image. With a shared import, a call may cross into the installed library's image. The compiler's optimizer keeps only the dependencies your program actually reaches; merely offering a shared library does not force a dependency on it into a program that never uses it.

Project-local modules

For a name that is not under dworks.*, the compiler looks for the module below the project root, the directory that holds the entry source file:

my-project/
├── main.bas
└── model/
    └── invoice.bas
' main.bas
IMPORT model.invoice

Path separators, absolute paths, .. and any disguised way of escaping the project are not accepted; the compiler stops and says so. An import is a module name, not permission to read the filesystem.

A library cannot become a second application

Imported source may define:

  • constants and declarations;
  • TYPE records;
  • classes;
  • procedures and the bodies of class members; and
  • imports of its own.

It may not define a second program entry point, nor any of the handlers that make a program a window program: WINDOW SUB lifecycle routines, ON KEY, ON POINTER, WINDOW ACCEPTS or ON DROP. The application owns its one entry point and its event handlers.

That rule is what keeps dworks.widgets a toolkit rather than a hidden window manager.

Standard libraries

Module Purpose
dworks.widgets themes, repaint tracking, controls, menus, and local UI events
dworks.document_media bounded document tables, images, and staging for local transfers
dworks.network HTTP helpers, capability and status records, and sessions that own their connection
dworks.files typed helpers for mounts, directories, bounded file reads and writes, and file edits
dworks.data typed fixed-array wrappers for database reads, browsing, writes, and saved documents
dworks.sha256 wrapper around the system's COMPUTE.SHA256 request
dworks.sha256_oracle a deliberately slow reference SHA-256 written entirely in BASIC, used by the tests
dworks.drag typed helpers for the data a drag session carries
dworks.interchange helpers for exchanging data between applications
dworks.dwrite_dobj d/Write's DOBJ shapes and operations (DOBJ is the typed object a program hands to another program when you drag, drop, copy or paste)
dworks.dwr1 helpers for DWR1 documents
dworks.write_artifacts document save and publish workflows and their confirmations
dworks.calc_model d/Calc model components
dworks.calc_view d/Calc view helpers
gx the GX game engine; the part supported today covers scenes, entities, tiles, cameras, collision and animation over arrays your program owns

These are libraries of source. Importing one gives you declarations and implementations; it does not switch anything on in the host. IMPORT dworks.data does not mount any storage, and IMPORT gx does not give the program a graphics or sound route. The machine running the program must still provide every capability the library or the application asks it for.

Build a library artifact

The desktop toolchain can build a compiled d/BASIC library:

dbasic build-library library.bas -o library.dbl \
  --name example.library --version 1.0.0.0 --id 42

A .dbl describes itself: it is compiled code with a table of exports and no program entry point or window handlers. The publisher's identity and the version are inputs to the build; a library's source cannot claim them for itself.

Import safety limits

The part of the compiler that assembles source enforces limits so that a build is always finite:

  • no more than 128 distinct modules;
  • no more than 1 MiB of expanded source;
  • each file expanded once;
  • no cycles;
  • no escape from the project's source root; and
  • no program entry point or window handlers in imported code.

These limits produce useful diagnostics, not arbitrary inconvenience. A build should not depend on an unbounded chain of includes, or on whichever file happened to exist outside the project today.

What d/BASIC does not mean by import

IMPORT is not:

  • C's #include with textual macros;
  • a request at runtime for an unchecked source path;
  • a search for a DLL by a convenient filename;
  • a plugin loader for one particular machine; or
  • a way to sneak a second application entry point into the program.

The compiler knows either the source or the exact library file. The compiled program records what it depends on.

Library design advice

  • Export concepts, not wrappers for one particular machine.
  • Keep WINDOW and input handlers in the application.
  • Put the packing of typed data for transfer behind small procedures.
  • Keep changing session state in objects the application owns, not in module globals shared by everyone who imports the library.
  • Give public operations stable names and explicit argument modes (BYVAL or BYREF).
  • Test both BAKED and SHARED delivery for a library meant to support both.

Next: Compiler & Optimizer →