💾 Archived View for schinkel.bevuta.com › fleng › LIBRARY captured on 2023-11-04 at 11:46:58.

View Raw

More Information

➡️ Next capture (2024-02-05)

-=-=-=-=-=-=-


FLENG LIBRARY REFERENCE
=======================


### Table of contents:

 1. Syntactic Forms
 2. Toplevel declarations
 3. Guard expressions
 4. Builtin primitives
 5. "$rt" module: low-level primitives
 6. "lib" module: FGHC standard library
 7. "sys" module: Event-loop for thread-communication
 8. "fmt" module: Formatted output
 9. "sort" module: Sorting
 10. "map" module: Key-value maps
 11. "list" module: List operations
 12. "set" module: Lists as sets
 13. "proc" module: Subprocess invocation
 14. "lex" module: FGHC/FLENG lexical analyzer
 15. "parse" module: FGHC/FLENG parser
 16. "array" module: Mutable numeric arrays
 17. "app" module: Higher-order operations on lists
 18. "io" module: Extended I/O stream operations
 19. "scan" module: Simple parsers for various character sequences
 20. "match" module: Pattern matching
 21. "spec" module: Parse lists according to a specification
 22. "path" module: Pathname destructuring
 23. "find" module: Find files and directories
 24. "ucs" module: unicode character classification and conversion
 25. PCN syntax and primitive operations
 26. Security-relevant stuff
 27. "color" module: Access to named colors
 28. "sdl" module: basic SDL2 graphics and sound interface
 29. "ezd" module: Structured graphics
 30. "ezbb" module: EZD building blocks
 31. Formal definition of PCN syntax


## 1. Syntactic Forms

        Block comment, may not be nested.

        [FGHC] Single-line comment.

    GOAL1, GOAL2
        [FGHC] Execute goals in parallel. The goals may not be variables.

    MODULE:GOAL
        [FGHC] Invokes GOAL, which must be a tuple or string and be defined in
        and exported from the module with the given name.

    GOAL@PEER
    MODULE:GOAL@PEER
        [FGHC] Invokes the specified non-variable GOAL in the thread designated
        by PEER, which may be a variable but must be bound.  All threads
        are ordered before execution in ring and torus topologies. PEER
        specifies what neighbouring thread in a topology should receive
        the call:

            fwd  bwd        
                previous or next in the ring topology.

            north  south  east  west
                neighbours in the torus topology.

            all
                broadcast call to all threads.

        All threads are connected in the ring topology. Some threads
        may not be connected in the torus topology, depending on the
        number of threads.  Additionally PEER may be an integer thread
        number (starting at 1) addressing a specific thread in the set
        of all threads.

    GUARD -> GOAL1
    GUARD -> GOAL1; GOAL2
        [FGHC] Executes GOAL1 if the guard expression GUARD evaluates to true
        or executes GOAL2, otherwise. If GOAL2 is not given, it defaults
        to "true".

    GOAL1 & GOAL2
        [FGHC] Execute GOAL1 in a new task (see also "call/3") and then, once 
        GOAL1 and all child processes created by GOAL1 have successfully
        executed, execute GOAL2. Note that "&/2" binds stronger than ",/2".
        Operationally, "X & Y" is equivalent to "(call(X, S), when(S, Y))".

    X = Y
        [FGHC] Unifies X with Y by recursively comparing the values in
        X and Y.  Unbound variables will be bound. In FLENG a failed
        unification will silently be ignored. Note tha variables in X
        or Y may be partially bound when the unification fails. In FGHC
        a failed unification will signal an error.

    X := Y
        [FGHC] Equivalent to "assign(Y, X, _)".

    S <= M
        [FGHC] Equivalent to "S0 = [M|S1]" where S is a paired argument.

    M => S
        [FGHC] Equivalent to "[M|S0] = S1" where S is a paired argument.

    S <== X
        [FGHC] Equivalent to "S1 = X" where S is a paired argument. The previous
        value is lost and the next occurrence of S will mean X instead.

    S += E
    S -= E
    S *= E
    S /= E
        [FGHC] Equivalent to "S1 is S0 + E" ("-" ...), where S is a paired 
        argument.

    RESULT^ is EXPRESSION?
        [FGHC] Evaluates the numerical expression and unifies RESULT with
        the result of the computation. EXPRESSION must be an arithmetic
        expression and may not contain unbound variables. Available
        operations are:

            X + Y
            X - Y
            X * Y
            X / Y           Usual arithmetic operators
            X \\ Y          Integer remainder
            +X              Identity
            -X              Negation
            \X              Bitwise NOT
            sqrt(X)
            integer(X)      Truncate and convert to integer
            real(X)         Convert integer to float
            X /\ Y          Bitwise AND
            X \/ Y          Bitwise OR
            X >< Y          Bitwise XOR
            X << Y          
            X >> Y          Shift X by Y
            X ** Y          Exponentiation of X by Y
            rnd             Pseudo random float betwwen 0 and 1
            rnd(X)          Random integer between 0 and X-1
            floor(X)      
            ceiling(X)
            round(X)        Rounding operations
            sin(X)
            cos(X)
            tan(X)
            atan(X)         Trigonometry
            log(X)          Natural logarithm
            exp(X)          Exponential value
            abs(X)          Absolute value
            sign(X)         Sign
            real_integer_part(X)   
            real_fractional_part(X)
                            Deconstruct floating-point value
            max(X, Y)
            min(X, Y)       Return greater or lesser argument

        Random numbers returned by "rnd/0" are uniformly distributed,
        "rnd/1" uses a slightly faster method and may not be uniformly
        distributed.
        Note that variables holding valid arithmetic expressions (as
        in ISO Prolog) are not supported.  Signals an error if any
        argument is not numeric.

    when(VAR?, GOAL)
        [FGHC] Executes GOAL, which must not be an unbounded variable, once
        VAR is bound.

    foreign_call(NAME(ARGUMENT, ...))
        Invokes an externally linked foreign function with the following
        signature:

            void <NAME>_<ARITY>(FL_TCB *tcb, FL_VAL argument1, ...)

        where ARITY is the number of arguments given. Arguments are 
        dereferenced and passed on to the foreign function, using the C 
        ABIs calling conventions. At most 4 arguments can be passed.
        The call does not block, you must ensure arguments are bound to
        something meaningful or are intended to be unbound.
        The "tcb" argument holds a pointer to a "thread control block" 
        designating the thread context in which this foreign function is 
        invoked.
        Execution of concurrent processes in the same thread will not
        commence, until the foreign function returns. See "fleng.h" and
        "fleng-utils.h" for structure definitions, macros and functions to 
        access and manipulate the TCB and FLENG runtime values.

## 2. Toplevel declarations

    -arguments(STRING)                                       
        Adds additional run-time arguments to be used when running the
        compiled executable. This declaration is ignored in modules
        other than the main ('') module. The arguments can ge given as
        a string or character list and are effectively prepended to any
        arguments that are given when the executable is invoked. This
        declaration can be given multiple times, the argument strings
        will be appended in reverse order.

    -fleng_library([MODULENAME, ...])
        [PCN] Declare the given modules as FGHC/Strand/FLENG code, so
        mutable variables are passed by value. As only PCN handles
        mutable (boxed) values transparently, this declaration is needed
        to seamlessly interoperate with libraries written in FGHC,
        Strand or FLENG and applies automatically to the library modules
        provided by the FLENG base system.  Meta-calls that refer to
        dynamically constructed modules will not be handled in this
        way, though. In that case, you must take care not to pass mutable
        variables directly in calls to separately compiled non-PCN
        modules, for example by passing their value via temporary
        definitional variables.

    -foreign(SPECLIST)                                       
        Creates stubs and wrappers for external native code. SPECLIST should
        be a list of foreign-call specifications or strings and generates
        FLENG stubs that call generated C wrapper code which is produced in
        a file named "<module>.c", where "<module>" is the name of the currently
        compiled module. If no module is declared, the file will be named
        "foreign.c". The specifications can be of the following form:

            STRING
            NAME(TYPE, ...)
            struct(NAME(SLOT1:TYPE1, ...))
            const(NAME = STRING:TYPE)

        See the MANUAL file for a full description of the foreign 
        specification format.

        [PCN] Only the first variant for including verbatim C code is
        currently provided in PCN.

    -exports(EXPORTLIST)                                     
        [FGHC] EXPORTLIST should be a list of "<Name>/<Arity>" specifications of
        predicates that should be visible to external modules. If this
        declaration is not given, then by default all predicates are exported.

        Note that there is no performance or space advantage in not exporting 
        predicates. This declaration exists mostly for compatibility reasons
        with Strand.

    -include(FILENAMES)                                      
        [FGHC] Includes the contents of the files given by the string
        or list of strings in FILENAME at the current toplevel position
        in the compiled file. The file-extension defaults to ".ghc",
        ".fghc", ".strand" or ".st" when compiling GHC or Strand and
        to ".fl" or ".fleng" otherwise.

    -initialization(PROCESSLIST)                             
        Declares that on startup, the processes given will be spawned.
        PROCESSLIST should be a string or a list of strings naming predicates
        with zero arity, which must be defined, but not necessarily exported.
        Only a single initialization declaration will be respected (later 
        override earlier ones).

    -machine(TOPOLOGY)                                       
        Provided for Strand compatibility. Only "ring" and "torus"
        topologies are allowed. This declaration has no effect.

    -mode(CLAUSE)                                            
        [FGHC] Declares the predicate defined by CLAUSE to have a mode. CLAUSE 
        should specify a predicate term "<name>(<mode>, ...)", where "<mode>" is
        either the symbol "?" or "^". The compiler will transform arguments
        to the predicate marked as "^" into an explicit output-argument
        unification, e.g.

            -mode(wheels(?, ^)).
            wheels(car, 4).

        is equivalent to

            wheels(car, X) :- X = 4.

        Output arguments (marked as "^") may not be used in guards.

    -module(NAME)                                                    
        Declares the name of the current module, unless the file was 
        compiled with the "-m" command line option. If not given and
        no module name was specified during the invocation of the
        compiler, the module name defaults to the empty ("") module,
        which is the program's main module.

    -struct(TUPLE)                                           
        [FGHC] Generates code to easily extract and modify tuple components. 
        TUPLE should be a named tuple of length > 1. A struct declaration of the
        form

            -struct(point(x, y)).

        produces code similar to (but slightly more efficient than):

            point_x(point(X, _), Y) :- Y = X.
            point_y(point(_, X), Y) :- Y = X.
            point_x(X, point(_, B), Y) :- Y = point(X, B).
            point_y(X, point(A, _), Y) :- Y = point(A, X).

        Note that the update predicates take the new value as the first 
        argument.

        [PCN] The functionality is similar, but the accessor operation
        is a function, so the generated code is something like this:

            function point_x(p) {; return p[ 1 ] }
            function point_y(p) {; return p[ 2 ] }
            point_x(x, p, p2) {? p ?= {_, _, y}, p2 = {"point", x, y} } 
            point_y(y, p, p2) {? p ?= {_, x, _}, p2 = {"point", x, y} } 

    -synthesized(NAME)
    -synthesized(NAME1/ARITY1, NAME2/ARITY2)
        [FLENG] Declares the the predicate NAME1/ARITY1 should be considered 
        part of the implementation of NAME2/ARITY2 in profiling information. Also,
        NAME1/ARITY1 will not generate any entry-point information. This is
        mainly intended to hide internally generated clauses in compiled FGHC,
        Strand or PCN code. The simple form, where only a name is given 
        declares that all predicates with that name (regardless of arity)
        should be considered hidden.

    -uses(MODULELIST)                                        
        Declares that the modules should be loaded and process
        definitions be made available for use using the "MODULE:GOAL"
        notation. MODULELIST may be a string or a list of strings.
        In FGHC, declarations for modules called directly using the 
        "MODULE:GOAL" notation are added automatically and so not
        required.

## 3. Guard expressions

    X == Y                                                        
        [FGHC] Succeeds if X matches Y. Note that matching never binds logic
        variables. If an argument variable in a clause head occurs
        several times, then subsequent occurrences imply an argument
        match equivalent to using "==/2", e.g.

        foo(X, [X]) :- ...

        is the same as

        foo(X, [Y]) :- X == Y | ...

    X =\= Y                                                       
        [FGHC] Succeeds if X does not match Y.

    X =:= Y                                                       
        [FGHC] Succeeds if X is numerically the same as Y, X and Y may be 
        numerical expressions as in "is/2".

    X \=:= Y                                                      
        [FGHC] Succeeds if X is numerically different to Y. X and Y may be 
        numerical expressions.

    X > Y                                                         
    X < Y                                                         
    X >= Y                                                        
    X =< Y                                                        
        [FGHC] Succeeds if X is numerically greater or less than Y. X and Y 
        may be numerical expressions.

    X @> Y                                                        
    X @< Y                                                        
    X @>= Y                                                       
    X @=< Y                                                       
        [FGHC] Succeeds if X is ordered accordingly relative to Y. Implements a 
        general ordering relation where

            integers < reals < strings < lists < tuples < ports < modules

        Integers and reals are compared numerically, lists element by
        element, strings lexicographically, tuples by size and, if
        equal, element by element. All other objects are sorted by some
        arbitrary but stable order. 

    array(X)
        Succeeds if X is an array.

    atomic(X)                                                     
        [FGHC] Succeeds if X is a number or a string.

    data(X)                                                       
        [FGHC] Suspends if X is an unbound variable. This is identical to
        qualifying a head variable with "!".

    idle                                                          
        [FGHC] Suspends the clause until the current thread is "idle", that is,
        when no active processes are scheduled for execution. Once idle,
        the process will resume and subsequent matching of an "idle"
        guard will wait for the next idle cycle. Note that threads listening
        for input or other events like signals are not considered idle.

    integer(X)                                                    
        [FGHC] Succeeds if X is an integer.

    known(X)                                                      
        Succeeds if X is a non-variable object or a bound variable, the 
        guard does not suspend in the case that X is bound.

    list(X)                                                       
        Succeeds if X is a non-empty list.

    module(X)                                                     
        Succeeds if X is a module object.

    number(X)                                                     
        Succeeds if X is an integer or a floating point number.

    otherwise                                                     
        [FGHC] Always succeeds. A clause with this guard will only be matched
        if all textually previous clauses of the same process definition
        failed to match.

    port(X)                                                       
        Succeeds if X is a port object,

    real(X)                                                       
        Succeeds if X is a floating point number.

    remote(X)
        Succeeds if X is a variable or port object located in a different
        thread than the current one.

    string(X)                                                     
        Succeeds if X is a string. Note that this includes the empty
        list ("[]").

    tuple(X)                                                      
        Succeeds if X is a tuple.

    unknown(X)                                                    
        Succeeds if X is an unbound variable, the guard does not
        suspend in that case.

## 4. Builtin primitives

    all_modules(LIST^)
        Unifies LIST with a list of all linked modules.

    assign(VAR^, VAL?)
    assign(VAR^, VAL?, DONE^)
        Assigns VAL to the variable VAR, which must be unbound and
        unifies DONE with the empty list, when the assignment is
        completed. VAR must be currently unbound or contain a value
        identical to VAL or an error is signalled.

    cancel_timer(ID?)
    cancel_timer(ID?, RESULT^)
        Cancels and removes the timer identifier by ID, previously
        created by calling "timer/3" or "clock/3" and unifies RESULT with the 
        "true" when the operation is complete. The variable associated
        with the timer is assigned the string "false" or an empty list,
        depending on whether the timer is single-shot or periodic.
        If no timer with the given ID is currently active, then this 
        operation does nothing and unifies RESULT with "false" (if given).

    clock(MS?, VAR^, ID^)
        Establishes a periodic timer and assigns an integer identifier
        to ID that can be used to cancel the timer. Every MS milliseconds
        an integer or float indicating the number of expirations since the 
        last will be assigned to the stream in VAR.

    close_file(FILE?)
    close_file(FILE?, DONE^)
        Closes the open file with the descriptor FILE and unifies DONE
        with the empty list when the operation is completed.

    command_line(LIST^)
        Unifies LIST with a list of strings holding the command line
        arguments to the currently executing program, not including the
        program name and any run-time options.

    current_seconds(SECONDS^)
        Unifies SECONDS with the current UNIX timestamp in seconds 
        since 00:00, Jan. 1, 1970.

    dbgwrite(OBJECT?)
    dbgwrite(OBJECT?, DONE^)
        Writes OBJECT to stderr, including uninstantiated variables and with
        maximum output limited. If DONE is given, it is assigned the empty list
        after the operation completes.

    environment(ENV^)
        Unifies ENV with the environment slot of the current task, or the
        empty list, if the current process does not belong to an explicitly
        created task.

    fdup(FILE1?, FILE2?)
    fdup(FILE1?, FILE2?, DONE^)
        Duplicates file descriptor FILE1 to FILE2. In the error case 
        "fdup/2" will signal an error and abort. After the operation,
        DONE will be unified with the empty list.

    get_arg(INDEX?, TUPLE?, ITEM^)
    get_arg(INDEX?, TUPLE?, ITEM^, DONE^)
        Unifies ITEM with the INDEXth element of TUPLE, assigning the
        empty list to DONE, if given. Indexing starts at 1. Signals an
        error if INDEX is out of range.

    get_global(NAME?, VAL^)
    get_global(NAME?, VAL^, DEFAULT^)
        Retrieves the global variable named by the string NAME and unifies 
        its value with VAL. If no global variable under this name is defined, 
        an error is signaled if DEFAULT is not given. If DEFAULT is given and 
        the global variable has not yet been set, VAL is unified with DEFAULT. 
        Global variables are thread-local.

    get_module(NAME?, MODULE^)
        Assigns the module  with the given name to MODULE. If no module
        with this name is linked, MODULE is unified with the empty list.

    getcwd(DIR^)
        Unifies DIR with a character list holding the name of the current
        directory.

    getpid(PID^)
        Unifies PID with the UNIX process ID of the currently executing
        program.

    global(NAME?, VAL?)
        If a global variable with the given name exists, its current value
        is unified with VAL. Otherwise the global variable will be created,
        set to a fresh unbound variable which will then be unified with
        VAL.

    halt(EXITCODE?)
        Terminates the program with the given exit code, which must be
        an integer.

    heap_statistics(TUPLE^)
        Unifies TUPLE with a tuple of 5 elements containing the number of
        bytes currently used in the threads's heap holding variables, tuples,
        list, floats and port objects.

    idle_thread(PEER^)
        Unifies PEER with the number of a random idle thread (other than
        the current). If no other thread is currently idle, PEER is unified
        with "false". Note that threads listening for input or other events 
        like signals are not considered idle.

    kill(SIGNAL?, PID?)
    kill(SIGNAL?, PID?, DONE^)
        Sends SIGNAL (which should be an integer or a string containing the 
        Linux/BSD uppercase signal name) to the process with the ID PID using the 
        kill(2) system call and unifies DONE with the empty list when the 
        operation is completed. If the system call fails, DONE will be unified
        with a tuple of the form "error(ERRNO, STRING)" where ERRNO designates 
        the error code and STRING a textual representation.

    listen(FILE?, VAR^)
        Registers the file descriptor FILE with the internal polling
        loop and unifies VAR with the empty list once data can be read 
        from the file. To listen again, invoke "listen" again for
        the same file. If an error occurs, then VAR will be unified with
        a tuple of the form "error(ERRNO, STRING)", where ERRNO is the UNIX
        error code and STRING a textual representation of the same.

    make_tuple(LENGTH?, TUPLE^)
        Unifies TUPLE with a fresh tuple of the specified length, the
        resulting tuple is populated with unbound variables. Signals
        an error if LENGTH is not between 0 and 127. Creating a tuple
        of length 0 will unify TUPLE with the empty list.

    module_exports(MODULE?, LIST^)
        Unifies LIST with the list of exported process definitions 
        contained in MODULE. Each element of the list will be a tuple of 
        the form '/'(NAME, ARITY).

    module_name(MODULE?, NAME^)
        Assigns astring holding the name of MODULE to NAME.

    number_to_list(NUMBER?, LIST^, TAIL?)
    number_to_list(NUMBER?, BASE?, LIST^, TAIL?)
        Converts the integer or real NUMBER into a list of character
        codes terminated by TAIL and unifies it with LIST.

    open_port(PORT^, LIST^)
        Creates a "port" object and unifies it with PORT. LIST is
        unified with a "stream" receiving objects send to PORT using
        the "send" primitive. When the port is not referenced anymore,
        the stream is closed by assigning the empty list to its tail.

    program_name(NAME^)
        Assign the name of the current executable to NAME as a string.

    put_arg(INDEX?, TUPLE?, ITEM?)
    put_arg(INDEX?, TUPLE?, ITEM?, DONE^)
        Unifies the INDEXth element of TUPLE with ITEM, assigning the
        empty list to DONE, if given. Indexing starts at 1. Signals an
        error if INDEX is out of range.

    put_global(NAME?, VAL^)
    put_global(NAME?, VAL^, DONE^)
        Assigns VAL to the global variable named by the string NAME and 
        unifies DONE with the empty list when the assignment is completed.
        Global variables are thread-local.

    restart_timer(ID?, MS?)
    restart_timer(ID?, MS?, RESULT^)
        Modifies the timeout for the timer identified by ID and previously
        created by calling "timer/3" or "clock/3" and unifies RESULT with 
        "true" when the operation is complete. MS specifies the new
        timeout (possibly periodic when ID designates a periodic timer).
        If no timer with the given ID is currently active, then this 
        operation does nothing and unifies RESULT with "false" (if given).

    self(ID^)
        Unifies ID with the thread number of the thread that is executing
        the current clause.

    send(PORT?, OBJECT?)
    send(PORT?, OBJECT?, DONE^)
        Sends OBJECT (which may be any data objecv whatsoever) to
        PORT and unifies DONE with the empty list, when given.

    signal(SIGNAL?, VAR^)
    signal(SIGNAL?, VAR^, DONE^)
        Installs a signal handler for the signal with the name given
        by the string SIGNAL, which should be an integer or a valid uppercase 
        Linux/BSD signal name. If the signal is raised, or was raised since the
        program started, the number of times the signal was raised since
        is added as an integer to the stream in VAR. 
        Handling of the signal can not be disabled, once the signal
        handler was installed.

    statistics(TUPLE^)
        Unifies TUPLE with a tuple of 4 elements containing the number of
        goals, active goals, suspended goals and used heap-cells.

    string_to_byte_list(STRING?, LIST^, TAIL?)
        Converts STRING to a list of bytes, terminated by TAIL.

    string_to_integer(STRING?, INTEGER^)
    string_to_integer(STRING?, BASE?, INTEGER^)
        Converts the atom STRING to an integer, interpreting the
        characters in STRING in BASE. BASE defaults to 10. If STRING
        can not be converted, then an error is signaled.

    string_to_list(STRING?, LIST^, TAIL?)
        Converts STRING to a list of UNICODE code points, terminated by
        TAIL.

    string_to_real(STRING?, REAL^)
        Converts the atom STRING to a floating-point number.  If STRING
        can not be converted, then an error is signaled.

    thread_resource_usage(UTIME^, STIME^, MAXRSS^)
        Assigns the current thread's time spend in user- and system-mode
        to UTIME and STIME as seconds in floating point format and
        assigns the maximum resident set size in kilobytes to MAXRSS.

    threads(NUMBER^)
        Unifies NUMBER with the number of threads, as given to the
        "+THREADS" run-time option on startup.

    ticks(TICKS^)
        Unifies TICKS with an integer counter, corresponding to "ticks"
        of the internal process scheduler.

    timer(MS?, VAR^, ID^)
        Establishes a single-shot timer that unifies VAR with the empty
        list after MS milliseconds have passed. ID is unified immediately
        with an integer identifying this timer and can be used to cancel
        the timer before it has expired.

    true
        [FGHC] Does nothing.

    tuple_to_list(TUPLE?, LIST^, TAIL?)
        Unifies LIST with the elements of TUPLE, terminated by TAIL. If
        TUPLE is the empty list, the result is the empty list.

    unify(X?, Y?, RESULT^)
        Unifies X with Y and finally unifies RESULT with the string "true" or 
        "false", depending on whether the unification succeeded or
        failed. Variables bound during the unification will be restored to 
        their unbound state if the full unification should fail.
        Note that this primitive receives the result variable in the third
        argument position, which differs from the description given in [1].
        Also note that "remote" variable bindings in other threads are
        not restored on failure and still trigger inter-thread messages
        that negotiate the final value.

        [1] "FLENG Prolog - The Language which turns Supercomputers into 
            Parallel Prolog Machines"
            Martin Nilsson and Hidehiko Tanaka 

    unify_with_occurs_check(X?, Y?, RESULT^)
        Similar to "unify/3", but fails in case a binding would make a variable
        directly or indirectly refer to itself. This primitive always performs
        the check, regardless of the "+OCCURS_CHECK" runtime option.

    utf_encode(CHAR?, LIST^, TAIL?)
        Convert UNICODE code points into a stream of bytes.
        Takes a character code and unifies LIST with the
        list of bytes required to encode CHAR, terminated by TAIL.

## 5. "$rt" module: low-level primitives

    The definitions in this module are available directly, without using
    any module prefix and provide some higher-order operations for
    invoking dynamically computed goals ("call", "trace", "run") and
    for FLENG's built-in "compute" operation.

    call(GOAL?)
    call(GOAL?, STATUS^)
    call(GOAL?, ENVIRONMENT?, STATUS^)
        Invokes the process specified by GOAL which must be a tuple
        or string. If GOAL is not a variable, then it may refer to a
        primitive operation (a suitable user-defined wrapper clause is
        synthesized by the compiler), but if it is a variable, then it 
        must refer to a user defined process.
        GOAL may refer to a module-qualified term and must be exported
        from the module in which it is defined. 
    
        "call/2" and "call/3" execute GOAL in a new group of processes
        called a "task". Every process created in GOAL belongs to the
        same task and once all processes in that group have terminated
        (including child tasks), STATUS is unified with the empty list.
        IF ENVIRONMENT is given, it is provided as a parameter associated
        with the task group and can be accessed by the "environment/1"
        primitive, otherwise the new task inherits the same environment
        as the current task (if any).
    
        Tasks may span multiple threads, a task is considered completed
        when all processes of the task and it's child tasks in all threads 
        have terminated.

    call_detached(GOAL?, STATUS^)
        Invokes GOAL in a new task, similar to "call/2", but does not
        Link the new task to the current one. Executuion of said new 
        task will be independent and allow the current task to complete
        without waiting for the new task.

    trace(GOAL?)
    trace(GOAL?, STATUS^)
        Similar to "call/2", but enabled logging for the newly created
        task to show entered predicates along with their arguments. 
        Tracing is inherited, so child tasks will be logged as well,
        even in other threads. Note that tracing only produces logging
        output for code that was compiled with the "-d" option.

    compute(OP?, ARG?, RESULT^)
    compute(OP?, ARG1?, ARG2?, RESULT^)
        Performs a unary or binary numeric computation. OP must be a
        string and may be one of the following:
    
                    Unary      Binary   Result
            +         -          x      Sum
            -         -          x      Difference
            *         -          x      Product
            /         -          x      Quotient
            mod       -          x      Remainder
            and       -          x      Bitwise AND
            or        -          x      Bitwise OR
            xor       -          x      Bitwise XOR
            shl       -          x      Shift bits left
            shr       -          x      Arithmetic shift bits right
            =         -          x      Equality comparison
            >         -          x      Is ARG1 numerically greater than ARG2
            <         -          x      Is ARG1 numerically less than ARG2
            min       -          x      Return lesser of the two arguments
            max       -          x      Return greater of the two arguments
            sametype  -          x      Have ARG1 an ARG2 the same type
            **        -          x      Exponentiation
            sqrt      x          -      Square root
            sin       x          -      Sine (radians)
            cos       x          -      Cosine (radians)
            tan       x          -      Tangent
            atan      x          -      Arc tangent
            exp       x          -      Exponential value
            abs       x          -      Absolute value
            sign      x          -      Sign (returns integer)
            rnd       x          -      Returns random integer
            real_integer_part
                      x          -      Extract integer part of float
            real_fractional_part
                      x          -      Extract fractional part of float
            integer   x          -      Convert float to integer
            real      x          -      Convert integer to float
            truncate  x          -      Truncate float
            floor     x          -      Round float to next lower integer
            round     x          -      Round float
            ceiling   x          -      Round float to next higher integer
    
        +, -, * and / produce real results if one of the arguments is
        a real number. "abs" returns a result of the same type as the argument.
        "**" always produces a real result. "and", "or", "xor", "rnd",
        "shl" and "shr" require integer arguments. With the exception of 
        "=", "sametype" the arguments must be bound to numbers. 
    
        Note that arguments to "compute" are not forced if unbound.
        Note that integer over- and underflow is not detected and will
        result in significant bits being silently truncated. "sametype"
        will not force unbound arguments (the type of the argument is
        considered "var" in that case).
    
        See "is/2" for a more convenient way to perform arithmetic 
        computations.
    
    current_module(MODULE^)
        Assigns the module containing this expression to MODULE.
    
    run(MODULE?, TERM?)
        Invokes TERM similar to "call/1" in the module represented by MODULE,
        which may be a module object or a name referring to a linked
        module.


## 6. "lib" module: FGHC standard library

    The definitions in this module are all available by default and
    are used without the module prefix.

    apply(GOAL?, LIST?)
        [FGHC] Invokes GOAL with a list of arguments in LIST. GOAL may be a
        string or tuples of the form "{<string>, <argument>, ...}",
        "{':', <modulename>, <string>}" or "{':', <modulename>, {<string>, 
        <argument>, ...}}". GOAL may be seen as a "closure", holding state 
        in a given list of values that is passed along with additional
        arguments when called.

    chdir(DIRECTORY?)
    chdir(DIRECTORY?, DONE^)
        Changes the current directory to DIRECTORY, which should be a
        string or a character list. When the operation is complete,
        DONE is unified with the empty list.
    
    chmod(FILENAME?, MODE?)
    chmod(FILENAME?, MODE?, DONE^)
        Changes file permission bits of the file with the given name to
        MODE, which must be an integer and unifies DONE with the empty
        list when the operation is completed. In case of an error, DONE
        is unified with a tuple of the form "error(ERRNO, STRING)", where 
        ERRNO is the UNIX error code and STRING is a textual representation 
        of the same.
    
    compare(X?, Y?, RESULT^)
        Unifies RESULT with -1, 0 or 1, depending on whether X is ordered
        below, equal or above Y, as compared with '@>'/2 and '@<'/2.
    
    delete_file(FILENAME?)
    delete_file(FILENAME?, DONE^)
        Deletes the file with the name FILENAME and unifies DONE with the
        empty list when the operation is completed. Note that no error
        is signaled if FILENAME does not exist.
    
    deref(OBJECT?)
    deref(OBJECT?, DONE^)
        Recursively derefences variables in OBJECT until the term
        is fully ground, possibly suspending. Once all variables inside
        OBJECT are bound, DONE is unified with the empty list.
    
    directory(NAME?, ITEMS^)
    directory(NAME?, ITEMS^, TAIL^)
        Reads the directory specified by the string or character list NAME
        into the list ITEMS, containing the names of included files as
        character lists and terminated by TAIL (or the empty list).
        If reading the directory should fail, then ITEMS will be unified
        with a tuple of the form "error(ERRNO, STR)", holding the error
        code and a textual description of the error as a string. The
        entries for "." and ".." are not included.

    environment(ENV^)
        Unifies ENV with the environment slot of the current task, or the
        empty list, if the current process does not belong to a task group.
    
    error(MESSAGE?)
        Writes MESSAGE to standard error and terminates the program
        with exit code 1. Message may have any type and will be written
        as is, even if it contains unbound variables.
    
    file_exists(FILENAME?, FLAG^)
        If FILENAME designates an existing file or directory, assign
        "true" to FLAG, otherwise "false". Signals an error if the
        system call fails due to other causes.
    
    file_modification_time(FILENAME?, TIME^)
        Unifies TIME with the UNIX timestamp of the last modification of the
        file FILENAME, which should be a string or character list. If an error
        occurs, TIME is unified with a term of the form "error(ERRNO, STRING)" 
        where ERRNO designates the error code and STRING is a textual 
        representation of the same.
    
    file_size(FILENAME?, BYTES^)
        Unifies BYTES with the number of bytes in the file named by the
        string or character list FILENAME. If an error occurs, BYTES is
        unified with a term of the form "error(ERRNO, STRING)" where ERRNO
        designates the error code and STRING is a textual representation of 
        the same.
    
    file_type(FILENAME?, TYPE^)
        Unifies TYPE with the string "file", "directory", "link", "fifo"
        or "socket" depending on what type of file FILENAME refers to.
        If an error occurs, TYPE is unified with a term of the form 
        "error(ERRNO, STRING)" where ERRNO designates the error code
        and STRING is a textual representation of the same. FILENAME may
        be a string or a character list.
    
    getenv(NAME?, VAL^)
        Unifies VAL with a character list holding the value of the environment
        variable of the given name, which should be a string or character list.
        If no variable with that name is defined, VAL is unisifed with the
        empty list.
    
    halt
        Terminates the program with exit code 0.
    
    length(OBJECT?, LEN^)
        Unifies LEN with the length of OBJECT, which may be a string,
        a list or a tuple.
    
    list_to_number(LIST?, NUMBER^)
        Unifies NUMBER with the number consisting of the characters
        in LIST, which must specify a valid integer or floating point 
        number. If the conversion fails, NUMBER is assigned the string 
        "error".
    
    list_to_real(LIST?, REAL^)
        Similar to "list_to_number/2", but implicitly converts the
        result into a floating-point number.
    
    list_to_integer(LIST?, NUMBER^)
    list_to_integer(LIST?, BASE?, NUMBER^)
        Similar to "list_to_number/2", but implicitly converts the
        result into a floating-point number. BASE should be an integer
        and defaults to 10.
    
    list_to_string(LIST?, STRING^)
        Unifies STRING with the string holding the UNICODE code points
        in LIST.
    
    list_to_tuple(LIST?, TUPLE^)
        Unifies TUPLE with the string holding the elements of LIST.
        If LIST is the empty list, TUPLE will be unified with the empty
        list as well (there are no empty tuples).
    
    log(MESSAGE?)
    log(MESSAGE?, DONE^)
        Dereferences MESSAGE fully and writes it into the log file.
        After the message is written, DONE is unified with the empty list.
    
    merger(INSTREAM?, OUTSTREAM^)
        Takes elements from INSTREAM and writes them to OUTSTREAM.
        Element of the form "merge(STREAM?)" in INSTREAM result in 
        merging the elements from STREAM into OUTSTREAM. Items are added 
        to the output stream in an arbitrary order, but retain the
        order from the respectively merged streams. Merged streams
        may again receive "merge(STREAM)" messages. Once all streams
        are terminated with the empty list, OUTSTREAM is terminated
        as well.
    
    mkdir(NAME?)
    mkdir(NAME?, DONE^)
        Create a directory with given name. NAME should be a string 
        or character list. Signals an error if the operation fails and
        unifies DONE with the empty list on success. It is not an error
        if the directory already exists.

    nl
    nl(DONE^)
        Write a single newline, unify DONE with the empty list when the
        operation is complete.

    open_file(NAME?, MODE?, FILE^)
        Creates or opens a file with the name NAME in the given MODE,
        which may be one of the strings (not character lists!) "r" (read),
        "w"(write) or "a" (append). The file descriptor will be unified
        with FILE.  If the system call fails, FILE will be unified with
        a tuple of the form "error(ERRNO, STRING)", where ERRNO is the UNIX
        error code and STRING is a textual representation of the same.
    
    randomize(SEED?)
    randomize(SEED?, DONE^)
        Initializes the internal pseudo random number generator. SEED
        should be a string or a byte list. The buffer holding the random
        state has a size of 16 machine words. If the byte sequence given
        by SEED has a smaller size, then initialization "wraps around"
        to the start of SEED again. Once the random number generator
        has been initialized, DONE is unified with the empty list.
        On startup the random number generator is seeded with the
        system time.
    
    read_file(FILE?, COUNT?, LIST^, TAIL?)
        Reads COUNT bytes from the file designated by the file descriptor
        FILE and unifies LIST with the read data, terminated by TAIL.
        If an error occurs, then LIST will be unified with a tuple of the
        form "error(ERRNO, STRING)", where ERRNO is the UNIX error code
        and STRING is a textual representation of the same.
    
    readlink(FILENAME?, VAL^)
        Unifies VAL with a character list holding the contents of the
        symbolic link FILENAME. If an error occurs, VAL is
        unified with a term of the form "error(ERRNO, STRING)" where ERRNO
        designates the error code and STRING is a textual representation of 
        the same.
    
    real_to_list(REAL?, LIST^)
        Converts the floating-point number REAL to a list of character codes.
    
    rmdir(FILENAME?)
    rmdir(FILENAME?, DONE^)
        Deletes the directory with the name FILENAME and unifies DONE with the
        empty list when the operation is completed. Note that no error
        is signaled if FILENAME does not exist. In case the operation
        fails DONE is unified with a tuple of the form "error(ERRNO, STRING)",
        where ERRNO is the UNIX error code and STRING is a textual 
        representation of the same.
    
    utf_decode(LIST?, OUTLIST^)
    utf_decode(LIST?, CHAR^, REST^)
        Converts bytes in LIST to UNICODE code points. The former
        primitive writes characters into the stream OUTLIST until
        LIST is exhausted and closes OUTLIST by unifying it with the
        empty list. The latter primitive converts a single character
        and unifies REST with the remaining elements of LIST.
    
    utf_encode(INLIST?, OUTLIST^)
        Convert UNICODE code points into a stream of bytes. 
        Receives a stream of code points and
        produces a stream of bytes until the end of INLIST is reached,
        after which OUTLIST is unified with the empty list.
    
    write(OBJECT?)
    write(OBJECT?, DONE^)
        Fully dereferences OBJECT and writes it to standard output.
        After writing, DONE is unified with the empty list, if given.
    
    write_file(FILE?, DATA?, REST^)
    write_file(FILE?, DATA?, COUNT?, REST^)
        Writes COUNT bytes of DATA to the file designated by the file
        descriptor FILE and unifies REST with the empty list or any
        yet unwritten data. DATA may be a string or a byte list.
        If COUNT is not given, writes DATA completely. When an error
        occurs, REST will be unified with a tuple of the form
        "error(ERRNO, STRING)", where ERRNO is the UNIX error code
        and STRING is a textual representation of the same.
    
    writeln(OBJECT?)
    writeln(OBJECT?, DONE^)
        Fully dereferences OBJECT and writes it to standard output,
        followed by a newline character. After writing, DONE is unified
        with the empty list, if given.


## 7. "sys" module: Event-loop for thread-communication

      This module starts the internal event system for inter-thread 
      communication and is used automatically. Additionally, some library
      predicates are available to communicate with external FLENG processes.

    sys:attach(FILENAME?, FILE^, ADDR^)
          Access detached message port at FILENAME and unify ADDR with an 
          address to the mapped memory area and FILE with the file 
          representing FILENAME.

    sys:detach(FILE?, ADDR?)
    sys:detach(FILE?, ADDR?, DONE^)
          Release the attached message port designated by FILE and ADDR.

    sys:detach_message_port(FILENAME?)
    sys:detach_message_port(FILENAME?, DONE^)
          Detach message port to external file given and unify DONE with 
          the empty list when the operation is complete. The thread will 
          wait continuously for messages to arrive on the detached port 
          and so will not terminate by itself. Messages forwarded to
          the port will invoke "'':forwarded(MESSAGE?)".

    sys:transmit(ADDR?, MESSAGE?)
    sys:transmit(ADDR?, MESSAGE?, DONE^)
          Dereference MESSAGE completely and transmit it to the detached 
          message port identified by ADDR, then unify DONE with the empty 
          list. MESSAGE may not contain unbound variables, references to 
          modules or ports. The receiver is expected to define a clause 
          named '':forwarded(MESSAGE?) that will be called with the
          transmitted message object.


## 8. "fmt" module: Formatted output

    Support for formatted output and string construction. These
    operations take a string describing a format and a list of
    arguments to be displayed, depending on the formatting instructions 
    encoded in the string.
    
    Character sequences in the format string are interpreted in the
    following manner:

            ~~  Output the "~" (tilde) character.
    
            ~a  Output the unquoted characters of the next argument
                which must be a string.

            ~s  Output the string, byte block or character list given in the 
                next argument.
    
            ~d  Output the next argument, which must be a number, in
                decimal notation.
    
            ~x  Output the next argument, which must be a number, in
                hexadecimal notation.
    
            ~w  Output the next argument term in operator syntax.
    
            ~q  Output the next argument term in operator syntax and with
                strings quoted, if necessary.
    
            ~c  Output the next argument which must be an integer as
                a single character.
    
            ~n  Output a newline character.

            ~?  Take a format-string and argument-list, format recursively.

    Use "format_chars" to format into a character list instead of writing
    the result to an output file.

    fmt:format(STRING?, ARGS?)
    fmt:format(FILE?, STRING?, ARGS?)
    fmt:format(FILE?, STRING?, ARGS?, DONE^)
          Writes the values in the list ARGS in a manner described by
          the format string STRING to FILE or to standard output. 
          STRING may be a string or a list of character codes and may 
          contain special characters describing how to output the next 
          item in ARGS.

          Write the formatted output to stdout and assign [] to DONE
          when finished.

    fmt:format_chars(STRING?, ARGS?, OUT^)
    fmt:format_chars(STRING?, ARGS?, OUT^, TAIL?)
          Format to list of character codes and assign to OUT,
          optionally with a list tail, which defaults to the empty_list.
          STRING may be a string or a list of characters.


## 9. "sort" module: Sorting

    Sorting of lists, using the total ordering of values provided by the
    "compare" primitive. "sort" sorts a list in ascending order,
    "keysort" sorts a list holding key-value tuples and "merge" merges
    two sorted lists into one.

     Originally from the Edinburgh DEC-10 Prolog utility Library
     Author : R.A.O'Keefe
     this code is in the public domain

    sort:sort(LIST?, RESULT^)
          Sorts the terms in LIST in term order, ascending and assign
          the result to RESULT. Duplicate keys are removed.

    sort:keysort(LIST?, RESULT^)
          Sorts LIST by keys, where each element of LIST is a 3-tuple
          of the form "KEY-VALUE", and assigns the sorted list to RESULT.
          Duplicate keys are removed.
    
    sort:merge(LIST1?, LIST2?, RESULT^)
          Merge the sorted lists and assign the sorted result to
          RESULT.


## 10. "map" module: Key-value maps

    Maps based on AVL trees. The maps allow random-access and are fully
    functional: inserting, deleting or replacing a value will create a
    new map that will share contents with the old one, but both new and old 
    map are fully independent. 

    Use "insert" and "replace" to add values, "lookup" to search the map for
    a key and "delete" to delete entries. "keys" and "values" retrieve
    lists of all keys and values of a map, respectively. "list_to_map" and"
    "map_to_list" convert maps to and from lists.

    The implementation of this module was inspired by:
    https://two-wrongs.com/purely-functional-avl-trees-in-common-lisp.html

    map:node(KEY?, VAL?, TREE^)
          Create a new TREE with a single entry.

    map:insert(KEY?, VAL?, TREE?, RTREE^)
          Insert new entry into TREE, assigning the new tree to RTREE.
          If KEY already exists, the existing node is replaced.

    map:delete(KEY?, TREE?, RTREE^)
    map:delete(KEY?, TREE?, VAL^, RTREE^)
          Delete entry from TREE and assign new tree to RTREE, optionally
          assigning old existing value (or []) to VAL.

    map:replace(KEY?, VAL?, TREE?, RTREE)
    map:replace(KEY?, VAL?, TREE?, OLDVAL^, RTREE^)
          Replace existing entry in TREE with new value, optionally assigning
          the old value (or []) to OLDVAL. RTREE holds the final result tree.
          If the entry does not yet exist, create a new one.

    map:lookup(KEY?, TREE?, VAL^)
    map:lookup(KEY?, TREE?, VAL^, DEFAULT?)
          Unifies the value for the entry with the given KEY in TREE to VAL,
          unifies [] (or DEFAULT) if no such entry exists.

    map:keys(TREE?, KEYS^)
    map:keys(TREE?, KEYS^, TAIL?)
          Collects the keys of all entries and assigns the list of keys to KEYS.
          The returned list holds the keys in ascending order.

    map:values(TREE?, VALS^)
    map:values(TREE?, VALS^, TAIL?)
          Collect the values of all entries and assigns the list to VALS.
          The returned list holds the values in the ascendng order of the map's keys.

    map:list_to_map(LIST?, MAP^)
    map:list_to_map(LIST?, MAP1?, MAP^)
          Insert elements from LIST into MAP1 (or into a new map) and 
          unifies MAP with the new map, LIST should have elements of the 
          form "{'-', <KEY>, <VALUE>}" or "{<KEY>, <VALUE>}".

    map:map_to_list(MAP?, LIST^)
    map:map_to_list(MAP?, LIST^, TAIL?)
          Collects keys and values into a list with elements of the form
          "{'-', <KEY>, <VALUE>}". The list will be in sorted key order,
          ascending.

    map:root(MAP?, KEY^, VALUE^)
          Assigns KEY and VALUE the key and value of the root node of MAP.
          If the map is empty, the predicate fails.

    map:above(MAP?, RMAP^)
    map:below(MAP?, RMAP^)
          Unifies RMAP with the submap holding the elements above or
          below the root node of MAP.


## 11. "list" module: List operations

    This module provides various types of operations on lists, like 
    extraction ("take", "drop", "cut", "slice", "split"), appending ("append",
    "join"), searching ("member", "search", "scan") and deleting elements 
    ("trim", "delete").

    list:take(NUM?, LIST?, RESULT^, TAIL?)
          Copy the first NUM elements of LIST into RESULT, terminated by TAIL.

    list:drop(NUM?, LIST?, RESULT^)
          Remove the first NUM elements from LIST and unify RESULT with the
          remaining list.

    list:cut(NUM?, LIST?, TAIL?, RESULT^, REST^)
          Copy the first NUM elements of LIST into RESULT, terminated by TAIL
          and unify REST with the remaining list.

    list:make(NUM?, LIST^, TAIL?)
    list:make(NUM?, VALUE?, LIST^, TAIL?)
          Create a list of NUM elements, initialized to VALUE and terminated by
          TAIL. If VALUE is not given, initialize each element to a fresh 
          variable.

    list:split(LIST?, SEP?, RESULT^)
    list:split(LIST?, SEP?, RESULT^, TAIL?)
          Splits the list into sublists, separated by SEP.

    list:scan(LIST?, X?, RLIST^, TAIL^)
    list:scan(LIST?, X?, RLIST^, RTAIL^, TAIL^)
          Collect elements of LIST in RLIST until the end of LIST or element 
          occurs X and assign remaining elements to TAIL. If given, the end
          of RLIST is terminated with RTAIL or the empty list otherwise.

    list:iota(N?, LIST^, TAIL?)
    list:iota(N?, START?, LIST^, TAIL?)
    list:iota(N?, START?, STEP?, LIST^, TAIL?)
          Creates a sequence of N integers START, START + STEP, ... .
          START and STEP default to 1. The result list is terminated with TAIL 
          and unified with LIST.

    list:slice(INDEX?, NUM?, LIST?, RESULT^, TAIL?)
          Returns a list of NUM elements, starting at index INDEX in LIST,
          terminating the extracted list with TAIL. Indexing starts at 1.

    list:search(NEEDLE?, HAYSTACK?, POSITION^)
    list:search(NEEDLE?, HAYSTACK?, PREFIX^, TAIL?, REST^)
          Searches for the list NEEDLE in HAYSTACK and either assigns the
          index of the sublist to POSITION (or 0 if not found) or unifies 
          PREFIX with the list of elements in HAYSTACK preceeding NEEDLE 
          (terminated by TAIL) and REST with all remaining elements.

    list:trim(LIST?, SET?, RESULT^)
    list:trim_left(LIST?, SET?, RESULT^)
    list:trim_right(LIST?, SET?, RESULT^)
          Remove elements from LIST found in SET from start (left), end 
          (right) or both end and unify the result list with RESULT.

    list:delete(ITEM?, LIST?, RESULT^)
          Remove all elements equal to ITEM from LIST and unify the resulting
          list with RESULT.

    list:reverse(LIST?, RESULT^)
    list:reverse(LIST?, RESULT^, TAIL?)
          Reverse list and unify with RESULT, optionally terminated by TAIL.

    list:append(LISTS?, LIST^)
    list:append(LIST1?, LIST2?, LIST^)
          Concatenates all lists in LISTS (or both LIST1 and LIST2) and 
          unifies the result with LIST.

    list:member(VALUE?, LIST?, BOOL^)
          Unifies BOOL with the string "true" or "false", depending on
          whether LIST contains a toplevel element that matches VALUE.

    list:last(LIST?, RESULT^)
          Unifies RESULT with the last element of LIST (or the empty list
          if LIST is empty).

    list:prefix(PREFIX?, LIST?, RESULT^)
          Unifies RESULT with the string "true" or "false", depending
          on whether LIST begins with the list PREFIX or not.

    list:suffix(SUFFIX?, LIST?, RESULT^)
          Unifies RESULT with the string "true" or "false", depending
          on whether LIST ends with the list SUFFIX or not.

    list:join(LIST?, SEP?, RESULT^, TAIL?)
          Unifies RESULT with all elements of the list of lists LIST,
          separated by SEP. The final result is terminated with TAIL.

    list:characters(THING?, LIST^)
    list:characters(THING?, LIST^, TAIL?)
       Converts THING, which should be a string or number, into a list of
       characters and unifies the result with LIST, optionally terminated
       by TAIL, which defaults to the empty list. If THING is already a 
       list, it is not changed.

    list:get_prop(PROP?, LIST?, VALUE^, DEFAULT?)
      Searches for a 2-tuple {PROP, THING} in LIST and unifies VALUE
      with the corresponding THING, if found, or with DEFAULT, otherwise.

    list:zip(LIST1?, LIST2?, RESULT^)
      Unifies RESULT with a list of 2-element tuples containing the
      elements of LIST1 and LIST2.

    list:nth(INDEX?, LIST?, RESULT^)
      Unify RESULT with the INDEXth item of LIST, starting from index 1.
      Signals an error if the list is too short.


## 12. "set" module: Lists as sets

    This module provides operations that treat lists as sets, i.e.
    collections of unique items. "union", "intersection" and "difference" 
    perform the corresponding set operations. "Subset" tests for one
    set being a subset of another, "equal" compares sets, regardless of
    the order of elements.

    set:difference(SET1?, SET2?, RESULT^)
          Computes the set-difference of removing SET2 from SET1.
    
    set:intersection(SET1?, SET2?, RESULT^)
          Computes the intersection of the two lists.
    
    set:union(SET1?, SET2?, RESULT^)
          Computes the union of the two lists.

    set:equal(SET1?, SET2?, RESULT^)
          Unifies RESULT with "true" or "false", depending in whether
          SET1 is the same as SET2.

    set:adjoin(ITEM?, SET?, RESULT^)
          Add element to SET.

    set:subset(SET1?, SET2?, RESULT^)
          Assigs the string true or false to RESULT, depending on whether
          SET1 is a subset of SET2.


## 13. "proc" module: Subprocess invocation

    This module allows invoking sub-processes. The main operation is
    "execute", which takes a program name with optional arguments and
    starts a child process. A status variable will be bound once the
    child process terminates. Further options allow redirecting input
    and output for the child process to and from files or file streams.

    The convenience procedures "shell", "capture", "submit" and "pipe"
    all build on "execute" to simplify running processes without
    redirection or for directly providing data for in- or output of
    the child process. 

    proc:execute(STRLIST?, STATUS^)
    proc:execute(STRLIST?, OPTIONS?, PID^, STATUS^)
      Execute command in STRLIST, which may be a string or a list
      of strings or character lists, with I/O channels connected to the 
      child process.
      OPTIONS is a list of one ore more forms of the following kind:
        open(CH?, STR?)     open file named STR and redirect from/to 
                            channel
        close(CH?)          close channel
        pipe(CH?, FILE^)    open bi-directional pipe for channel
        CH may be one of the strings in, out or err. PID and STATUS are
        unified with the process-identifier of the sub-process and the
        exit status (once it has terminated).

    proc:shell(STRING?, STATUS^)
      Execute the command in "[sh, '-c', STRING]" (as in execute/3) and assign 
      the exit status to STATUS.

    proc:capture(STRLIST?, RESULT^)
    proc:capture(STRLIST?, RESULT^, STATUS^)
      Execute the command in STRLIST (as in execute/3) with
      the output of the command captured in a character list and 
      assigned to RESULT. The result string will have trailing whitespace
      removed. STATUS, if given is assigned the exit status of the call.

    proc:submit(STRLIST?, INPUT?)
    proc:submit(STRLIST?, INPUT?, STATUS^)
      Execute the command in STRLIST (as in execute/3) with
      the INPUT holding a character stream that should be sent as the
      subprocess' input. STATUS, if given is assigned the exit status of 
      the call.

    proc:pipe(STRLIST?, INPUT?, OUTPUT^)
    proc:pipe(STRLIST?, INPUT?, OUTPUT^, STATUS^)
      Execute the command in STRLIST (as in execute/3) taking the
      character stream INPUT as input. OUTPUT is a character stream holding
      the  output of the command. Execution of the subprocess continues until
      INPUT is closed. STATUS, if given is assigned the exit status of 
      the call.


## 14. "lex" module: FGHC/FLENG lexical analyzer

    Support lexical analysis and tokenization of FGHC, Strand and FLENG
    source code. Only one operation is exposed, namely "lex_file".

    lex:lex_file(FILENAME?, OUT^, ERRORS?)
      Performs lexical analysis of the given list or file and writes a stream of
      tokens to OUT. Errors are send to the port ERRORS in the form
      error(LINE, FMT, ARGS, EXITCODE).


## 15. "parse" module: FGHC/FLENG parser

    A parser for FGHC, Strand and FLENG source code. This module is used
    internally by the FGHC and FLENG compilers and expose a few procedures
    for taking a stream of tokens (as produced by the "lex" module) and
    producing another stream of terms.

    "parse_terms" takes a token stream or a file and produces a term
    stream. Additionally, a port is expected that receives error messages
    that can then be presented to the user. If a file is the source of
    tokens, a lexical analyzer is implicitly started.

    parse:parse_terms(FILE?, FORMS^, ERRS?)
    parse:parse_terms(TOKENS?, FORMS^, ERRS?)
    parse:parse_terms(FILE?, VTAG?, FORMS^, ERRS?)
    parse:parse_terms(TOKENS?, VTAG?, FORMS^, ERRS?)
          Parses period-terminated toplevel terms from the token stream
          TOKENS or a token-stream read from FILE, with VTAG being a unique 
          integer used for tagging variable placeholders (of the form 
          "'$VAR'(VTAG, INDEX, NAME)") and writes each toplevel form into the 
          stream FORMS. ERRS is a port receiving error messages in the form 
          "error(LINENUM, FMT, ARGS, CODE)".

    parse:parse_term(TOKENS?, VTAG?, RTOKENS^, EXP^, ERRS?)
          Parses a single term, terminated by period and assigns it to
          EXP. ERRS is a port receiving error messages.

    parse:parse_term_list(LIST?, FORMS^, ERRS?)
    parse:parse_term_list(LIST?, VTAG?, FORMS^, ERRS?)
          Parses period-terminated expressions from LIST.


## 16. "array" module: Mutable numeric arrays

    This module provides random access numeric arrays. Arrays are
    allocated dynamically and automatically freed after the all
    references are dropped. Elements of arrays are stored in native
    format. Indexing starts at zero.

    Note that arrays represent shared mutable data, no provisison is
    made to avoid the problems caused by concurrent read/write access 
    to shared memory. It is the responsibility of the user of these
    arrays to ensure correctness in the presence of multiple processes.

    Element types correspond to the following C types:

      byte            unsigned octet ("unsigned char")
      char            unsigned 32 integer ("unsigned int")
      int             machine-word sized integer ("long")
      double          double precision floating point ("double")

    Arrays are created with "make", and accessed with "put" and "get".
    Operations for reading and writing arrays in native format to
    file streams are provided.

    It is also possible to create "views", which are array slices
    refering to another array. Any change to the underlying array
    will be visible in the slice.

    array:make(TYPE?, LENGTH?, ARRAY^)
      Creates an array of the given type, with LENGTH elements.
      TYPE may be one of the strings 'int', 'char', 'double' or 'byte'.
      The array has initially random contents.

    array:put(INDEX?, DATA?, ARRAY?)
    array:put(INDEX?, DATA?, ARRAY?, DONE^)
      Store the values from the list DATA in ARRAY at the given index.
      DATA may also be a single numeric value or a string. After the operation 
      is complete, the index of the element following the stored data is 
      assigned to DONE. If the data list  exceeds the array range, the 
      remaining elements are ignored.
      Data elements may be a floating point values if ARRAY is of type 
      "double". In all other cases they must be a integers.

    array:get(INDEX?, LENGTH?, ARRAY?, DATA^)
    array:get(INDEX?, LENGTH?, ARRAY?, DATA^, TAIL?)
      Extract LENGTH elements at INDEX from ARRAY and store them in the
      list DATA, optionally terminated by TAIL (which defaults to the
      empty list). Only elements up to the size of the array are extracted.

    array:write(INDEX?, LENGTH?, ARRAY?, FILE?)
    array:write(INDEX?, LENGTH?, ARRAY?, FILE?, DONE^)
      Write LENGTH elements at INDEX in ARRAY in native format to the
      given file descriptor and unify DONE with the number of written bytes
      when the operation is complete. Writing stops then the array length
      is exceeded.

    array:read(FILE?, INDEX?, LENGTH?, ARRAY?)
    array:read(FILE?, INDEX?, LENGTH?, ARRAY?, DONE^)
      Read LENGTH elements in native format from FILE and store them in
      ARRAY, starting at INDEX. When the operation is complete DONE is
      unified with the empty list. Reading stops when the array length
      is exceeded.

    array:fill(VALUE?, ARRAY?)
    array:fill(VALUE?, ARRAY?, DONE^)
    array:fill(VALUE?, INDEX?, LENGTH?, ARRAY?)
    array:fill(VALUE?, INDEX?, LENGTH?, ARRAY?, DONE^)
      Fills ARRAY at INDEX with LENGTH elements of the given VALUE.
      INDEX defaults to 0, LENGTH to the length of the array. When
      the operation is complete, DONE is unified with the empty list.
      VALUE may be a floating point value if ARRAY is of type "double".
      In all other cases it must be an integer.

    array:copy(FROMARRAY?, TOARRAY?, FROMINDEX?, TOINDEX?, COUNT?)
    array:copy(FROMARRAY?, TOARRAY?, FROMINDEX?, TOINDEX?, COUNT?, DONE^)
      Copies COUNT elements from FROMARRAY to TOARRAY at the given
      indices and unifies DONE with the empty list when the operation 
      is complete.

    array:size(ARRAY?, SIZE^)
      Assigns the number of elements in ARRAY to SIZE. If ARRAY is not an
      array, SIZE will be set to 1.

    array:type(ARRAY?, TYPE^)
      Unifies TYPE with the element type of ARRAY, which is one of the
      strings "byte", "int", "char" or "double".

    array:view(INDEX?, LENGTH?, ARRAY?, VIEWARRAY^)
    array:view(TYPE, INDEX?, LENGTH?, ARRAY?, VIEWARRAY^)
      Creates a "view", a slice of ARRAY at the given index and length
      and assigns it to VIEWARRAY. The two arrays share the same storage.
      The underlying memory of an array is released when the array and
      all views of it are not longer accessible. TYPE may be given
      explicitly to determine the type as which the shared elements
      are exposed and defaults to the type of the original array.

    array:clone(ARRAY?, CLONED^)
      Creates a read-only copy of ARRAY and assigns it to CLONED.

    array:pack(OBJECT?, ARRAY?, INDEX?, RINDEX^)
      Writes a binary representation of OBJECT into the integer array
      ARRAY at position INDEX and assigns the index of the first unused
      item after the stored object in the array to RINDEX. Note that
      arrays, ports, modules and variables can not be serialized using 
      this operation. If the array is not large enough to contain the
      serialization, the empty list will be assigned to RINDEX. If OBJECT
      contains unserializable data, the string "error" will be assigned
      to RINDEX.

    array:unpack(ARRAY?, INDEX?, RINDEX^, OBJECT^)
      Reads the binary representation of a serialized object from the
      integer array ARRAY at position INDEX and unifies the object and
      the position of the rest of the array to OBJECT and RINDEX,
      respectively.


## 17. "app" module: Higher-order operations on lists

    This module provides operations to invoke user-specifcied goals
    over lists, like fold, mapp, filter and partition.

    All goals default to the empty module ('':...) when no module prefix 
    in given. Goals are invoked using "apply/2".

    The FGHC->FLENG compiler will detect and wrap primitive operations in GOAL 
    position, but if GOAL is a variable, then it must refer to a 
    user-defined process.

    [PCN] For PCN programs, the goals must be user-defined and given as 
    strings or tuples of the form

      {"<name>", <argument>, ...}

    Calls to specific modules should be encoded as tuples like this:

      {":", "<module>", "<name>"}
      {":", "<module>", {"<name>", <arguments>, ...}}

    app:maplist(GOAL?, LIST?, RESULTLIST^)
    app:maplist(GOAL?, LIST?, RESULTLIST^, TAil?)
          Invoke "apply(GOAL?, [ELEMENT?, RESULT^])" for each element of LIST and
          unify the list of results with RESULTLIST, optionally terminated by
          TAIL.

    app:foreach(GOAL?, LIST?)
    app:foreach(GOAL?, LIST?, DONE^)
          Invoke "apply(GOAL?, [ELEMENT?])" for each element of LIST and unifies DONE
          with the empty list when the last application is invoked.
          The applications of GOAL are performed as a separate task and
          each application only takes place after the previous one completed.

    app:filter(GOAL?, LIST?, RESULTLIST^)
    app:filter(GOAL?, LIST?, RESULTLIST^, TAIL?)
          Invoke "apply(GOAL?, [ELEMENT?, RESULT^])" for each element of LIST and
          unify the list of elements for which RESULT was "true" with RESULTLIST,
          optionally terminated with TAIL.

    app:partition(GOAL?, LIST?, TRUERESULTS^, FALSERESULTS^)
          Invoke "apply(GOAL?, [ELEMENT?, RESULT^])" for each element of LIST and
          unify the list of elements for which RESULT was "true" with TRUERESULTS and
          those for which RESULT was "false" with FALSERESULTS.

    app:foldl(GOAL?, LIST?, INITIAL?, FINAL^)
          Invoke "apply(GOAL, [PREVIOUS?, ELEMENT?, RESULT^])" for each element of 
          LIST, where PREVIOUS holds the result of the previous application (INITIAL if this
          is the first one) and unifies FINAL with the final result.

    app:mapreduce(GOAL?, LIST?, INITIAL?, FINAL^)
          Similar to app:foldl, but invokes GOAL over as many threads as LIST has
          elements.

    app:take(GOAL?, LIST?, RESULT^, TAIL^)
    app:take(GOAL?, LIST?, RESULT^, TAIL^, MORE^)
          Unify RESULT with the elements of LIST until "call(ELEMENT, FLAG)"
          assigns "false" to FLAG. The result list is terminated with TAIL.
          if MORE is given, it will be unified with the remaining list in case
          GOAL returns false.

    app:drop(GOAL?, LIST?, RESULT^)
          Unify RESULT with the elements of LIST remaining when "call(ELEMENT, FLAG)"
          first assigns "false" to FLAG.

    app:any(GOAL?, LIST?, RESULT^)
          Unifies RESULT with "true" if "apply(GOAL, [ELEMENT?, FLAG^])"
          assigs a value other than "false" to FLAG for at least one of the 
          elements of LIST.

    app:every(GOAL?, LIST?, RESULT^)
          Unifies RESULT with "true" if "apply(GOAL, [ELEMENT?, FLAG^])"
          assigs a value other than "false" to FLAG for all elements of LIST.

    app:sequence(GOAL?, COUNT?, RESULT^, TAIL?)
    app:sequence(GOAL?, START?, COUNT?, RESULT^, TAIL?)
          Call GOAL with additional arguments [N, R], where N increases from
          START (or 1) to COUNT (inclusive) and unify RESULT with the list of 
          results R, terminated by TAIL.

    app:compose(GOALS?, INITIAL?, RESULT^)
          Invokes each goal in the list GOALS with "apply(GOAL?, VALUE?, 
          NEXT^)", taking the result of the previous application (INITIAL,
          in the first GOAL and the previous NEXT in all subsequent goals)
          and unifies RESULT with the final value.


## 18. "io" module: Extended I/O stream operations

    This module contains several procedures to read and write from
    byte and character-streams. Character-handling is assuming UTF-8
    encoding. Use "read_byte_stream" and "write_byte_stream" for
    raw binary access and "read_char_stream" and "write_char_stream"
    for automatic decoding and encoding from and to UTF-8. 

    "read_lines", "parse_lines" and "write_lines" access whole lines
    of in- or output.

    To have better control over the speed at which data is consumed,
    use "read_byte_stream_chunked", "read_lines_bounded" or 
    "rate_limited_stream".

    io:read_byte_stream(FILE?, LIST^)
        Reads bytes from the file designated by the file descriptor FILE
        and writes them into the stream given by LIST until the end of
        file is reached.
    
    io:read_byte_stream_chunked(FILE?, LIST^, CHUNKS^)
        Reads bytes from the file designated by the file descriptor FILE
        and writes them as separate lists into LIST. The length of each
        sublist is taken from the stream CHUNKS. When the end of file is
        reached then LIST is terminated with an empty list as it's 
        remaining element (or an error indicator in case of a read-error).
        This library primitive allows control over the amount of bytes
        read, something that is not possible with "read_byte_stream", the
        latter always reading as many bytes as quickly as possible, which
        may exhaust available memory when the consumer of the output
        stream is too slow.
    
    io:read_char_stream(FILE?, LIST^)
        Reads UNICODE characters from the file designated by the file
        descriptor FILE and writes them into the stream given by LIST
        until the end of file is reached.
    
    io:write_byte_stream(IN?, FILE?)
    io:write_byte_stream(IN?, FILE?, DONE^)
        Writes the bytes in the integer stream IN blockwise into FILE 
        until IN is empty. The operation may block the current thread.
        This predicate fails if the underlying file-system operation 
        should result in an error. DONE, when given, is unified with the 
        empty list when the operation is complete.
    
    io:write_char_stream(IN?, FILE?)
    io:write_char_stream(IN?, FILE?, DONE^)
        Writes the UNICODE code points in the integer stream IN blockwise 
        into FILE until IN is empty. The operation may block the current 
        thread. This predicate fails if the underlying file-system 
        operation should result in an error. DONE, when given, is unified 
        with the  empty list when the operation is complete.

    io:read_lines(FILE?, LINES^)
      Read lines from FILE and store each line as a character list in the 
      stream LINES. This operation will read lines "eagerly", so reading from
      a large file may consume memory unless the lines are processed quick
      enough.

    io:parse_lines(IN?, LINES^)
      Parses data from the stream IN into a list of byte/character lists
      and unify the result with LINES.

    io:write_lines(LINES?, FILE?, DONE^)
      Writes elements from the list LINES to FILE and unifies DONE with the empty
      list when finished. LINES may contain numbers, strings or character lists
      and each line is terminated by "\n".

    io:read_lines_bounded(FILE?, IN?, LINES^)
      Read lines from FILE, but fills LINES with variables holding input lines
      as supplied by the stream IN. It provides a "bounded buffer", producing
      lines only on demand and not filling up memory with still unprocessed input. 
      If IN is terminated by the empty list, reading will stop. 

    io:transfer(FROM?, TO?, DONE^)
    io:transfer(FROM?, TO?, COUNT?, DONE^)
      Copy data from the file descriptor FROM to TO, at most COUNT bytes,
      or all if COUNT is not given. Unifies DONE with the empty list when the
      operation is complete.

    io:rate_limited_stream(RATE?, STREAM^, ID^)
      Starts a timer producing a stream of unbound variables at RATE per
      second. ID holds the timer ID and can be used to terminate the
      stream by calling "cancel_timer(ID)".


## 19. "scan" module: Simple parsers for various character sequences

    This module provides "scanners", operations that take a character
    list and parse it according to certain criteria. "identifier", "number",
    "word", "integer" and "whitespace" scan the prefix of a character
    list as long as it falls into the required character class. 
    "delimited", "from_set" and "not_from_set" give a bit more control
    over the characters pass and whose that do not.

    scan:integer(NUM^, LIST?, REST^)
           Unify NUM with integer digits from LIST and unify REST with the
           remaining list. The integer may start with the "-" character.

    scan:number(NUM^, LIST?, REST^)
           Unify NUM with numeric characters representing a valid number 
           from LIST and unify REST with the remaining list, or unify N with
           the empty list if no number is found. The number may start with 
           the "-" character.

    scan:word(WORD^, LIST?, REST^)
           Unify WORD with non-whitespace characters from LIST 
           and unify REST with the remaining list, or unify WORD with
           the empty list if no word is found.

    scan:delimited(DELIM?, STRING^, LIST?, REST^)
    scan:delimited_with_escape(DELIM?, STRING^, LIST?, REST^)
           Unify STRING with characters from LIST until the character code DELIM
           is found and unify REST with the remaining list. 
           "delimited_with_escape" also expands escape sequences 
           "\n", "\r", "\t", "\b", "\f", "\uXXXX" and "\<c>".

    scan:identifier(IDENT^, LIST?, REST^)
           Unify IDENT with characters from LIST representing a valid identifier,
           which starts with a letter or "_" and is followed by digits, 
           letters or "_", unify REST with the remaining list. If no 
           identifier can be scanned, unify IDENT with the empty list.

    scan:from_set(CHARS?, STRING^, LIST?, REST^)
           Unify STRING with characters from LIST that can be found in the list CHARS
           and unify REST with the remaining list.

    scan:not_from_set(CHARS?, STRING^, LIST?, REST^)
           Unify STRING with characters from LIST that are not in the list CHARS
           and unify REST with the remaining list.

    scan:whitespace(LIST?, REST^)
           Skip whitespace characters in LIST and unify REST with the remaining list.


## 20. "match" module: Pattern matching

    This module allows matching sequences with patterns. Patterns may be 
    strings or lists and are matched according to the following rules: 
    Each element of the pattern is matched with zero or more elements of 
    the subject list

      0'*     matches zero or more elements
      0'?     matches any element
      0'[, ..., 0']       matches any element between the brackets
      0'[, 0'^, ..., 0']  matches an element that different from the sequence
                          given between the brackets
          Bracket-sequences may contain ranges of the form 
          <element1>, 0'-, <element2>

      {X}     matches the arbitrary value X

    Note that patterns and subjects need not necessarily be character
    lists.

    "all" matches a full subject sequence, "begins" only a prefix. "find"
    searches a sequence for a match, and "extract" will produce the
    matched part after searching a sequence.

    match:all(PATTERN?, SUBJECT?, RESULT^)
      Unifies RESULT with "true" or "false", depending on whether PATTERN
      matches the complete sequence SUBJECT.

    match:begins(PATTERN?, SUBJECT?, REST^)
      Unifies REST with the portion of SUBJECT that follows the part matching
      PATTERN, or "false", if SUBJECT does not begin with a matching sequence.

    match:find(PATTERN?, SUBJECT?, REST^)
    match:find(PATTERN?, SUBJECT?, PREFIX^, REST^)
    match:find(PATTERN?, SUBJECT?, PREFIX^, PREFIXTAIL?, REST^)
      Searches SUBJECT for a sequence matching PATTERN and unifies PREFIX
      with the part before the match (terminated by PREFIXTAIL, which defaults to
      the empty list) and REST with the remaining part. If no match can be
      found, REST is unified with the string "false".

    match:extract(PATTERN?, SUBJECT?, MATCH^)
    match:extract(PATTERN?, SUBJECT?, MATCH^, TAIL?)
    match:extract(PATTERN?, SUBJECT?, MATCH^, TAIL?, REST^)
    match:extract(PATTERN?, SUBJECT?, PREFIX^, PREFIXTAIL?, MATCH^, TAIL?, REST^)
      Like "find", but unifies MATCH with the found sequence, optionally
      terminated by TAIL (which defaults to the empty list). PREFIX and
      PREFIXTAIL designate the part before the match (if found).


## 21. "spec" module: Parse lists according to a specification

    The definitions in this module allow verifying or processing a list
    of values according to a specification or protocol. A specification
    is a data structure of the following form:

      SPEC = [RULE, ...]
      RULE = -GOAL
           | PATTERN-GOAL

      PATTERN = ATOMIC | [MATCH, ...]
      MATCH = [MATCH, ...]
            | {MATCH, ...}
            | '?'
            | VALUE

    Each rule consists of a pattern that should match the start of the
    subject list and a goal to be called when the rule fires.
    When a rule matches a list, then the associated GOAL is invoked using
    "apply/2", with all occurrences of '?' in the match part passed as
    additional arguments to the applied goal, in the order in which they
    textually appear in the pattern. A rule without a pattern part applies
    when no other rule matches. If no rule matches and such a "fallback"
    pattern is not given, an error will be signalled. The fallback goal
    is invoked using "apply(GOAL?, [LIST?, REST^])", where REST should be
    unified with the remainder of the subject list.
    If GOAL has no module qualifier, then the GOAL is assumed to be
    defined in the main ('') module.

    Use "match" to match a list to a specification or "process" to
    match it repeatedly until the end of the list is reached.

    spec:match(SPEC?, LIST?)
    spec:match(SPEC?, LIST?, REST^)
      Apply SPEC to LIST. If REST is given, then it is unified with the
      remaining part after the match. If REST is not given and the list
      holds more elements than can be matched, an error is signalled.

    spec:process(SPEC?, LIST?)
    spec:process(SPEC?, LIST?, IN?, OUT^)
      Repeatedly match SPEC with LIST until the end of the list is reached.
      IN and OUT (if given) are the start and end states, 
      respectively, and are threaded through each invocation of a goal,
      as extra parameters (appended to any arguments from '?' matches).


## 22. "path" module: Pathname destructuring

    This module provides basic operations to extract parts of filenames.
    All predicates accepts strings or character lists as input filenames
    and always produce character lists. 

    Use "basename", "tail", "dirname" and "extension" to extract parts of
    a filename. "canonical" normalizes a path as much as possible by 
    eliminating redundant "." and ".." parts. "join" creates a new filename
    by joining directory components.

    path:basename(NAME?, BASENAME^)
      Unify the last non-directory part of NAME without the file-
      extension with BASENAME.

    path:tail(NAME?, TAIL^)
      Unify the last non-directory part of NAME with TAIL.

    path:dirname(NAME?, DIR^)
      Unify the directory part of NAME with DIR.

    path:extension(NAME?, DIR^)
      Unify the part of NAME after the last "." (if any) with DIR. If 
      the file has no extension, then unify DIR with the empty list.

    path:canonical(NAME?, CNAME^)
    path:canonical(NAME?, ROOT?, CNAME^)
      Unifies CNAME with the normalized representation of NAME as an
      absolute path based on ROOT or the current working directory),
      with redundant "." and ".." parts removed.

    path:join(PARTS?, JOINED^)
      Join directory and filenames, separated by the 0'/ character and
      unify JOINED with the result. The parts may be strings or character
      lists.


## 23. "find" module: Find files and directories

    Collect lists of files in a given directory matching certain
    criteria. 

    Use "files" and "leaves" to collect files for which a given goal 
    succeeds, or "matching" to collect files that match a particular
    pattern. Consult the documentation for the "match" library module
    for further information regarding matching.

    find:files(GOAL?, FILES^)
    find:files(GOAL?, ROOT?, FILES^)
    find:files(GOAL?, ROOT?, FILES^, TAIL?)
    find:files(GOAL?, ROOT?, TEST?, FILES^, TAIL?)
      Traverses ROOT (or the current directory) recursively and unifies
      FILES with the list of all files that pass the test represented by
      GOAL. The file-list is terminated by TAIL or the empty list, if TAIL
      is not given. Sub-directories are ignored if the goal TEST answers
      "false" when passed the directory name.

      GOAL is invoked by calling "apply(GOAL, [FILENAME?, RESULT^])",
      TEST by calling "apply(TEST?, [DIRECTORY?, RESULT^])". Note that
      if GOAL or TEST have no module prefixes, then they default to
      the main ('') module. TEST may also be the empty list, meaning
      to descend into any subdirectory not detected by GOAL.
      
    find:matching(PATTERN?, FILES^)
    find:matching(PATTERN?, ROOT?, FILES^)
    find:matching(PATTERN?, ROOT?, FILES^, TAIL?)
      Unify FILES with a list of files and directories found in ROOT
      (or the current directory) that have names matching PATTERN,
      as with "match:all". Directories are traversed recursively.
      The list is terminated by TAIL or the empty list, if TAIL is not
      given.

    find:leaves(GOAL?, FILES^)
    find:leaves(GOAL?, ROOT?, FILES^)
    find:leaves(GOAL?, ROOT?, FILES^, TAIL?)
      Unifies FILES with all non-directory entities for which GOAL, when
      applied to the entity-name returns true. GOAL is invoked by calling
      "apply(GOAL, [FILENAME?, RESULT^])". If no module prefix is given
      in GOAL, it defaults to the main ('') module.


## 24. "ucs" module: unicode character classification and conversion

    Provides character classification (uppercase, lowercase, letter, digit,
    whitespace, alphanumeric), case conversion and folding. The operations
    in this module are all UNICODE aware (in the Basic Multlingual Plane).

    ucs:upper(CODE?, FLAG^)
    ucs:lower(CODE?, FLAG^)
    ucs:alpha(CODE?, FLAG^)
    ucs:space(CODE?, FLAG^)
    ucs:digit(CODE?, FLAG^)
      Unify FLAG with the string "true" or "false", depending on whether
      the character code is of the given class.

    ucs:tolower(CODE?, RESULT^)
    ucs:toupper(CODE?, RESULT^)
      Unify RESULT with the lower or uppercase conversion of the character
      code given by CODE.

    ucs:foldcase(CODE?, RESULT^)
    ucs:foldcase(CODE?, RESULT^, TAIL?)
      Unify RESULT with the list of character codes resulting from the
      case-folding of CODE, optionally terminated by TAIL.


## 25. PCN syntax and primitive operations

    PCN-specific operators and primitives. The "pcn" module is not 
    explicitly used and serves here only to group PCN-specific syntax.

    //
      [PCN] Single-line comment.

    pcn:main(ARGC?, ARGV?, EXIT_CODE^)
    pcn:main()
      The main entry point. ARGC contains the number of arguments in the
      list ARGV, including the program name, as a listg of strings. 
      EXIT_CODE should be bound to the exit status of the program. If 
      the execution of "main" completes and EXIT_CODE is not bound, the 
      program terminates with exit status 0.

      A program may optionally just define a "main" procedure with zero
      arguments, if access to the command-line and exit code is not
      needed.

    X + Y
    X - Y
    X * Y
    X / Y
    X % Y
    X & Y
    X | Y
    X ^ Y
    X >> Y
    X << Y
    ~ X
    - X
      [PCN] (Expression) Arithmetic and binary logic operators in 
      expressions, with precedence as follows:

          -  ~                (highest)
          *  /  %
          +  -
          <<  >>
          &  |  ^             (lowest)

      For arithmetic operators, if both arguments of an operation are 
      integer, then the result will be an integer as well. Otherwise the 
      result will be a floating-point number.

    sin(X)
    cos(X)
    tan(X)
    atan(X)
    sqrt(X)
    log(X)
    exp(X)
      [PCN] (Expression) Trigonometric and transcendental functions.

    truncate(X)
    ceiling(X)
    floor(X)
    round(X)
      [PCN] (Expression) Rounding operations.

    max(X, Y)
    min(X, Y)
      [PCN] (Expression) Return the maximum or minimum of two numbers.

    real(X)
    integer(X)
      [PCN] (Expression) Convert integer to real or vice versa.

    real_fractional_part(X)
    real_integer_part(X)
      [PCN] (Expression) Extract the fractional or integer part of a real 
      number.

    rnd()
    rnd(X)
      [PCN] (Expression) Return a uniformly distributed real number between 0
      and 1, or a random integer between 0 and X-1.

    IDENTIFIER over EXPRESSION .. EXPRESSION :: STATEMENT
    IDENTIFIER over TERM :: STATEMENT
      [PCN] (Statement) Executes STATEMENT with IDENTIFIER bound to the values 
      in the range EXPRESSION .. EXPRESSION (inclusive) or in the list
      TERM. Depending on the enclosing composition the iteration takes 
      place sequentially or in parallel. 
      In a sequential composition IDENTIFIER may be a mutable variable. 
      If IDENTIFIER is a definitional variable, then it is rebound during the
      execution of STATEMENT, but remains unbound in subsequent statements.

    MODULE:IDENTIFIER(...)
      [PCN] Designates a call of the external procedure IDENTIFIER defined 
      in MODULE, the procedure must be exported.

    {; STATEMENT, ...}
    {|| STATEMENT, ...}
    {? CHOICE, ...}
      [PCN] (Statement) Composition blocks. STATEMENTS are executed 
      sequentially in a "{; ...}" block, in parallel in a "{|| ...}" 
      block. "{? ...}" contains a list of choices and executes the 
      body of the first choice of which all guard-expressions succeed.

    GUARD, ... -> STATEMENT
      [PCN] Defines a choice that executes STATEMENT when all guards succeed.

    return(TERM)
      [PCN] (Statement) Sets the result of the function that contains this 
      statement. Execution continues normally, this operation does not in 
      any way change the flow of control.

    data(TERM)
      [PCN] (Guard) Suspends and succeeds once TERM is fully ground, i.e. 
      contains no unbound definitional variables.

    number(TERM)
      [PCN] (Guard) Succeeds if TERM is a integer or floating-point number.

    atomic(TERM)
      [PCN] (Guard) Succeeds if TERM is a number or string.

    string(TERM)
      [PCN] (Guard) Succeeds if TERM is a string.

    tuple(TERM)
      [PCN] (Guard) Succeeds if TERM is a tuple.

    list(TERM)
      [PCN] (Guard) Succeeds if TERM is a nonempty list.

    default -> STATEMENT
      [PCN] (Guard) Always succeeds after all other choices failed.
      Should other choices in the same composition suspend, then this
      guard also suspends until all other choices still fail after
      resumption. 

    CALL @ TERM
      [PCN] (Statement/Expression) Invokes the procedure or function call 
      in the  peer thread defined by TERM. TERM may be one of the strings 
      "fwd", "bwd", "north", "east", "south", "west" or an integer designating 
      a thread number from 1 to the number of threads.

    IDENTIFIER = TERM
    IDENTIFIER . IDENTIFIER = EXPRESSION
    IDENTIFIER [ EXPRESSION ] = EXPRESSION
      [PCN] (Statement) Binds a definitional variable and resumes any 
      code currently suspended and waiting for this variable. If IDENTIFIER
      is already bound and not identical to TERM, then an error is signalled.
      If IDENTIFIER refers to a tuple or a list, then the referenced element
      must contain an unassigned variable.

    IDENTIFIER1.IDENTIFIER2
      [PCN] ID1.ID1 = EXPR is an abbreviation for

          pcn:put_attr(ID1, "ID2", EXPR)

      The expression ID1.ID2 is shorthand for

          pcn:get_attr(ID, "ID2", RESULT)

      where RESULT represents a variable used as the result of the expression.

    IDENTIFIER := EXPRESION
    IDENTIFIER [ EXPRESSION ] := EXPRESSION
      [PCN] (Statement) Assigns the value of the right-hand side expression to 
      the mutable variable or array element specified on the left-hand side.

    length(TERM)
      [PCN] (Expression) Returns the length of the array, string, list or tuple 
      designated by TERM. If TERM is any other type, 1 is returned.

    `IDENTIFIER`
    `IDENTIFIER`(TERM, ...)
    IDENTIFIER:`IDENTIFIER`(TERM, ...)
    `IDENTIFIER`:`IDENTIFIER`(TERM, ...)
      [PCN] (Statement/Expression) Meta calls. in the first form, IDENTIFIER 
      should hold a tuple with name and arguments of a call, or a tuple 
      of the form

          {":", MODULE, CALL}

      where MODULE is a string naming an existing module and CALL is
      itself a tuple.
      In the second form the identfier should contain a string naming
      the procedure. In the third form, the call is qualified in a 
      given or dynamically computed module.

    IDENTIFIER ?= PATTERN
      [PCN] (Guard) Matches a pattern in a choice. Succeeds if IDENTIFIER
      contains a structure that matches PATTERN. If unbound
      definitional variables are embedded in PATTERN, then the match
      will suspend until the variables have been bound.

    TERM == TERM
    TERM != TERM
      [PCN] (Guard) Equality guards. If one of the terms is an expression, the 
      comparison is numerical, otherwise the it is a structural comparison. 
      Any unbound definitional variables in the terms/expressions will 
      suspend execution until bound.

    EXPRESSION > EXPRESSION
    EXPRESSION < EXPRESSION
    EXPRESSION >= EXPRESSION
    EXPRESSION <= EXPRESSION
      [PCN] (Guard) Numerical comparison guards. Unbound definitional variables 
      in one of the expressions will suspend execution until bound.

    int(TERM)
    byte(TERM)
    char(TERM)
    double(TERM)
    int DECLARATOR, ...;
    char DECLARATOR, ...;
    byte DECLARATOR, ...;
    double DECLARATOR, ...;
      [PCN] (Guard/Declaration) These are both used as guards, testing the types 
      of its arguments and as mutable variable declarations. In guards, 
      "int", "byte" and "char" all simply test the argument for being 
      an integer.

      DECLARATOR may be one of:

          IDENTIFIER                  defines a mutable variable
          IDENTIFIER[INTEGER]         allocates a numeric array
          IDENTIFIER[IDENTIFIER]      allocates a numeric array with dynamic length[*]
          IDENTIFIER[]                declares an argument to be a numeric 
                                        array

      DECLARATOR may refer to procedure arguments and marks such arguments
      as being mutable.

      [*] IDENTIFIER must refer to a mutable argument variable

    if(GUARD, ...) STATEMENT
    if(GUARD, ...) STATEMENT else STATEMENT
      [PCN] (Statement) A convenience syntax for 

          {? GUARD, ... -> STATEMENT }

      and

          {? GUARD, ... -> STATEMENT, default -> STATEMENT }

    let GUARD, ... :: STATEMENT
      [PCN] (Statement) A convenience syntax for 

          {? GUARD, ... -> STATEMENT, default -> <fail with error> }

      This form may be used instead of a composition or choice as a procedure
      body, as in

          main(_, argv, _) 
          let argv ?= [name|_] :: fmt:format("this program is called as: ~s\n", [name])

    function
    function PROCEDURE-DEFINITION
      [PCN] (Program modifier) Marks the following procedure as being a
      function. The procedure has a hidden definition variable has 
      argument and may use the "return" statement to define the result
      value.

    true
    false
      [PCN] Preprocessor macros standing for the strings "true" and "false",
      respectively.

    nodes()
      [PCN] [Expression) Function that returns the number of threads.
      Equivalent to "threads/1".

    pcn:put_attr(VALUE, ATTR, X)
      [PCN] Assigns attribute, depending on the type of VALUE:

      Port:   send(VALUE, put_attr(ATTR, X))
      Tuple:  apply(VALUE, [put_attr(ATTR, X)])

    pcn:get_attr(VALUE, ATTR, RESULT)
      [PCN] Retrieves attribute, depending on the type of VALUE:

      Port:   send(VALUE, get_attr(ATTR, RESULT))
      Map:    map:lookup(ATTR, VALUE, RESULT)
      Tuple:  apply(VALUE, [get_attr(ATTR, RESULT)])


## 26. Security-relevant stuff

    sec:drop_privileges(USERNAME?)
    sec:drop_privileges(USERNAME?, DONE^)
          Assumes process is root and drops privileges to those of the user given by the
          string, character list or integer USERNAME and unifies DONE with the empty 
          list when the operation is complete. Signals an error when the operation 
          failed for some reason or USERNAME does not identify a known user.

    sec:unveil(PATH?, PERMISSIONS?)
    sec:unveil(PATH?, PERMISSIONS?, DONE^)
          Performs the unveil(2) system call with the strings or character lists
          in PATH and PERMISSIONS as arguments and unifies DONE with the empty list
          when the operation is complete. Signals an error when the operation failed 
          for some reason.
          On platforms other than OpenBSD this operation does nothing and binds
          DONE. PATH + PERMISSIONS may be empty lists to disable future calls
          to unveil(2).

    sec:pledge(PROMISES?, XPROMISES?)
    sec:pledge(PROMISES?, XPROMISES?, DONE^)
          Performs the pledge(2) system call with the strings or character lists
          in PROMISES and XPROMISES as arguments and unifies DONE to the 
          empty list when the operation is complete. Signals an error when the 
          operation failed for some reason.
          On platforms other than OpenBSD this operation does nothing and binds
          DONE. (X)PROMISES may be the empty list to indicate keeping the current
          settings.


## 27. "color" module: Access to named colors

    This module provides RGB values for all named X11 colors as normally found
    in "rgb.txt".

    color:get(NAME?, RGB^)
           Unifies RGB with a 3-tuple holding the red/green/blue components
           of the color named NAME. If no color with that name exists, RGB
           will be unified with "false".

    color:get_indexed(INDEX?, NAME^)
           Retrieve color by numeric index from 0-N, or the empty list of
           INDEX is out of range.

    color:all(COLORS^)
           Unifies COLORS with a list of all color known names.


## 28. "sdl" module: basic SDL2 graphics and sound interface

    This module provides a stream-based interface to libSDL2 for displaying
    graphics, render fonts and perform basic audio operations.

    Use "sdl:init/{1,2,3}" to initialize the SDL subsystem and obtain a
    port which accepts messages to show graphics and perform other tasks.
    Messages interpreted in the SDL stream are any of the following:

      listen(EVENT?, PORT^)     (EVENT may also be a list)
      ignore(EVENT?)
      close
      draw_image(X?, Y?, IMAGE?)
      draw_image(X?, Y?, IMAGE?, FLIP?)
      draw_image(X?, Y?, IMAGE?, ANGLE?, CX?, CY?)
      draw_image(X?, Y?, IMAGE?, ANGLE?, CX?, CY?, FLIP?)
      draw_text(X?, Y?, TEXT?)
      draw_text(X?, Y?, TEXT?, FONT?)
      draw_text(X?, Y?, TEXT?, FONT?, WIDTH?)
      redraw
      clear
      clear(R?, G?, B?)
      draw_line(X1?, Y1?, X2?, Y2?)
      draw_polygon(X?, Y?, SIDES?, RADIUS?, ANGLE?)
      draw_box(X?, Y?, X2?, Y2?)
      fill_box(X?, Y?, X2?, Y2?)
      clip(off)
      clip(X1?, Y1?, X2?, Y2?)
      color(R?, G?, B?)
      volume(CHANNEL?, VOL?)  (Channel: 0-7, Volume: 0-128)
      play(CHAN?, SAMPLE?)
      play(CHAN?, SAMPLE?, LOOPS?)
      play(CHAN?, SAMPLE?, LOOPS?, TICKS?)
      fade_in(CHAN?, SAMPLE?, MS?)
      fade_in(CHAN?, SAMPLE?, MS?, TICKS?)
      fade_in(CHAN?, SAMPLE?, LOOPS?, MS?, TICKS?)
      fade_out(CHAN?, MS?)
      pause(CHAN?)
      resume(CHAN?)
      set_mouse(FLAG?)
      mouse_cursor(CURSOR?)

      FONT may be "[]" to specify the builtin 8*16 bitmap font.

    Event types reported by the "listen" message:

      Type:                   Event sent to port:

      mouse                   mouse(X, Y)                 (mouse movement)
      keyup                   keyup(K)
      keydown                 keydown(K)
      buttonup(B)             buttonup(B, X, Y)           (mouse button press/release)
      buttondown(B)           buttondown(B, X, Y)
      joybuttonup             joybuttonup(B, JOYSTICK)    (joystick)
      joybuttondown           joybuttondown(B, JOYSTICK)
      joymotion               joymotion(JOYSTICK, AXIS, VALUE)
      controllerbuttonup      controllerbuttonup(B, JOYSTICK) (game controller)
      controllerbuttondown    controllerbuttondown(B, JOYSTICK)
      controllermotion        controllermotion(JOYSTICK, AXIS, VALUE)
      resize                  resize(W, H)                (window resizing)
      quit                    quit                        (window close)
      expose                  expose                      (window exposed/shown)
      textinput               textinput(CODE)             (textual input from keyboard)

      "K": keycode (ASCII character or control key
      "JOYSTICK": joystick indicator
      "B": button number
      "CODE": UNICODE code point

    With the exception of "sdl:wake_up/1", all predicates in this module _must_ 
    be called from the main thread.

    sdl:init(STREAM^)
    sdl:init(PARAMS?, STREAM^)
    sdl:init(PARAMS?, STREAM^, DONE^)
          Creates a resizable window of given size and returns a stream 
          for communicating with SDL. PARAMS may be a list of the following 
          elements:

          size(WIDTH?, HEIGHT?)
          fixed
          fullscreen
          language(LANG?)
          handle(PORT^)       expects events are read via "next_event/1" and
                              sent to PORT

          Unless "handle(PORT)" is passed, an event loop will be started
          and run in the background, processing events as they are delivered
          from input devices.

          DONE is assigned the empty list once initialization is complete.

    sdl:next_event(EVENT^)
          Unify EVENT with the next available SDL event or the empty list if
          no event is pending.

    sdl:image_size(IMAGE?, W^, H^)
          Unify W and H with the width and height of IMAGE.

    sdl:text_size(TEXT?, W^, H^)
    sdl:text_size(TEXT?, FONT?, W^, H^)
          Unify W and H with the width and height of TEXT (which can be a string
          or a character list) rendered using FONT. FONT defaults to the built-in
          bitmap font.

    sdl:load_image(NAME?, IMAGE^)
    sdl:load_sample(NAME?, SAMPLE^)
    sdl:load_font(NAME?, SIZE?, FONT^)
          Load external asset, unifies result with the empty list if loading fails.

    sdl:load_image_from_bundle(PTR?, LEN?, IMAGE^)
    sdl:load_sample_from_bundle(PTR?, LEN?, SAMPLE^)
    sdl:load_font_from_bundle(PTR?, LEN?, SIZE?, FONT^)
          Load external asset from memory, unifies result with the empty list if 
          loading fails.

    sdl:release_image(IMAGE?, DONE^)
    sdl:release_sample(SAMPLE?, DONE^)
    sdl:release_font(FONT?, DONE^)
          Release loaded resource and unify DONE with the empty list.

    sdl:window_size(W^, H^)
          Returns the current window size.

    sdl:resize_window(W?, H?, DONE^)
          Resize window to given size.

    sdl:wake_up(DONE^)
          If the SDL event loop is currently blocked by waiting for events, then 
          invoking this operation from another thread will trigger re-entry of the 
          loop and give other processes in the thread the chance to run. 

    sdl:window_title(STRING?, DONE^)
          Set the title of the window to STRING, which should be a string or a 
          character list. DONE is unified with the empty list when the operation 
          is completed.

    sdl:font_metrics(FONT?, METRICS^)
          Unifies metrics with a tuple of the form {HEIGHT, ASCENT, DESCENT, LINESKIP}
          for the given font.

    sdl:mouse_position(X^, Y^)
          Unifies X and Y with the current mouse position.

    sdl:mouse_buttons(B^)
          Unifies B with a bit-mask where bits #0 to #2 are set when the
          mouse buttons 1 to 3 are pressed.

    sdl:shift_state(STATE^) 
    sdl:shift_state(EVENT^, STATE1?, STATE2^)
          The first form initializes a new, unset shift state object. The
          second form updates the current shift state according to the
          passed event object. If the event does not designate a keyboard
          event, the state is unchanged.

    sdl:get_shift_state(SHIFT?, STATE?, FLAG^)
          Unifies FLAG with the string "true" or "false", if the shift
          state STATE represenents the keys given in SHIFT, which may
          be one of the strings "shift", "control" or "alt", or a binary
          operator tuple like "control+shift".

    sdl:message_box(MESSAGE?, ITEMS?, RESPONSE^)
    sdl:message_box(TITLE?, MESSAGE?, ITEMS?, RESPONSE^)
          Shows a message box. Title may begin with the characters "!" or "?"
          to mark the message as a warning or an error. ITEMS should hold a list
          of strings or character lists with button texts. Each item may be 
          preceded by the characters "*" or "^" to mark a button that should
          be selected when RETURN or ESCAPE is pressed, respectively.
          After the box is confirmed, RESPONSE will be assigned the index
          of the selected button (starting from 1). During display of the
          message box execution in the current thread is suspended.


## 29. "ezd" module: Structured graphics

    A structured graphics package inspired by the "EZDraw" by Joel Bartlett,
    based on libSDL2. Named graphical objects can be defined, consisting of 
    one or more graphical primitives, where further operations allow grouping
    objects in "drawings" (layers). An object can be re-defined at any time
    and redrawing the canvas happens automatically.

    Currently the graphical primitives supported are lines, recangles, filled
    rectangles, polygons and text. Depending on whether a number of extension
    libraries for libSDL2 are available, TrueType font rendering and loading
    of extended image types is provided.

    As in the "sdl" module, all interfacing to the structured graphics system
    is done via a port that receives messages, as obtained by the 
    "ezd:init/{1,2,3}" predicate.

    ezd:init(STREAM^)
    ezd:init(SDLINIT?, STREAM^)
    ezd:init(SDLINIT?, STREAM^, DONE^)
          Initialize SDL and EZD and accept commands in STREAM.
          DONE is assigned the empty list once initialization is complete.

          Note that invoking any of the "sdl:..." operations will only
          work once SDL is properly initialized.

         Commands:

         drawing(NAME?)
          Select or create drawing. Initially a default drawing named "ezd"
          is active.

         object(NAME?, PRIMITIVES?)
          Create or recreate object with given name and containing a list of
          graphics primmitives, which may be one or more of the following:

          line(X1, Y1, X2, Y2)
          line(X1, Y1, X2, Y2, COLOR)
          rectangle(X1, Y1, X2, Y2)
          rectangle(X1, Y1, X2, Y2, COLOR)
          polygon(X, Y, SIDES, RADIUS, ANGLE)
          polygon(X, Y, SIDES, RADIUS, ANGLE, COLOR)
          box(X1, Y1, X2, Y2)
          box(X1, Y1, X2, Y2, COLOR)
          text(X, Y, TEXT)
          text(X, Y, TEXT, PROPERTIES)            [font(FONT), color(COLOR), 
                                                   anchor(ANCHOR), width(W)]
          image(X, Y, IMAGE)
          image(X, Y, IMAGE, PROPERTIES)          [angle(A), anchor(ANCHOR), flip(FLIP)]

              COLOR is a 3-tuple of 8 bit unsigned integers {R, G, B},
              a string naming a standard X11 color, or the string "clear", 
              the default is "black".
              IMAGE and FONT are entities loaded via "sdl:load_image/2" or
              "sdl:load_font/3".
              ANGLE is the rotation in degrees
              FLIP is "none" or a string holding "h" and/or "v".
              ANCHOR specifies point of origin ("center", "n", nw", ...), 
              "center" is the default.

          If PRIMITIVES is empty, delete any object with this name. If it is not
          empty, the object retains any attributes and its drawing order. Primitives
          are drawn in the order in which they are given in the PRIMITIVES list.

         raise(NAME?)
          Raise object to top.

         sink(NAME?)
          Sink object to bottom.

         clear
          Clear current drawing, including all event handlers.

         background(COLOR)
          Set background color.

         draw_now
          Redraw everything without waiting for next idle cycle.
          Note that EZD redraws when the thread is idle and any changes
          where made. If "ezd:init" was called with a polling event handler
          and other processes (e.g. timers) are running, then it may be 
          necessary to trigger redraw explicitly using this message.

         delete_view(NAME?)
          Delete drawing.

         overlay(NAME?)
          Raise drawing with name to top.

         underlay(NAME?)
          Sink drawing with name to bottom.

         origin(NAME?, X?, Y?)
          Set origin of named drawing to X/Y.

         scale(NAME?, X?, Y?)
          Set scale of named drawing to X/Y.

         clip(NAME?, CLIP?)
          Define clipping region for drawing with given name. CLIP should be
          a tuple of the form {X1, Y1, X2, Y2} in screen coordinates or the string
          "off".

         when(NAME?, EVENT?, ACTION?)
          Define event handler for named object in the current drawing. ACTION is 
          invoked with "apply/2", with a string or tuple representing the event when 
          the event occurs. The first matching handler in all drawings is invoked,
          starting at the topmost drawing, for which the event is defined and which
          is below the mouse cursor in case of mouse and button events.
          Once an object handles the event, the event is not further propagated.

          EVENT may be one of the following:

          buttonup(BUTTON?)               BUTTON: button number
          buttondown(BUTTON?)
          joybuttonup
          joybuttondown
          controllerbuttonup
          controllerbuttondown
          mouse
          joymotion
          controllermotion
          quit
          resize
          keyup
          keydown
          textinput

          Events of type mouse and button trigger an action only if they occur 
          with the mouse being inside any of the primitives of the object. 
          NAME may be "*" to match any object in the drawing.

          Specifying an event with action "[]" disables the handler.

         get(NAME?, ATTR?, VAL^)
          Retrieve attribute ATTR from object NAME and unify with VAL. If the 
          attribute is not defined, unify with the empty list. The object must exist.

         put(NAME?, ATTR?, VAL?)
          Set attribute ATTR of object NAME to VAL, replace any existing atribute.
          The object must exist. Redefining an object will retain any defined
          attributes.

         set_mouse(FLAG?)
          Enable or disable mouse cursor.

         mouse_cursor(NAME?)
          Set mouse cursor to one of a set of predefined system cursors,
          NAME should be one of the following strings:

          default, arrow, ibeam, wait, crosshair, waitarrow, size_nwse,
          size_nesw, size_we, size_ns, size_all, no, hand

         sdl(MESSAGE?)
          Pass MESSAGE on unchanged to the SDL command stream.


## 30. "ezbb" module: EZD building blocks

    This module provides simple UI components built using EZD objects.
    To use the facilities in this module, your application should adhere
    to a structure demonstrated in this example:

      -initialization(main).                                      [FGHC]

      main :- 
          ezd:init(EZD),              % initialize EZD
          open_port(Events, S),       % create event port
          ezbb:std_events(Events, EZD, EZD2), % prepare some standard event handling
          build(Events, EZD2, EZD3),  % create static objects with EZD (tail is EZD3)
          loop(S, Events, EZD3).

      build(Events, EZD1, EZD) :-
          % create dynamic objects with ezd1 (tail is ezd)
          % pass "events" to event-handlers established with "when"
          % which will forward messages to the event stream "s", 
          % handled in "loop"
          ...

      loop([buttondown(1, X, Y)|S2], Events, EZD) :- % event loop
          % process button press...
          % invoke "build" to re-create dynamic
          % objects with EZD (tail is EZD2)
          build(Events, EZD, EZD2),
          loop(S2, Events, EZD3).
      loop([...|S2], Events, EZD) :-
          % handle further events 
          ...
      loop([_|S2], Events, EZD) :-
          otherwise |
          loop(S2, Events, EZD).  % ignore other events

      main() {||                                                  [PCN]
          ezd:init(ezd),
          open_port(events, s),
          ezbb:std_events(events, ezd, ezd2),
          build(events, ezd2, ezd3),
          loop(s, events, ezd3)
      }

      build(events, ezd1, ezd) {||
          ...
      }

      loop(s, events, ezd) {?
          s ?= [{"buttondown", 1, x, y}|s2] -> {  
              // process button press...
              build(events, ezd, ezd2),
              loop(s2, events, ezd3)
          },
          ...
          default ->
              let s ?= [_|s2] -> loop(s2, events, ezd)
      }

    In the following descriptions POSITION and SIZE refer to two-element tuples
    holding the X and Y position and the width and height of the created object,
    respectively. All object constructors accept an EZD input and output stream
    pair that is used to communicate with the EZD subsystem. If a PORT is specified
    as argument, then it should refer to the event-handling port created above.
    Events triggered by objects created by the operations in this module will
    be forwarded to this port and can be intercepted by the user or passed
    on to component-specific handlers.

    ezbb:std_events(PORT?, EZD1^, EZD?)
      Handles "quit" event + termination on pressing the ESCAPE key.
      Key events are sent as {"textinput", KEY} tuples.

    ezbb:label(ID?, POSITION?, TEXT?, EZD1^, EZD?)
    ezbb:label(ID?, POSITION?, TEXT?, FONT?, EZD1^, EZD?)
      A passive text label with the text anchored at the left center border of
      POSITION.

    ezbb:entry(ID?, POSITION?, SIZE?, TEXT?, FOCUS?, PORT?, EZD1^, EZD)
    ezbb:entry(ID?, POSITION?, SIZE?, TEXT?, FONT?, FOCUS?, PORT?, EZD1^, EZD)
    ezbb:entry(ID?, POSITION?, SIZE?, TEXT?, FONT?, STATUS?, FOCUS?, PORT?, EZD1^, EZD)
      An active entry field containing TEXT. FOCUS determines whether field is ready 
      for input: if equal to ID then this object is marked as receiving input.
      Pressing button 1 sends the tuple {"focus", id} as a message to PORT.
      STATUS may be one of the strings "disabled", "normal" or "invalid".

    ezbb:entry_update(OLDTEXT?, CHARCODE?, NEWTEXT^)
    ezbb:entry_update(OLDTEXT?, CHARCODE?, VALIDATOR?, NEWTEXT^)
      Update entry field text with the key represented by CHARCODE, handling BACKSPACE.
      If VALIDATOR is given, it should be an object suitable for "apply/2" and is
      called as "apply(VALIDATOR, [UPDATED?, OLDTEXT?, NEWTEXT^])". The validator should 
      assign the new value to NEWTEXT, if UPDATED is valid, or OLDTEXT otherwise.
      CHARCODE may also be a "keydown" or "textinput" event tuple.

    ezbb:button(ID?, POSITION?, SIZE?, TEXT?, CLICK?, EZD1^, EZD)
    ezbb:button(ID?, POSITION?, SIZE?, TEXT?, FONT?, CLICK?, EZD1^, EZD)
    ezbb:button(ID?, POSITION?, SIZE?, TEXT?, FONT?, STATUS?, CLICK?, EZD1^, EZD)
      A push button. The callable object CLICK is invoked using "apply/2" as
      "apply(CLICK, [EVENT])" when B1 is pressed over the button.
      STATUS may be one of the strings "disabled" or "normal".

    ezbb:menu(ID?, POSITION?, SIZE?, LIST?, SEL?, PORT?, EZD1^, EZD)
    ezbb:menu(ID?, POSITION?, SIZE?, LIST?, FONT?, SEL?, PORT?, EZD1^, EZD)
      Creates a menu of items given by LIST, where each element should be a string
      or character list. SEL holds the index of the currently selected item
      (starting from 1). When a menu item is clicked, a tuple of the form
      {"menu_select", ID, INFO} is sent to PORT.

    ezbb:menu_select(INFO?, STREAM^, PORT?, EZD^, K)
      Take over event-handling for managing a menu selection. INFO should be the
      value received via the "menu_select" message. STREAM is the event stream
      for PORT that usually is processed by application logic ("loop" in the
      example in the instroduction). The normal event handling should be suspended
      until K is invoked via "apply/2" as "apply(K, [SEL?, STREAM^, EZD^])",
      K can then continue processing events from STREAM and communicate with ezd
      using EZD. SEL will hold the index of the selected item.

    ezbb:gauge(ID?, POSITION?, SIZE?, VALUE?, EZD1^, EZD?)
    ezbb:gauge(ID?, POSITION?, SIZE?, VALUE?, COLOR?, EZD1^, EZD?)
      Draws a progress bar. VALUE is a floating-point value from 0 to 1 indicating
      the "fullness" of the bar.

    ezbb:slider(ID?, POSITION?, SIZE?, VALUE?, PORT?, EZD1^, EZD?)
      A slider object that can be adjusted via mouse buttons. When the value changes
      a tuple of the form {"adjust", ID, VALUE} is sent as a message to PORT, where
      VALUE indicates the new value.

    ezbb:delete_slider(ID?, EZD1^, EZD?) 
      Delete a slider object and associated helper objects.

    ezbb:listbox(ID?, POSITION?, SIZE?, LIST?, SEL?, PORT?, COUNT^, EZD1^, EZD?)
    ezbb:listbox(ID?, POSITION?, SIZE?, LIST?, FONT?, SEL?, PORT?, COUNT^, EZD1^, EZD?)
      A "listbox" showing a list of text elements given by LIST. SEL specifies the
      index of the currently selected entry, starting from 1. COUNT will hold the
      number of items shown. When an item is clicked, the tuple 
      {"list_select", INDEX, ITEM} is sent as a message to PORT, where INDEX is the
      index and ITEM the text of the selected item.

    ezbb:delete_listbox(ID?, COUNT?, EZD1^, EZD^)
      Delete a listbox and associated objects.

    ezbb:scrollbar(ID?, POSITION?, SIZE?, ORIENTATION?, RANGE?, PORT?, EZD1^, EZD)
    ezbb:scrollbar(ID?, POSITION?, SIZE?, ORIENTATION?, RANGE?, FG?, BG?, PORT?, EZD1^, EZD)
      A horizontal or vertical scroll bar. ORIENTATION should be one of the strings
      "h" or "v". RANGE is a a 2-element tuple holding the start and end value of the
      "thumb" as floating-point values of 0 to 1. When clicked, a tuple of the form
      {"scroll", ID, INFO} is sent to PORT as a message, which should be passed
      on to "ezbb:scrollbar_move/6".

    ezbb:scrollbar_move(ID?, INFO?, RNG^, EZD1^, EZD)
      Adjusts the scroll bar range from INFO, as received previously via the "scroll"
      message. RNG will be assigned a new tuple holding the adjusted range.

    ezbb:delete_scrollbar(ID?, EZD1^, EZD)
      Deletes a scrollbar and the associated objects.


## 31. Formal definition of PCN syntax


    * Block comments are of the form "/* ... */" any may not be nested.

    * Line comments are of the form "// ..."

    * The following keywords are reserved and may not be used as variables
      or procedure names:

        char, int, double, default, length, over, return, 
        foreign_call, byte, idle, if, else, function, let

    * Syntax in BNF:

    <program> = <toplevel> ...

    <toplevel> = <procedure> 
               | <declaration>

    <declaration> = "-" <identifier> "(" <element> "," ... ")"

    <procedure> = [<modifiers>] <identifier> "(" <parameter> "," ... ")" 
                  [<mutable-declaration> ...] <body>

    <mutable-declaration> = <type> <identifier> ["[" <size> "]"] "," ... ";"

    <type> = "int" | "byte" | "double" | "char"

    <size> = <integer> | <identifier>

    <modifiers> = <modifier> "," ...

    <modifier> = "function"

    <body> = <composition> 
           | <choice>
           | "let" <guard> "," ... "->" <statement>

    <composition> = "{" [";"] <statement> <delimiter> ... "}"
                  | "{" "||" <statement> <delimiter> ... "}"
                  | "{" "?" <choice> <delimiter> ... "}"

    <delimiter> = ";" | ","

    <statement> = <identifier> "=" <term>
                | <identifier> ["[" <expression> "]"] ":=" <expression>
                | <identifier> "." <identifier> "=" <expression>
                | <identifier> "=" <expression>
                | <composition>
                | "if" "(" <guard> "," ... ")" <statement> ["else" <statement>]
                | "let" <guard> "," ... "->" <statement>
                | "foreign_call" "(" <element> ")"
                | "return" "(" <term> ")"
                | <call> ["(" <term> "," ... ")"]
                | <call> ":" <call> "(" <term> "," ... ")"
                | <quantification>

    <quantification> = <identifier> "over" <expression> ".." <expression> "::"  
                       <statement>
                     | <identifier> "over" <term> "::" <statement>

    <call> = <identifier>
           | "`" <identifier> "`"

    <term> = "[" <element> "," ... ["|" <element>] "]"
           | "{" <element> "," ... "}"
           | <identifier>
           | <number>
           | <string>
           | <expression>

    <element> = "[" <element> "," ... ["|" <element>] "]"
              | "{" <element> "," ... "}"
              | <identifier> ["(" <element> "," ... ")"]
              | <number>
              | <string>

    <string> = "\"" <character> ... "\""

    <number> = "'" <character "'"
             | <integer>
             | <float>

    <expression> = <expr2> "&" <expression>
                 | <expr2> "|" <expression>
                 | <expr2> "^" <expression>
                 | <expr2>

    <expr2> = <expr3> ">>" <expr3>
            | <expr3> "<<" <expr3>
            | <expr3>

    <expr3> = <expr4> "+" <expr3>
            | <expr4> "-" <expr3>
            | <expr4>

    <expr4> = <expr5> "*" <expr4>
            | <expr5> "/" <expr4>
            | <expr5> "%" <expr4>
            | <expr5>

    <expr5> = "-" <expr5>
            | "~" <expr5>
            | <identifier> "[" <expression> "]"
            | <identifier> "." <identifier>
            | <identifier>
            | <number>
            | <call> "(" <term> "," ... ")"
            | <call> ":" <call> "(" <term> "," ... ")"
            | "``" <identifier> [":" <identifier>] ["(" <term>, ... ")"] "``"*
            | "`" "(" <identifier> "," ... ")" "->" <statement>*

    <choice> = <guard> "," ... "->" <statement>

    <guard> = <identifier> "?=" <element>
            | <test> "(" <term> ")"
            | <term>* "==" <term>*
            | <term>* "!=" <term>*
            | <expression>* ">" <expression>*
            | <expression>* "<" <expression>*
            | <expression>* ">=" <expression>*
            | <expression>* "<=" <expression>*
            | "default"

      *: expression may use arithmetic functions but no user function calls