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 _(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 ".c", where "" 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 "/" 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 "(, ...)", where "" 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 "{, , ...}", "{':', , }" or "{':', , {, , ...}}". 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 "{'-', , }" or "{, }". 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 "{'-', , }". 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 {"", , ...} Calls to specific modules should be encoded as tuples like this: {":", "", ""} {":", "", {"", , ...}} 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 "\". 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 , 0'-, {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 -> } 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: = ... = | = "-" "(" "," ... ")" = [] "(" "," ... ")" [ ...] = ["[" "]"] "," ... ";" = "int" | "byte" | "double" | "char" = | = "," ... = "function" = | | "let" "," ... "->" = "{" [";"] ... "}" | "{" "||" ... "}" | "{" "?" ... "}" = ";" | "," = "=" | ["[" "]"] ":=" | "." "=" | "=" | | "if" "(" "," ... ")" ["else" ] | "let" "," ... "->" | "foreign_call" "(" ")" | "return" "(" ")" | ["(" "," ... ")"] | ":" "(" "," ... ")" | = "over" ".." "::" | "over" "::" = | "`" "`" = "[" "," ... ["|" ] "]" | "{" "," ... "}" | | | | = "[" "," ... ["|" ] "]" | "{" "," ... "}" | ["(" "," ... ")"] | | = "\"" ... "\"" = "'" | = "&" | "|" | "^" | = ">>" | "<<" | = "+" | "-" | = "*" | "/" | "%" | = "-" | "~" | "[" "]" | "." | | | "(" "," ... ")" | ":" "(" "," ... ")" | "``" [":" ] ["(" , ... ")"] "``"* | "`" "(" "," ... ")" "->" * = "," ... "->" = "?=" | "(" ")" | * "==" * | * "!=" * | * ">" * | * "<" * | * ">=" * | * "<=" * | "default" *: expression may use arithmetic functions but no user function calls