Hilde lernen

Diese Tour zeigt vollständige Programme aus dem Verzeichnis examples/ des Hilde-Repositorys. Zu jedem steht die Ausgabe, die es beim Bauen dieser Seite tatsächlich erzeugt hat. Die Regeln dahinter erklären die Grundlagen, jeden Fehlercode die Seite Fehlercodes.

Hallo, Welt

Datei: HelloWorld.hih

module Example.HelloWorld is
    """
    No contract: the exit code of the programme, the result of what it did.
    """
    function main(): ExitCode;
end module.

Datei: HelloWorld.hiq

module body Example.HelloWorld is
    function main(): ExitCode is
        printLn("Hello, world!");
        return ExitSuccess();
    end;
end module body.

Ausgabe von ./main (Exit-Status 0):

Hello, world!
Quelle: Beispiel examples/hello-world im Hilde-Repository, hilde 1.0.0

Records mit Invariante

Interval ist ein Record mit einer Invariante. Sie wird jedes Mal geprüft, wenn ein Wert sein Modul verlässt. makeInterval liefert einen Option-Wert: Ein Intervall, dessen unteres Ende über dem oberen liegt, wird mit None abgelehnt, statt das Programm abstürzen zu lassen.

Datei: Interval.hih

"""
A closed interval of whole numbers whose low end is never above its high
end.  The type is opaque, so only this module builds and changes one, and
its invariant is checked whenever a value leaves the module.
"""
module Example.Interval is
    """
    An interval from low to high, both included.
    """
    type Interval: Linear;

    """
    The interval from low to high, or None when low is above high.
    No contract: an Option result, which an ensures cannot look into.
    """
    function makeInterval(low: Nat32, high: Nat32): Option[Interval];

    """
    How many numbers the interval holds: high minus low plus one.
    No contract: a number read from its argument; its type gives its range, and nothing cheaper holds.
    """
    generic [R: Region]
    function width(interval: &[Interval, R]): Nat32;

    """
    Moves the high end up by amount.  Returns false and leaves the interval
    as it was when the high end would pass the largest Nat32.
    No contract: the answer is a Bool, nothing cheaper holds.
    """
    generic [R: Region]
    function stretch(interval: &![Interval, R], amount: Nat32): Bool;

    """
    The high end of the interval.
    No contract: a number read from its argument; its type gives its range, and nothing cheaper holds.
    """
    generic [R: Region]
    function highEnd(interval: &[Interval, R]): Nat32;

    """
    Ends the interval.
    No contract: it only frees or closes what it is given.
    """
    function destroyInterval(interval: Interval): Unit;
end module.

Datei: LearnRecords.hiq

import Example.Interval (
    Interval,
    makeInterval,
    width,
    stretch,
    highEnd,
    destroyInterval
);

module body Example.LearnRecords is
    record Day: Linear is
        morning: Interval;
        evening: Interval;
    end;

    -- A write reference to the evening alone, from a write reference to
    -- the whole day.  It uses up the reference to the day.
    generic [R: Region]
    function stretchEvening(day: &![Day, R], amount: Nat32): Bool is
        return stretch(&!(day->evening), amount);
    end;

    generic [R: Region]
    function hours(day: &[Day, R]): Nat32 is
        return width(&(day->morning)) + width(&(day->evening));
    end;

    function makeDay(morning: Interval, evening: Interval): Day is
        return Day(morning => morning, evening => evening);
    end;

    function main(): ExitCode is
        case makeInterval(9, 5) of
            when Some(value as wrong: Interval) do
                printLn("an interval from 9 to 5, which must not be");
                destroyInterval(wrong);
            when None do
                printLn("an interval from 9 to 5: refused, the low end is above the high end");
        end case;
        case makeInterval(8, 11) of
            when Some(value as morning: Interval) do
                case makeInterval(17, 19) of
                    when Some(value as evening: Interval) do
                        var day: Day := makeDay(morning, evening);
                        print("hours in the day: ");
                        printLn(hours(&day));
                        print("the evening stretched by two: ");
                        printLn(stretchEvening(&!day, 2));
                        print("stretched past the largest number: ");
                        printLn(stretchEvening(&!day, maximum_nat32));
                        borrow view: &[Day, V] := &day do
                            print("the evening ends at: ");
                            printLn(highEnd(&(view->evening)));
                            print("hours now: ");
                            printLn(hours(view));
                        end borrow;
                        let { morning as m: Interval, evening as e: Interval } := day;
                        destroyInterval(m);
                        destroyInterval(e);
                    when None do
                        printLn("no evening");
                        destroyInterval(morning);
                end case;
            when None do
                printLn("no morning");
        end case;
        return ExitSuccess();
    end;
end module body.

Ausgabe von ./main (Exit-Status 0):

an interval from 9 to 5: refused, the low end is above the high end
hours in the day: 7
the evening stretched by two: true
stretched past the largest number: false
the evening ends at: 21
hours now: 9
Quelle: Beispiel examples/learn-records im Hilde-Repository, hilde 1.0.0

Vorlagen

Neben dem Modul liegt eine Vorlage in Jinja2-Syntax. Der Compiler prüft sie gegen den Record, den sie bekommt, und übersetzt sie in Hilde-Code. Zur Laufzeit wird nichts mehr geparst.

Datei: order.jinja

Order for {{ customer|upper }}
{%- for line in lines %}
{{ loop.index }}. {{ line.name }}, {{ line.count }} of them{% if loop.last %} (the last line){% endif %}
{%- endfor %}
{% if rush %}Ship today.{% else %}Ship this week.{% endif %}

Datei: LearnTemplate.hiq

import Standard.Buffer (
    Buffer,
    allocateEmpty,
    insertBack,
    removeLast,
    destroyEmpty,
    length
);
import Standard.String (
    String,
    fromLiteral,
    destroyString,
    length as textLength,
    getSpan
);
import Standard.StringBuilder (
    StringBuilder,
    makeStringBuilder,
    finalize
);
import Standard.Template (
    Render
);

module body Example.LearnTemplate is
    record Line: Linear is
        name: String;
        count: Int32;
    end;

    record Order: Linear is
        customer: String;
        lines: Buffer[Line];
        rush: Bool;
    end;

    -- The template order.jinja, in the syntax of Jinja2, is read by the
    -- compiler and turned into code here: a name in the template that the
    -- record does not have, or a filter that does not exist, is a compile
    -- error, not a surprise at run time.
    generic [R: Region]
    function render(order: &[Order, R]): String is
        var out: StringBuilder := makeStringBuilder();
        @template("order.jinja", order, out);
        return finalize(out);
    end;

    function destroyLines(lines: Buffer[Line]): Unit is
        var rest: Buffer[Line] := lines;
        while length(&rest) > 0 do
            let line: Line := removeLast(&!rest);
            let { name: String, count: Int32 } := line;
            destroyString(name);
        end while;
        destroyEmpty(rest);
        return nil;
    end;

    function main(): ExitCode is
        var lines: Buffer[Line] := allocateEmpty();
        insertBack(&!lines, Line(name => fromLiteral("apples"), count => 6));
        insertBack(&!lines, Line(name => fromLiteral("pears"), count => 2));
        insertBack(&!lines, Line(name => fromLiteral("plums"), count => 12));
        let order: Order := Order(
            customer => fromLiteral("Ann"),
            lines => lines,
            rush => true
        );
        let text: String := render(&order);
        printLn(getSpan(&text, 0, textLength(&text) - 1));
        destroyString(text);
        let { customer: String, lines as rest: Buffer[Line], rush: Bool } := order;
        destroyString(customer);
        destroyLines(rest);
        return ExitSuccess();
    end;
end module body.

Ausgabe von ./main (Exit-Status 0):

Order for ANN
1. apples, 6 of them
2. pears, 2 of them
3. plums, 12 of them (the last line)
Ship today.
Quelle: Beispiel examples/learn-template im Hilde-Repository, hilde 1.0.0

Ein Graph

Standard.Graph ist ein gerichteter Graph mit Werten an Knoten und Kanten. Das Beispiel sucht in einer kleinen Karte den Weg mit den wenigsten Straßen und den mit den wenigsten Kilometern. Den Quelltext finden Sie unter examples/learn-graph.

Ausgabe von ./main (Exit-Status 0):

fewest roads: Aachen Cologne Frankfurt
fewest kilometres: Aachen Bonn Frankfurt, 265 km
Quelle: Beispiel examples/learn-graph im Hilde-Repository, hilde 1.0.0

Tasks

Vier Tasks laufen in einem gemeinsamen Scope, ein fünfter hat seine eigene Arena. Die Ausgabe ist bei jedem Lauf dieselbe. Den Quelltext finden Sie unter examples/learn-tasks.

Ausgabe von ./main (Exit-Status 0):

5239625
36552125
99114625
192927125
333833500
338350
Quelle: Beispiel examples/learn-tasks im Hilde-Repository, hilde 1.0.0

Weitere Beispiele

Das Repository enthält sechzig Beispielverzeichnisse. Die Verzeichnisse learn-* zeigen jeweils eine Eigenschaft oder ein Modul, etwa reguläre Ausdrücke, die Sandbox, SQLite und SQLCipher, Passwörter, Sitzungen, einen FastCGI-Server, Dateien, Zeit und Archive. In jedem liegt eine README.md, die erklärt, wie man das Beispiel baut und was herauskommt. Die Anleitung Hilde lernen beginnt beim ersten Programm, und die Seite Fallstricke zeigt typische Fehler beim Schreiben von Hilde mit falschem und richtigem Code.

Playground

Geplant ist ein Playground im Browser auf Grundlage des WebAssembly-Ziels von Hilde. Ein Modul für wasm32-wasi wird mit zig cc gebaut, und das Repository enthält je ein Beispiel für den Browser und für die Kommandozeile (examples/wasm-browser und examples/wasm-cli). Auf dieser Website gibt es noch keinen Playground. Die Website kommt ohne JavaScript aus.