This chapter describes how the tokens of chapter 1 form a unit: a program, a library or an object, with the sections, routines and body of its block, the value every variable starts at, and the imports that bring names from other units into it. Each construct names its rule in the grammar, says what it means, and quotes what the compiler answers when a source breaks the rule. What a declaration inside the sections declares is the grammar’s units and declarations and the type system.

Every example is complete, compiled and run by the compiler’s own test suite on AMD64 and AArch64, and the output shown beneath it is the output those runs are held to.

A compilation unit

A source is one unit: its kind, its name, a semicolon, its block and a final period (compilation-unit). The kind says what the unit becomes when it is linked (unit-kind):

KindBecomes
programan executable, named after the program, whose body is what runs
libraryan archive or a shared object, built on its own under a version, whose contract carries its surface to every program built against it later
objecta part of the build it is compiled in, beside a program or a library, which imports it by the unit’s name

A build holds one program or one library, and any number of object units beside it. The name in the heading is what another unit imports by; the name of the file that holds the source does not matter, and the binary a program links to takes the program’s name.

The source or the buildCodeThe compiler’s words
a source without a heading2005expected keyword to declare the output kind, found import, for a source that opens with imp
a source whose last end has no period2007expected period at end of the program, found end of file
a program and a library togetherMultiple program or library statements found in source code; only one is allowed
object units aloneThe sources hold no program or library: an object unit is built beside the program or library that imports it, named with it in one --source list; compilation aborted
two headings in one source2082a source file holds one unit, and this 'program' heading begins a second one: put each unit in a file of its own and name the files together in the --source list

The block

A block is its sections of declarations, then its routines, then the statement it runs (block). The sections are the type parameters behind gen, the imports behind imp, the constants behind const, the types behind type and the variables behind var (declaration-section). They stand in any order and as often as the unit likes; the order of the house style is imports, constants, types, variables.

A constant may read any constant its block or an enclosing block declares, above or below it, in its own section or in another: each constant is computed after the constants it reads, and a type’s bound may read any of them. A name means the nearest declaration of it, so a routine’s constant hides the program’s constant of the same name even where the routine writes it below the constant that reads it.

const
    Area = Width * Height;    { 20, read from the two constants below it }
    Width = 4;
    Height = Width + 1;

Constants that read each other have no first one to compute, so they are refused once, at the one written first, by the names of all of them:

WrittenCodeThe compiler’s words
constants that read each other, First = Second; Second = First;5658'First' and 'Second' read each other, so none of them can be computed before the others: give one of them a value, a bound or a type that reads none of the others
a constant that reads itself, Again = Again + 1;5665'Again' reads its own value, so it cannot be computed: give it a value that does not read 'Again'

A section after a routine of the same block is refused, because every declaration of a block stands above its routines:

Written after a routineCodeThe compiler’s words
const, type or var2044a constant declaration cannot follow a function or procedure; move all of the block's constant, type, and variable declarations above its functions and procedures, naming the kind it met
imp2069an import declaration cannot follow a function or procedure; move all of the block's import declarations above its functions and procedures

A routine has a block of its own, so routines nest, at most sixteen deep. A seventeenth is refused with parser error 2004: routines nest at most 16 deep, and this one stands at depth 17: declare it in a routine further out, and hand it what it reads as parameters.

The statement a block runs is any one statement (action), and in practice a compound statement, begin, statements separated by semicolons, and end. A section of declarations goes on as long as a line begins with a name, so a body that follows a section directly and begins with a name is read as one more declaration: write the body as begin ... end.

{
    Distances - a program whole: its heading, its sections, its routines and its body.

    The sections stand in the house order, imports, constants, types and variables, and the routines below them. A
    routine has a block of its own, so it may declare variables and routines of its own before its body. The body at
    the end is the one statement the program runs, and the program ends when that statement does.
}
program Distances;

imp
    WriteLn : std;
    Sqrt : math;

const
    PointCount = 3;
    DistanceLine = "point %lld lies %lf from the origin";

type
    { A point of the plane. }
    Point = record
        x : float64;
        y : float64;
    end;

    { The points the program measures, counted from one. }
    Points = array[1..PointCount] of Point;

var
    points : Points;
    index : int64;

{ The distance of a point from the origin, read from the squares of its two coordinates. }
function Distance(p : Point) : float64;
var
    squares : float64;

    { The square of one coordinate, a routine nested in the routine that needs it. }
    function Square(coordinate : float64) : float64;
    begin
        Square := coordinate * coordinate;
    end;

begin
    squares := Square(p.x) + Square(p.y);
    Distance := Sqrt(squares);
end;

begin
    points[1].x := 3.0;
    points[1].y := 4.0;
    points[2].x := 6.0;
    points[2].y := 8.0;
    points[3].x := 0.0;
    points[3].y := 2.5;

    for index := 1 to PointCount do
        WriteLn(DistanceLine, index, Distance(points[index]));
end.

It prints:

point 1 lies 5.000000 from the origin
point 2 lies 10.000000 from the origin
point 3 lies 2.500000 from the origin

Variables and their start values

Every variable starts at its type’s start value, so a program may read a variable before it assigns one, and no read ever meets bytes that an earlier call or an earlier owner of the memory left behind. A program’s variables take their start values before its body runs, a routine’s at every call, a task’s and a generator’s when each begins, and the variables a compound statement declares at every entry of the compound, so a loop body’s variables start afresh every round. A cell that new makes starts the same way, and so does every element that reserve or Resize adds to a dynamic array.

A variable ofStarts as
an integer type, int8 to uint64, or a wide integer0
float32, float64, float16 or bfloat160.0
boolFalse
unicodethe character whose code is 0
descriptorthe number 0
a subrange0 where its range holds 0, and otherwise the bound nearest 0: 1..12 starts at 1, -5..-1 at -1 and 'a'..'z' at 'a'
an enumerationits first member, and a subrange of one, Green..Blue, its low member
an error domainits first code
a set or a bitsetempty
a bitrecordevery field at its own start value
a record, a fixed array, a vector, a matrix or a tensorevery field and every element at its own start value
a sum typeits first case, with that case’s fields at their start values
string, stringbuffer or stringpartempty
a dynamic array, a dynamic vector, a dynamic matrix or a mapempty
a spanthe empty view: length 0, Low 0 and High -1
a pointernil
a fileclosed
a streamended, so pulling from it yields nothing
tracked Tthe start value of T

A variable declared with an initial value, limit : int64 := 5;, starts at its start value as well and takes the initial value where its declaration stands. A parameter holds what the caller handed over. A variable declared absolute names a device register: it holds whatever the device holds there, and the program never writes a start value to it.

The program below reads its variables before it assigns any of them, and its function counts with a counter it never sets to zero:

{
    Readings - every variable starts at its type's start value, so a program may read one before it assigns it.

    The body prints its variables before it assigns any of them: the month starts at 1, the low bound of its range,
    the colour at its first member, the reading's value at zero and its hour at 1, the name empty and the list without
    values. 'Tally' counts the values above a limit and never writes 'count := 0', because its counter starts at zero
    at every call.
}
program Readings;

imp
    WriteLn, Ord, Length, Append : std;

const
    StartLine = "month %lld, colour %lld, reading %lld at hour %lld, name of %lld runes, list of %lld values";
    TallyLine = "%lld of %lld values above %d";
    Limit = 20;

type
    Month = 1..12;
    Colour = (Red, Green, Blue);
    Hour = 1..24;

    { One measured value and the hour it was taken. }
    Reading = record
        level : int64;
        hour : Hour;
    end;

    Values = array of int64;

var
    month : Month;
    colour : Colour;
    reading : Reading;
    name : string;
    values : Values;

{ The number of values above the limit; the counter starts at zero at every call. }
function Tally(list : Values, limit : int64) : int64;
var
    count : int64;
    item : int64;
begin
    for item in list do
        if item > limit then
            count := count + 1;

    Tally := count;
end;

begin
    WriteLn(StartLine, month as int64, Ord(colour), reading.level, reading.hour as int64, Length(name), Length(values));

    Append(address values, 12);
    Append(address values, 25);
    Append(address values, 31);

    WriteLn(TallyLine, Tally(values, Limit), Length(values), Limit);
end.

It prints:

month 1, colour 0, reading 0 at hour 1, name of 0 runes, list of 0 values
2 of 3 values above 20

Writing a start value out costs nothing. An assignment that stores a local’s start value while the local still holds it, as count := 0; does as a routine’s first statement, emits no store in a debug build, where a breakpoint on its line still stops, and an optimized build reads it as the counter’s first value, which the counter’s register needs in any case. This holds for a local that the routine neither lends with address nor shares with a routine nested in it; any other store of a start value, a reset at the head of a loop body among them, runs as written.

A pointer starts at nil, so a pointer variable that no statement assigns can only ever be nil, and a use of one is refused:

WrittenCodeThe compiler’s words
a read of a pointer variable that no statement assigns, or a write through one5167identifier with pointer data type is used before its initialization: p
a function returning a pointer whose result no statement assigns5168function return variable with pointer data type is uninitialized: Find

The program body

A program’s body is the statement the program runs. It starts after the body of every library and every object unit the program uses has run, and the program ends when the body does, with exit code 0. A leave statement in the body ends the program there. A trap ends the program with exit code 1, as the traps reference describes.

Exports

The mark exp in front of a declaration at the root of a library or an object unit exports it: a constant, a type, a function, a procedure, a task or a generator (constant-entry, type-declaration, routine-declaration). What the marks export is the unit’s whole surface. A declaration without the mark is private to the unit, and a variable is never exported: an importer reaches a unit’s state through the routines it exports.

A program has no importers, and a declaration nested in a routine is invisible outside it, so the mark on either is refused with analyzer error 5397: the 'exp' mark exports a library's root declaration, so 'Twice' cannot carry it here: a program has no importers, and a nested declaration is invisible outside its enclosing body.

The mark is refused by name on a callback (5511). A sum type takes it as any other type does: the contract states each case with the fields it carries, and an importer names the cases it uses beside the type, as it names an enumeration’s literals. A function with a conformant array parameter has no shape in a library’s interop contract, so a build asked for the contract (--emit-contract) refuses it by name: published function "Total" in library "conf" carries a conformant array parameter, which has no interop contract shape. An exported generic routine is compiled into each unit that imports it, so its signature and its body may name only what the exporting unit exports (5612 and 5557).

Libraries

A library is built on its own, as an archive with --output-modifier archive or as a shared object with --output-modifier object, and --emit-contract writes its contract, mica.external, beside it: the surface its exp marks state, which a program built later imports through. A shared object names the shared runtime floor installed beside the archives instead of carrying a copy of it, so every Mica library one process loads, linked or loaded with dlopen, runs on one floor with one scheduler and one carrier pool. The floor stays loaded once loaded, and the Mica libraries of one process are built alike, by one release at one tier with one tasking flavor and memory class: a library that names another floor is refused by name as it loads. A library that exports a task, or that places work on processes, links as an archive, as the known limitations state.

A library is linked only under a version, read from the version of the mica.project that --project names; a library build without one is refused before it links:

A library as output binary states its version as "version" in the mica.project it is built from: linking aborted

The version travels into the contract, into the build of every program built against it (Using library 'gauge' 1.0.0) and into that program’s manifest. A program states which versions of a library it accepts under requires in its own project file, and a library outside them is refused before a source is read against its contract:

Failed to resolve imported libraries: interop error 562: the project requires the library 'gauge' at '2.0', which accepts the versions from 2.0.0 up to 3.0.0, and the library is at version '1.0.0'

A library’s body runs once when a program that uses it starts, before the program’s own body: it sets up the library’s state. A statement registered in it with defer until program runs once when the program has ended, after its body, and several such statements run in the reverse order of their registration. Such a statement stands at the top level of the library’s body, outside every choice and loop, so every run of the body reaches it; one inside an if, a case, a loop or a select is refused (5799), as the deferred statements section shows.

The example is two directories side by side, the library in gauge and a program built against it in meter:

gauge/gauge.mica

{
    gauge - a library: built on its own, its surface is what 'exp' marks, and its body runs before the program's.

    The constant 'FullScale', the type 'Reading' and the function 'Measure' are exported; 'Limited' is not, so no importer
    can reach it. The library counts its readings in a unit variable, which its body sets up when the program starts and
    its farewell reads when the program has ended.
}
library gauge;

imp
    WriteLn : std;

const
    exp FullScale = 100;

type
    { One reading of the gauge, with the scale it was taken on. }
    exp Reading = record
        level : int64;
        scale : int64;
    end;

var
    taken : int64;

{ A value held inside the gauge's scale, private to the library. }
function Limited(amount : int64) : int64;
begin
    Limited := amount;

    if amount > FullScale then
        Limited := FullScale;
end;

{ A reading of a value on the gauge's full scale, counted among the readings taken. }
exp function Measure(amount : int64) : Reading;
var
    reading : Reading;
begin
    taken := taken + 1;
    reading.level := Limited(amount);
    reading.scale := FullScale;
    Measure := reading;
end;

{ The farewell the library says once the program has ended. }
procedure Farewell();
begin
    WriteLn("gauge: %lld readings taken", taken);
end;

begin
    taken := 0;
    WriteLn("gauge: ready");
    defer until program Farewell();
end.

gauge/mica.project

{
    "version": "1.0.0",
    "sources": [
        "gauge.mica"
    ]
}

meter/Meter.mica

{
    Meter - a program built against the contract the gauge library's build emitted, which its project file names.
}
program Meter;

imp
    WriteLn : std;
    FullScale, Reading, Measure : gauge;

var
    reading : Reading;

begin
    WriteLn("meter: start");
    reading := Measure(42);
    WriteLn("meter: %lld of %lld", reading.level, reading.scale);
    reading := Measure(250);
    WriteLn("meter: %lld of %d", reading.level, FullScale);
end.

meter/mica.project

{
    "version": "1.0.0",
    "sources": [
        "Meter.mica"
    ],
    "contracts": [
        "../gauge/build"
    ],
    "requires": {
        "gauge": "1.0"
    }
}

Built from the directory that holds the two, the library first:

mica --compile --link --output-modifier archive --emit-contract --project gauge --build gauge/build
mica --compile --link --project meter --build meter/build
meter/build/Meter

Among the lines the two builds print, the first names the contract it emitted and the second the library it was built against, each with its version:

Emitted interop contract 'gauge/build/mica.external' for library 'gauge' 1.0.0
Using library 'gauge' 1.0.0

The program prints, the library’s body first and its farewell last:

gauge: ready
meter: start
meter: 42 of 100
meter: 100 of 100
gauge: 2 readings taken

Limited stands in the library’s source without the mark, so a program that imports it is refused: no declarations found in namespace 'gauge' for identifier: Limited — a library exports only its 'exp'-marked declarations, so a name that exists in its source may still be private (5021).

Object units

An object unit is compiled in the same build as the program or the library that uses it, and imported by the unit’s name. Its variables live in the unit’s own storage, and its body runs once when the program starts, before the program’s body. Its surface is what its exp marks state, as a library’s is.

counter.mica

{
    counter - an object unit: compiled in the same build as the program that imports it, by the unit's name.

    The unit's variable lives in the unit's own storage. Its body runs once when the program starts, before the
    program's own body, so it is where the unit sets its state up. The two routines marked 'exp' are the unit's whole
    surface.
}
object counter;

imp
    WriteLn : std;

const
    StartCount = 10;

var
    count : int64;

{ The count goes up by one. }
exp procedure Bump();
begin
    count := count + 1;
end;

{ The count as it stands. }
exp function Current() : int64;
begin
    Current := count;
end;

begin
    count := StartCount;
    WriteLn("counter starts at %lld", count);
end.

Counting.mica

{
    Counting - a program built together with the object unit 'counter', whose two routines it imports by the unit's name.
}
program Counting;

imp
    WriteLn : std;
    Bump, Current : counter;

begin
    WriteLn("the program starts");
    Bump();
    Bump();
    WriteLn("the count is %lld", Current());
end.

Both sources are compiled in one build, --source counter.mica,Counting.mica, and the program prints:

counter starts at 10
the program starts
the count is 12

Imports

An import section names the names a unit takes from other units (import-section). Nothing reaches a unit unimported: WriteLn, Length and Ord arrive by imp as Sqrt does. Each group of the section ends with the name of the unit it imports from, in one of four forms (import-group, import-member):

WrittenImports
WriteLn, Length : std;the listed names, each under its own name
Root = Sqrt : math;Sqrt under the name Root, which the unit uses in its place
* : strings;every name the unit exports; * stands alone in its group
math;no name: the namespace becomes addressable, so math.Pi names its member

An import section may stand in a routine’s block as well as at the root, and its names are then visible in that routine and the routines nested in it, as the block’s own declarations are. A generic routine is the exception: imported inside a routine, it is not found there, so Sqrt imported by Sqrt : math in a routine’s section is answered at its call with identifier not found: Sqrt (5018), and Sort of arrays likewise. Import a generic routine at the root.

{
    Imports - the four forms an import group takes, and the qualified name a whole namespace gives.

    'WriteLn, Length : std' imports two names by name. 'Root = Sqrt : math' imports a name under one of the program's
    own choosing. '* : strings' imports every name the strings unit exports. 'math' alone imports no name at all: it
    makes the namespace addressable, so 'math.Pi' reaches a member without importing it.
}
program Imports;

imp
    WriteLn, Length : std;
    Root = Sqrt : math;
    MaxInt32 : limits;
    * : strings;
    math;

const
    Word = "import";

begin
    WriteLn("%lf", Root(2.0));
    WriteLn("%lf", math.Pi);
    WriteLn("%d", MaxInt32);
    WriteLn("%ls %lld", UpperCase(Word), Length(Word));
end.

It prints:

1.414214
3.141593
2147483647
IMPORT 6

Where imported names come from

A unit imports from six kinds of source:

SourceNamespacesWhere its surface is read
the language’s own unitsstd, limits, bitsinside the compiler
the standard libraryarrays, files, flags, integers, linalg, maps, math, net, paths, process, random, regex, streams, strings, time, utfinside the compiler
the C library, POSIX and Linuxcstd, posix, linuxinside the compiler
the Dragon backenddragoninside the compiler
the object units of the buildeach object’s namethe build’s sources
libraries built on their owneach library’s namethe library’s contract, mica.external

The code behind a surface joins the program’s link by itself: the standard library’s archive, which the compiler finds beside itself and then under /usr/lib/; the system’s own C library; and the Dragon backend’s archive, found beside the compiler, beside the standard library and under /usr/lib/, in that order.

A library’s contract is found in one of three ways: a mica.external beside the sources, one file for all of them; the paths of --external-contract, which replace it; or the contracts of the project file, each a contract or the directory holding one. A path a build names that holds no contract is refused rather than skipped.

The surfaces of the C library, POSIX, Linux and the Dragon backend travel inside the compiler, and a program imports from them by name as from any unit:

{
    Surfaces - names imported from the C library, from POSIX and from Linux, as names from any unit are.

    The three surfaces travel inside the compiler, each a namespace of its own: 'cstd' holds the C library's functions
    under Mica names, 'posix' the POSIX calls and constants, and 'linux' the kernel's own. An import names what it uses
    and nothing else arrives.
}
program Surfaces;

imp
    WriteLn : std;
    Atoi, IsDigit : cstd;
    Htons : posix;
    EPOLLIN : linux;

const
    Digits = "1234";
    Seven = 55;

begin
    WriteLn("Atoi(%ls) is %d", Digits, Atoi(Digits));

    if IsDigit(Seven) # 0 then
        WriteLn("IsDigit(%d) holds", Seven);

    WriteLn("Htons(258) is %u", Htons(258));
    WriteLn("EPOLLIN is %u", EPOLLIN);
end.

It prints:

Atoi(1234) is 1234
IsDigit(55) holds
Htons(258) is 513
EPOLLIN is 1

The dragon namespace drives the backend that compiles Mica itself, and its archive joins the link of a program that imports from it:

{
    Engine - a program that opens a session with the Dragon backend, the engine that compiles Mica itself.

    The 'dragon' namespace travels inside the compiler, and its archive links into the program by itself: the program
    imports the four entries it calls, opens a context, makes an empty unit of intermediate code in it, has the backend
    check the unit, and frees the context, which frees everything made in it.
}
program Engine;

imp
    WriteLn : std;
    ContextNew, UnitNew, Validate, ContextFree : dragon;

var
    context : uint64;
    unit : uint64;
    findings : int32;
    status : int32;

begin
    status := ContextNew(address context);
    WriteLn("context opened: %d", status);
    status := UnitNew(context, address unit);
    WriteLn("unit made: %d", status);
    status := Validate(context, unit, address findings);
    WriteLn("unit checked: %d, findings: %d", status, findings);
    ContextFree(context);
end.

It prints, the backend’s one finding being what an empty unit lacks, an entry point:

context opened: 0
unit made: 0
unit checked: 0, findings: 1

How an imported name is found

An imported name joins the scope of the block whose section imports it, beside that block’s own declarations, and the block uses it as it uses its own. Three rules keep that one lookup unambiguous:

  • A unit’s declaration and an imported name may not share a name: the declaration is refused as already declared, function identifier already declared: Sqrt (5019).
  • One name is imported once. A second import of it, from the same unit or from another, is refused with 5415, 'Copy' is already imported from library 'strings'; give one of the two imports an alias.
  • An import reaches only what the unit it names exports. A name that unit imports from a third is not passed on, so each unit imports what it uses itself.

A qualified name, math.Pi, reaches the member of a namespace the unit imported whole: a namespace not imported is refused with 5436, the qualified name 'math.Sqrt' needs its namespace imported first: 'imp math;' makes the whole namespace addressable, and importing the member by name is the unqualified road, a name the namespace does not export with 5437, and a generic routine, which is imported by name, with 5438.

The importCodeThe compiler’s words
a namespace no source holds5020namespace not found: stdio
a name the unit does not export5021no declarations found in namespace 'std' for identifier: Shout — a library exports only its 'exp'-marked declarations, so a name that exists in its source may still be private
* from a unit that exports nothing5022no declarations found in namespace 'helpers' for wildcard identifier
the unit’s own name5023cannot self-import from the current namespace: Probe
an alias for *2011aliases are not allowed for a wildcard identifier '*'
an alias that is a name the language declares5088imported identifier cannot use built-in reserved name: True
the language’s mathematics from std5596'Sqrt' is not exported by 'std', but 'math' exports it: write 'imp Sqrt : math;' — the language's mathematics lives in one namespace, and 'std' keeps the language core

The project file

A mica.project beside the sources states the facts of a build that belong to the source tree: its sources, the encoding they are written against, the contracts of the libraries they import, the version of the program or the library they are, and the versions of those libraries it accepts under requires. --project names the file or the directory that holds it, and every flag of the command line overrides the entry it names. A program compiled from its sources with flags alone needs no project file; a library needs one for its version. The file’s entries and their refusals are taught whole in the project file.

Where chapter 3 begins

The constants, types, variables and routines a unit declares in its sections, what each of them may refer to and the scopes their names are looked up in are chapter 3, and what each type carries is the type system.