Modules
Every source file is one module. Its first line names the kind of module and its name; the kind decides what the build makes.
Shape of a module
A file is module <kind> <name>;, then build directives, at most one import clause, the declarations, an optional initialize and finalize section, an optional begin body, and end. Test blocks may follow end.; anything else after it is PR0020.
- Source files use the
.pcppextension. - The module name equals the file name without
.pcpp, in exact case; a mismatch is PR0003. - A missing kind after
moduleis PR0002.
The smallest program is an exe whose body prints one line:
module exe hello;
begin
println("Hello from PaskalCPP!");
end.
Kinds
| Kind | What the build makes |
|---|---|
exe | an executable |
dll | a shared library: a .dll on Windows, a .so on Linux |
lib | a static library |
unit | nothing of its own: source compiled into the build of every module that imports it |
Only an exe has a begin body, and an exe must have one: an exe without one is PR0017, a body in a dll, lib or unit is PR0016. A unit given to the compiler as the main module is PR0004.
Imports
import A, B; names PaskalCPP units only, and stands once, right after the directive prologue; an import after a declaration is PR0010.
Importing an exe, dll or lib is PR0013. C and C++ libraries are used through #include, never import: see C++ mixing.
import Foo looks for Foo.pcpp in the importing file's folder, then in the module paths in the order they were added: the standard library folder ($P:res/libs/std), then the main module's @addmodulepath folders. A unit that is not found is PR0012 at its name.
- A unit named twice in one clause is PR0014 at the second name; two units that import each other are PR0011.
- A unit imported along several paths is one unit.
- An imported unit's names are reached qualified,
modules_shapes.Area,modules_shapes.TRect, which always works, or bare,Area, when exactly one unit of the module's own import clause exports the name. That holds in every bare position: a value, a type, a call, a for iterator, anexternalclause's library constant. - A bare name is looked up in this order: the module's own scopes (innermost first), the intrinsics, an own name declared later (SM0002), the imports, a bare choice value (SM0015), then C/C++. So the module's own names win over an import, and a PaskalCPP name wins over a C/C++ one of the same spelling (write
cpp("...")for the C/C++ one). - A bare name that two or more imported units export is SM0020 at the use, with a note at each unit's declaration: qualify it (
a.Name). Import order never decides, and two units' overload sets never merge: the same routine name in two units is ambiguous whatever the parameter types. - Only what a unit marks
exported(anexported routine, anexported typesection, ...) is visible to its importers. A declared but unexported name is SM0006, an undeclared one SM0005. - Imports are not transitive: a unit's own import is not its importer's (SM0007).
Here is a unit and the exe that imports it:
module unit modules_shapes;
exported type
TRect = record
Width: int32;
Height: int32;
end;
// Not exported: only this unit can call it
routine Multiply(const AX: int32; const AY: int32): int32;
begin
return AX * AY;
end;
exported routine Area(const ARect: TRect): int32;
begin
return Multiply(ARect.Width, ARect.Height);
end;
initialize
begin
println("modules_shapes initialize");
end;
finalize
begin
println("modules_shapes finalize");
end;
end.
module exe modules_main;
import modules_shapes;
var
GRect: modules_shapes.TRect;
initialize
begin
println("modules_main initialize");
end;
finalize
begin
println("modules_main finalize");
end;
begin
GRect.Width := 3;
GRect.Height := 4;
println("area = {}", Area(GRect));
end.
modules_shapes initialize
modules_main initialize
area = 12
modules_main finalize
modules_shapes finalize
[Heap] Allocs: 0, Frees: 0, Leaked: 0
The standard library folder
The standard library folder, res\libs\std, holds the units SysUtils, zlib, minizip and raylib. import SysUtils; finds one with no @addmodulepath.
ResolvePath(APath)andResolvePath(APath, ABaseDir)(SysUtils) turn any spelling of a path into one full, normalized path, with the prefixes the directives take:$P:the executable's folder,$D:the current folder,$S:or none the given base, else the current folder. The prefix is read at the start only, in either case; blanks are trimmed and./..folded as text. Each takes astringor aptr to const char(nil = "") and returns astring.- On Windows the result has forward slashes up to 247 UTF-16 units and the long-path spelling past it (
\\?\C:\...,\\?\UNC\srv\share\...); on Linux it is the plain/...path at any length. Resolving a result again gives it back. ExeDir()(SysUtils) is the running program's folder, with no trailing separator.zlib,minizipandraylibare vendor units: each sets the include path, library path and link libraries its C library needs on each official target and#includes its headers, so a program writes onlyimport zlib;and calls the C API (zlibVersion(),compress2,zipOpen64,InitWindow). The libraries are static and linked into the program:bin\holds only the program, and its.pdbatdebugon Windows.- The bundled examples in
res\examplesuse them:zlib\Compress.pcppcompresses and uncompresses a text and checks its crc32,minizip\PasswordZip.pcppwrites a password-encrypted .zip beside the program and reads it back, andraylib\BasicWindow.pcppopens a window with a line of text.
Initialize and finalize
Every module kind may have an initialize and a finalize section, each at most once, initialize first, after the declarations; a repeat or a wrong order is PR0018.
A section is a routine with no parameters and no result: local var, const and type blocks, then begin ... end;. return; leaves it early; return with a value is SM0611.
Every imported unit's initialize runs before its importer's, in dependency order (not the order of the import clause), then the importer's, then the exe body. finalize runs in the reverse order. The output above shows the order.