The three numeric types: boolean, integer and real, are organized in a hierarchy in the order mentioned. For arithmetic and comparative binary operations, if the types of arguments are both numeric but differ in type, they are coerced to the higher of the two types. For example, if you add an integer to a real number, say 3 + 4.5, the integer is coerced to 3.0 and a real number addition is done. Boolean atoms are always coerced to integers if they are used with arithmetic operations.
There are six types of atoms in Nial. They are boolean, integer, real, character, phrase and fault. The first three are numeric types and are used for arithmetic operations. The last three are literal types and are used for symbol manipulation. All six types of atoms are used in comparisons.
A boolean atom is the result of a comparison of array values; or the result of a test relating to a characteristic of an array or the content of an array. There are two boolean atoms: true and false, denoted by l and o respectively. When booleans are treated as numbers, true corresponds to one and false to zero.
An integer atom is a positive or negative whole number representing a quantity of units. A dash symbol (-) immediately preceding the integer denotes a negative integer. No space is permitted between the dash and the number, otherwise the dash is interpreted as the arithmetical operation of subtraction. Conversely, a space is required when subtraction is intended. An integer is represented by an internal form that limits its range of values in the Q’Nial implementation of Nial.
A real atom is a number which can represent any position on the real line. It may be written with a fractional part and/or with a decimal exponent. It is represented internally by a floating point number.
The operation
open
prepares the host file named by the phrase or string
Filename
for use in one of seven possible modes.
Mode
is a character, string or phrase encoding the mode as follows:
Mode
Mode Name
Description
r
read
existing sequential text file, read only
w
write
create sequential text file, write only
a
append
existing sequential text file, write only starting at the end
d
direct
existing or new binary file, direct access for both read and write
c
communication
existing or new sequential text file, for both read and write
pr
pipe read
output from the command Filename is available
pw
pipe write
input to the command Filename is provided
In
read, write, append
or
communication
modes,
Filename
must include the file extension, if any. In direct mode, two host files are opened, one with extension
.ndx
and the other with extension
.rec, standing for
index
and
record, respectively. In this mode,
Filename
is the root of the file name, without any extension.
The
communication
mode can be used with special forms of
readfile
and
writefile
usage to access a special device or a communications port. This mode is available only on the EXTDOS Version of Nial.
The pipe modes treat the
Filename
as a host command. The
open
causes the command to be executed. In
pipe read, the output from the command is obtained using
readfile; in
pipe write, input to the command is provided using
writefile.
The result of
open
is a file designator, an integer specifying an input/output port. This value is used in later references to the file.
The effect of
open
may be system dependent. If the file exists and can be opened by the user in the requested mode, the result will be the file designator. If it cannot be opened or does not exist, the result will be a fault message generated from the error message provided by the host system. For portable programs, the specific fault message should not be used in an equality test. The result of an open should be tested for a fault value using
isfault
to catch unexpected failures.
An
operation
is a functional object that is given an argument array and returns a result array. The process of executing an operation by giving it an argument value is called an
operation call
or an
operation application.
An operation can be constructed by defining one or more parameters and giving an algorithm to compute the result in terms of the parameters. An operation is usually given a name when it is defined. There are also program fragments that construct unnamed operations by composing operations, forming a list of operations or modifying an operation by use of a transformer.
A named operation is either a predefined operation or an operation that has been given an explicit name using the
IS
definition mechanism.
A sequence of simple-operation denotations forms an operation-sequence in the syntax of Nial. Its meaning is the same as the composition of the operations in the sequence which is explained by its effect when applied.
The result of applying an operation-sequence to an argument is determined by applying the simple-operations in the sequence in right-to-left order. The simple-operation on the right is applied to the argument giving an intermediate result. Then the simple-operation to the immediate left is applied to the result of the first application. Subsequent simple-operations are applied to the results in turn.
An example of this concept is the expression
(first rest) 8 7 2 5 3
7
The expression sequence
first rest
is evaluated by applying
rest
to the argument resulting in 7 2 5 3 and then
first
is applied giving 7.
The relationship can be described by the equations:
(f g) A = f (g A)
(f g h) A = f (g (h A))
etc.
which is mathematically known as the rule of
function composition
.
An
operation-form
is the syntactic structure used to describe an operation in terms of a parameterized expression-sequence. The identifiers following the keyword operation are called the
formal parameters
. The body of an operation-form is normally a block but it may be an expression-sequence without automatic localization.
An operation-form defines a local environment. The formal parameter names are names of local variables. If the body of the operation form is a block, the local environment of the block is extended to include the formal parameters. When the operation is applied, the formal parameter names are assigned from the value of the actual argument. If there is only one formal parameter, the actual argument is assigned to it as a whole; otherwise, the items of the actual argument are assigned to the formal parameters in corresponding order. If there is a length mismatch between the list of formal parameter names and the values of the actual argument, the fault
?op_parameter
is returned.
The value of the application of the operation is the value of the body of the operation-form, which is evaluated with the local variables in the parameter list assigned as described above. In determining the association for a name that appears in the body of an operation form, Q’Nial looks for the name in the local environment. If the name is not found locally, the name is sought in surrounding environments until it is found or until the global environment is searched. If it is not found, a fault
?unknown identifier:
is given when the operation-form is analyzed (parsed).
Operation-forms are most frequently used in definitions where they are given an associated name. However, an operation-form can appear directly in an expression provided it is enclosed in parentheses. In this usage, it can be an argument to a transformer name or can be applied to an array argument.
The operation
opposite
returns the opposite value of the integer or real number
A. The opposite value of a positive number A is the corresponding negative value and vice versa. The operation produces the following results when applied to atoms of the six types:
Atomic Type
Result
boolean
opposite of the corresponding integer
integer
opposite integer
real
opposite real number
character
fault ?A
phrase
fault ?A
fault
argument A
The name
opp
is a synonym for
opposite
used as an abbreviation.
In Nial, the symbol
-
is used as part of negative numbers and as a synonym for
minus. Care must be taken to use a space after the symbol before a constant to get the expected subtraction.
 If any item of
A
is
true, the result is
true; otherwise it is
false. Used in binary form, it implements the usual or-connective of logic. If
A
is a simple array but has a non-boolean item, the result is the logical fault
?L. The operation extends to non-simple arrays by the multi pervasive mechanism.
l or o
l
or oooolooo
l
lloo or lolo
lllo
Or
is a reductive operation in that it reduces an array of booleans to a single boolean. Applied to a pair of bitstrings, it produces a bitstring resulting from applying or to the bits of its arguments in corresponding positions.
The transformer
OUTER
transforms an operation
f
to an operation that applies
f
to each item of the cartesian product of its argument. In the case where the arguments are a pair of lists
A
and
B, the result is a table of the applications of
f
with every pair chosen with the first item from
A
and the second from
B.
The first example shows
OUTER *
being used to generate a multiplication table. The second shows that it can be applied to an operation that has a non-atomic result.
 Q’Nial uses 32 bit integers and checks for overflow during computations. If an arithmetic computation produces an overflow, a fault value is produced.
An overflow can also develop during computation with real numbers. The computer hardware detects such a problem and an interrupt occurs which causes Q’Nial to jump to top level.
The operation
pack
interchanges the top two levels of an array with equally shaped items. If
A
is an array with items all of the same shape, the result is an array of that shape, with items having the shape that
A
has. If
A
does not have items of the same shape but all the items that have zero items or more than one item are of the same shape, it is modified by replicating the items with one item to that shape before interchanging levels. Otherwise it returns the fault
?conform.
The items of the result are the arrays of items of
A
from corresponding positions.
Pack
is used implicitly in all binary pervasive and multi pervasive operations.
The first example shows that
pack
of a pair of triples is a triple of pairs. The second shows that
pack
of a quadruple of pairs is a pair of quadruples. The third shows that
pack
of a pair of 2 by 3 tables is a 2 by 3 table of pairs. The final example shows a case where replication occurs. The first and last items have shape [3]. The second and third items are an atom and a solitary and hence can be replicated.
 Used in infix notation, it has the effect of forming a pair from the two arguments. An explicit pair can also be achieved with list or strand notation. The operation is useful in functional expressions where it is an argument to a transformer.
The operation
parse
carries out the internal parsing capability of the interpreter. Its argument is a token stream produced by
scan
or
deparse. The result is the parse tree recording the semantic intent of the input. The parse tree is a highly encoded form of nested array designed for efficient processing.
The first example gives the parse tree corresponding to an application of an operation to a strand of constants. The second example gives the parse tree for the definition of
foo
as the predefined operation
first. The small integers that appear as the first items of the arrays serve as tags to indicate the kind of node that is represented in the parse tree. They are used by the
eval
and
deparse
operations. The representation of parse trees is implementation dependent and is not intended for program manipulation.
Equations
Ts a token stream ==> parse deparse parse Ts = parse Ts
S a string ==> eval parse scan S = execute S
The transformer
PARTITION
is used to apply operation
f
to partitions of the array
A, placing the results of the applications as partitions of the result array.
The partitions of
A
are determined by the axis numbers in
I, using (
I split A) to form them implicitly. The results of applying
f, viewed as an intermediate array
R, are combined into the final result, using (
J blend R), to place the axes of the items of
R
into the final result. If the left argument is a solitary, it is assumed to be used as the argument to both
split
and
blend
implicitly.
For a table
A, I = 1, f
being
reverse, the effect is to reverse the rows of
A. In general, if
I = J
and is a single axis number and
f
maps a list to one of the same length, the effect is to apply
f
to the lists formed by pushing axis
I
down, resulting in an array of the same shape as
A. Two dimensional partitions of a three dimensional array can be mapped using an
I
with tally 2.
The transformer
RANK
is a simpler version of a partitioning transformer that always pushes down the right end axes.
 It returns the argument
A.
Pass
is used when expressing computations in a functional style using an atlas (a list of operations).
The operation
paste
combines character tables in a two dimensional layout. It is used internally by
picture
and can be used to create modifications of the standard picture output to improve the display of results. The left argument
Sw
has six parameters that control the layout and the right argument
A
is the array of tables to be combined. The components of argument
Sw
are the following:
Argument
Vertical edge spacing
an integer indicating the number of blank lines to be put between the rows of items
Horizontal edge spacing
an integer indicating the number of blank spaces to be put between the columns of items
Vertical line indicator
If the value is 1, vertical lines are drawn between columns. If it is 0, they are not
Horizontal line indicator
If the value is 1, horizontal lines are drawn between rows. If it is 0, they are not
Vertical justification indicator
This is either a single integer indicating the same justification for all fields or an array of integers of the same shape as A indicating the justification for each item of A. The codes are: 0 for top justification, 1 for centered and 2 for bottom justification
Horizontal justification indicator
This is either a single integer indicating the same justification for all fields, or an array of integers of the same shape as A indicating the justification for each item of A. The codes are: 0 for left justification, 1 for centered and 2 for right justification
If either line indicator is 1, the entire result is boxed.
1 2 1 3 1 1 paste EACH sketch (2 4 reshape 'some' 'words' 'of' 'wisdom')
+--------+---------+------+----------+
| | | | |
| some | words | of | wisdom |
| | | | |
+--------+---------+------+----------+
| | | | |
| some | words | of | wisdom |
| | | | |
+--------+---------+------+----------+
Some of the operations of Nial that operate on simple arrays are extended to arbitrarily nested arrays by being applied to the atoms at the deepest level. These are called
pervasive
operations.
There are three classes of pervasive operations:
unary pervasive, binary pervasive
and
multi pervasive. The first class applies the operations to the atoms of the array. The second class are binary operations that apply to pairs of atoms from corresponding positions in the pair of arguments. The third class applies the operation to the simple arrays formed from atoms in corresponding positions in the items of the argument, reducing the simple array to a single atom.
The operation
phrase
converts a string into a phrase with the same content. It returns phrases and faults unchanged and converts other atoms after coercing them to their string equivalents. The resulting phrase may contain blanks. There is a one-to-one correspondence between phrases and strings.
phrase '(613) 549-2222'
(613) 549-2222
set "decor ; EACH phrase o 613 3.5 `A 'Now' "Wow ?error
"o "613 "3.5 "A "Now "Wow "error
Equations
isphrase P ==> phrase string P = P
Pragmatics
The correspondence between phrases and strings is not complete in that phrase cannot hold a null character (representation 0) and attempting to build such a phrase using the operation
phrase
will result in a truncated phrase.
Pick
is the fundamental selection operation for arrays. The normal use is that
I
is an address for
A. The result is the item that is located at address
I
in 0-origin addressing. If
I
is a solitary integer and
A
is a list,
I
is converted to an integer to make it an address for a list. If
I
is not a valid address for
A, the result is the fault
?address.
3 pick 3 6 23 46 57 25
46
[3] pick 3 6 23 46 57 25
46
A := 2 3 reshape "a "list "of "words "as "phrases
a list of
words as phrases
0 2 pick A
of
5 5 pick A
?address
In the first example,
pick
selects the fourth item because of 0-origin indexing. In the second example, [3] is not an address of the list (its addresses are integers) but it is converted to integer 3. Thus, a solitary integer can be used as an address of a list for
pick. The third example shows that an address for a table is a pair of integers. The last example shows that picking out of range returns a fault.
The concept of picking closely corresponds to subscript notation in mathematics. Q’Nial supports a direct notation for subscripting a variable:
Var@I. This notation, called the
at notation, can be used on both the left and right of an assignment expression. Its use on the right is equivalent to
pick.
Equations
I pick A = (list I) pick A
I in grid A ==> I pick grid A = I
I in grid A ==> I pick (EACH f A) = f (I pick A)
A choose B = A EACHLEFT pick B
I pick A = \[I] reach A
The operation
picture
computes a character table that describes the array
A.
An atom is displayed directly. A nonatomic array is displayed as a tableau of rows and columns arranged to allow sufficient space for the largest items. In most cases, the items of the array are displayed within a frame diagram.
The operation is affected by the setting of two internal switches:
diagram/sketch
and
decor/nodecor. The first controls the use of frames in the pictures. The second controls the decoration of atoms, solitaries, strings and empty arrays.
In
diagram
mode, a nonatomic array is displayed with a frame and the upper left corner has an
o
if it is a single. In sketch mode, a simple array, one having atomic items, is displayed without a frame. A non-simple array has a frame and a nonatomic single has an
o
in the upper left corner of the frame.
The
decor/nodecor
switch pictures atoms in an undecorated manner if nodecor mode is set. If sketch mode is also set, strings and phrases appear the same. The decor mode pictures atoms so that they can be distinguished: characters are preceded by a grave symbol
`, phrases by a double quote
"
and faults by a question mark
?. In sketch mode with decor set, a string is enclosed in quotes and a simple solitary is placed in brackets.
In sketch mode an empty array has an empty picture, whereas in diagram mode, the picture of an empty array is one side of a frame diagram, with no items.
An array of dimensionality higher than two is laid out as an arrangement of tables. If the array has valence 3, the tables are arranged horizontally. If it has valence 4, the first axis is placed down the page and the second axis across the page. Thus, an array of shape 3 4 5 6 is pictured as 3 rows of 4 tables each of which is a 5 by 6 tableau. A space is left between each of the tables. For higher dimensions, the axes continue to alternate across and down the page with greater spacing between arrangements.
The result of
picture
coincides with the output displayed by Q’Nial in the top level loop.
 The final example illustrates that if the result of
picture
is displayed at top level in diagram - decor mode, the implicit use of
picture
on the resulting character table causes it to be decorated.
 The normal use is that
I
is the address where
B
is to be placed in a modified version of
A. The result is the modified array. If
I
is a solitary integer and
A
is a list,
I
is converted to an integer before being used. If
I
is not a valid address for
A, the result is the fault
?address.
"abc 2 place 2 3 4 5 6
2 3 abc 5 6
place (`_ 2) 'my work'
my_work
A := 4 5 6 7
4 5 6 7
A := 27 2 place A; A
4 5 27 7
50 100 place A
?address
The fourth example shows the use of
place
in updating the value of a variable
A. The last example illustrates an out of range use of
place.
Nial provides a simpler notation for updating a variable.
A@I
(pronounced
A at I) can be used on the left of assignment to update the item at location
I. If the address is out of range a fault occurs.
A@1 := 25
4 25 27 7
A@5 := 7
?address
A
4 25 27 7
The semantics of using the
at notation
on the left in assignment can be achieved using the operation
update. The
at notation
and the
update
operation are usually more efficient than
place
because they avoid doing a copy of the right argument of
place.
Equations
I in grid A ==> (I pick A) I place A = A
B \[I] deepplace A = B I place A
I in grid A ==>shape(B I place A) = shape A
The operation
placeall
generalizes the operation
place
to modify a collection of items of
A. It returns a modified version of array
A
with the items at the addresses
I
replaced by the items of
B. It corresponds to the sequential use of
place
with corresponding items from
B
and
I. If an item of
I
is repeated, its last occurrence will determine the effect on
A. If
B
has fewer items than
I, its items are used cyclically (the first item is used after the last and the list repeated).
("abc "def) (2 3) placeall 2 3 4 5 6
2 3 abc def 6
Text := 'my work is fun';
placeall ('_' (` findall Text)) Text
my_work_is_fun
A := 4 5 6 7 ;
A := (27 28) (3 2) placeall A; A
4 5 28 27
The last example shows the use of
placeall
to update the value of a variable
A.
Q’Nial provides a simpler notation for updating a variable at several locations. The notation
A#I
(pronounced
A at all I) can be used on the left of assignment to indicate the update of the items at the addresses given by
I. If an item of
I
is out of range, the result is a fault but the variable is modified by the assignments done to locations that precede the invalid one.
The semantics of using the
at all
notation on the left in assignment can be achieved using the operation updateall.
The
at all
notation and the
updateall
operation are usually more efficient than
placeall
as they avoid doing a copy of the entire right argument of
placeall.
Placeall
is not the same as
(pack B I) EACHBOTH place A. The latter results in an array of modifications of
A, one for each item to be replaced.
Equations
I allin grid A ==> (I choose A) I placeall A = A
I allin grid A ==> shape (B I placeall A) = shape A
The operation
positions
is used in conjunction with
picture
on singles, lists or tables. For such arrays, it returns an array of the same shape with the items being the addresses at which the pictures of the corresponding items of
A
appear in the picture of
A.
The operation
post
restructures an array
A
to be a table with one column holding the items of
A
in row major order.
post 3 4 5
3
4
5
post 'Now is the time'
'for all good men'
'to come to the aid'
'of the party'
+------------------+
|Now is the time |
+------------------+
|for all good men |
+------------------+
|to come to the aid|
+------------------+
|of the party |
+------------------+
The major purpose of post is to restructure data for display.
The operation
power
returns the result of raising the number
A
to the power
B. The result is an integer if both
A
and
B
are boolean or integer and
B
is non-negative. If
B
is an integer, the power is done by multiplication. Otherwise it uses
exp (B * ln A).
A ?A ?error
2 4 5.65685 ?A ?A ?error
2.5 6.25 9.88212 ?A ?A ?error
?A ?A ?A ?A ?A ?A
?A ?A ?A ?A ?A ?A
?error ?error ?error ?A ?A ?error
2 power 3
8
2 power -3
0.125
16 power .5
4.
The examples show that
power
has different meanings depending on the right argument
B. If
B
is a positive integer, the result is
A
multiplied by itself
B
times. If
B
is a negative integer, the result is the reciprocal of the result when
B
is positive. The last case illustrates that if
B
is 0.5, the result is a real number that is a square root of
Equations
isreal B ==> A power B = exp (B * ln A)
sqrt A = A power 0.5 (within roundoff error)
The ordering sequence of the characters for sorting purposes is fixed for each Q’Nial version.
The binary and multi pervasive comparative operations of Nial organize the atoms of Nial in a
prelattice
or sorting sequence. The operation
lte
(or
<=) does a less than or equal comparison between atoms. The numeric atoms are comparable, with a coercion being done if the arguments are of different numeric type. The
nadir, represented by the fault
?O, is less than or equal to all atoms. The
zenith, ?I
, is greater than or equal to all atoms. Except for these two special cases, literal atoms are incomparable with atoms of different type. However, they can be compared within the same type using a character collating sequence that is version specific.
Phrases are compared lexicographically, such that
"apple
is after
"ape
but before
"apt. Strings, being character lists, produce a list of results when compared.
Comparisons are of two forms: binary pervasive predicates which return boolean values and multi pervasive predicates that obtain the largest or smallest item in a list. If two atoms are incomparable, the predicates return
false, whereas
max
returns the
zenith
and
min
returns the
nadir.
These rules were chosen so that the following laws hold for all arrays
A
and
B:
max A <= B = and (A EACHLEFT <= B)
A <= min B = and (A EACHRIGHT <= B)
See the entry for
up
for a discussion of the lexicographic ordering in Q’Nial.
The operation
product
multiplies the items of a simple array of numbers, reducing them to a single number that is their product. The type of the result is the highest type of the items. The operation is extended to non-simple arrays by the multi pervasive mechanism. The symbol
*
and
prod
are synonyms for
product. The operation
times
is
product
restricted to use on pairs.
The first example shows the result of
product
on all combinations of types of atoms. The last example shows that pervasive extension of
product
multiples a list of triples in an item by item fashion.
Equations
A product B = B product B
product single A = EACH(product single) A
product EACH product A =f= product link A
shape cart A = product EACH tally A
product shape A = tally A
The operation
profile
is used to convert the profiling statistics into a displayable form. If
Fnm
is a file name, as a string or a phrase, then the output is written to the file. If it is the empty string, then the profiling information is displayed on the screen. If logging is on, it will be copied to the log file. The gathering of profile statistics must be turned off when
profile
is called.
A detailed example of the output is given in the help entry on profiling.
The expression
Profiletable
is used to summarize the internal data structures that are used in the gathering of profiling statistics to produce the same information that is displayed by the operation
profile. It produces a list of entries, one for each definition that has been encountered during execution with profiling on. Each entry includes the name, the number of direct calls, the number of recursive calls, the execution time, and a list of subentries for the definitions directly called by the definition. The subentries summarize the information for the called definitions in the same format except that no further breakdown is given on definitions they call.
A detailed example of the output of
Profiletable
is given in the help entry on profiling.
The expression
Profiletree
is used to display the detailed information from the internal data structures that are used in the gathering of profiling statistics. It produces a nested list of entries showing the calling dynamic calling sequence from top level, with the amount of time and number of calls at each level. Each entry includes the name, the number of direct calls, the execution time, and a list of subentries for the definitions directly called by the definition. The subentries provide the same information for the called definitions in the same format including entries on all the definitions that they call in turn.
A detailed example of the output of
Profiletable
is given in the help entry on profiling.
Q’Nial has a profiling capability that is used to gather relative execution times for operations, transformers and defined expressions written in Nial. The profiling capability has the following features:
Command
Purpose
setprofile A
turns on or off the internal routines that collect the statistics
profile Fnm
that displays the profile data to the screen or writes it to a file
Clearprofile
clears the current profile information and reinitializes the profiling system
Profiletable
provides the profile information as a table
Profiletree
provides the detailed profile information in terms of the call tree
The operation
setprofile
is called with argument
l
to turn on the collection of data for a profiling session. It is called with argument
o
to suspend gathering statistics for a session. Both calls to
setprofile
should be in the same scope, and cannot be nested. Only one profiling session can be underway at a time.
The operation
profile
is called with a text argument (string or phrase) that is used as the file name for the profile information. If an empty string is the argument, the output is sent directly to the screen. It computes the statistics on the profiling session underway, prepares the output, and writes it to the file.
The expression
Clearprofile
clears the two internal data structures that are built as execution is profiled so that a new profiling session can be started.
The expression
Profiletree
provides the summary data that is displayed in the output produced by
profile, but stores it in a Nial array.
The expression
Profiletree
provides a dynamic call tree of the execution with related times. This provides a more detailed breakdown of the profile data. The following session illustrates the use of the profiling capability:
setwidth 60;
test is op n {
z := random n n;
max abs (z - inv inv z) }
tryit is op N { test N }
again is op M N { sum EACH tryit (M reshape N) }
fact is op n {
IF n<=1 THEN 1 ELSE n * fact (n - 1) ENDIF }
# first profiling session
setprofile l
o
test 20
3.86843e-15
tryit 50
2.83107e-14
again 10 75
6.72483e-13
setprofile o
l
profile ''
Total execution time of profile session: 4.780000
Total execution time in top level calls: 4.420000
op name[.tr arg] calls[rec] time time/call % time
test.......................... 12 4.42 0.3683 100.0<
tryit......................... 11 4.31 0.3918 97.5<
test......................... 11 4.31 0.3918 100.00
again......................... 1 4.04 4.0400 91.4<
tryit........................ 10 4.04 0.4040 100.00
profiletree
+--------+-+----+-------------------------------------------
|TOPLEVEL|0|4.78|+--------------+---------------------------
| | | ||+----+-+----++|+-----+-+----+-------------
| | | |||test|1|0.11||||tryit|1|0.27|+------------
| | | ||+----+-+----++|| | | ||+----+-+----
| | | || || | | |||test|1|0.27
| | | || || | | ||+----+-+----
| | | || || | | |+------------
| | | || |+-----+-+----+-------------
| | | || |
| | | || |
| | | || |
| | | || |
| | | |+--------------+---------------------------
+--------+-+----+-------------------------------------------
--------------------------------------------------------+
----+--------------------------------------------------+|
---+|+-----+-+----+-----------------------------------+||
--+|||again|1|4.04|+---------------------------------+|||
++|||| | | ||+-----+--+----+-----------------+||||
|||||| | | |||tryit|10|4.04|+---------------+|||||
++|||| | | ||| | | ||+----+--+----++||||||
--+||| | | ||| | | |||test|10|4.04||||||||
---+|| | | ||| | | ||+----+--+----++||||||
|| | | ||| | | |+---------------+|||||
|| | | ||+-----+--+----+-----------------+||||
|| | | |+---------------------------------+|||
|+-----+-+----+-----------------------------------+||
----+--------------------------------------------------+|
--------------------------------------------------------+
# second profiling session
clearprofile
setprofile l
o
test 30
1.72085e-14
fact 4
24
setprofile o
l
profile ''
Total execution time of profile session: 0.330000
Total execution time in top level calls: 0.050000
op name[.tr arg] calls[rec] time time/call % time
test.......................... 1 0.01 0.0100 20.0<
fact.......................... 1[ 3] 0.04 0.0400 80.0<
profiletree
+--------+-+----+-------------------------------------------
|TOPLEVEL|0|0.33|+--------------+---------------------------
| | | ||+----+-+----++|+----+-+----+--------------
| | | |||test|1|0.01||||fact|1|0.04|+-------------
| | | ||+----+-+----++|| | | ||+----+-+--+--
| | | || || | | |||fact|1|0.|+-
| | | || || | | ||| | | ||+
| | | || || | | ||| | | |||
| | | || || | | ||| | | |||
| | | || || | | ||| | | |||
| | | || || | | ||| | | |||
| | | || || | | ||| | | |||
| | | || || | | ||| | | ||+
| | | || || | | ||| | | |+-
| | | || || | | ||+----+-+--+--
| | | || || | | |+-------------
| | | || |+----+-+----+--------------
| | | |+--------------+---------------------------
+--------+-+----+-------------------------------------------
------------------------------+
-----------------------------+|
----------------------------+||
---------------------------+|||
--------------------------+||||
-------------------------+|||||
----+-+--+--------------+||||||
fact|1|0.|+------------+|||||||
| | ||+----+-+--++||||||||
| | |||fact|1|0.||||||||||
| | ||+----+-+--++||||||||
| | |+------------+|||||||
----+-+--+--------------+||||||
-------------------------+|||||
--------------------------+||||
---------------------------+|||
----------------------------+||
-----------------------------+|
------------------------------+
% end of session
The profile statistics show the division of time between the various definitions. For each definition called during the profiling session there is a summary of the number of calls and time used. For each definition there is also a breakdown on the time spent in other definitions that were directly called from that definition.
Entries in the profile statistics that have a "<" to the right of the last value are top level calls. All other entries have been called by these top level calls.
The time spent in direct recursive calls is counted in the original call. The number of such calls is placed in brackets after the number of top level calls.
The statistics do not account for local definitions within global ones; their execution time is simply added to the time for the global definition.
A feature of the profiler not shown in the above example is that when timing a transformer definition, the time spent executing the argument operation(s) is computed in order to understand how much of the cost of the transformer is due to applying the argument operation(s).
The accuracy of the timing information is limited by the precision of the information available through system calls to the host system. For very small definitions the statistics may show no execution time. Usually their effect can be estimated by considering the execution time of the calling definition.
The
Profiletable
result provides the summarized profile statistics as a quintuple consisting of the name of the definition, the number of calls, the number of recursive calls, the time, and a list of records for each definition it directly calls. Each of the latter records has the same information provided for the routine itself, but does not report on the definitions it itself calls.
The
Profiletree
result starts with a node indicating the toplevel and the total execution time. It has a list of subnodes for each definition called from the top level. Each of these has the number of calls, the execution time and nodes for each definition it calls. A recursive call is reported directly in a subnode and hence a deeply recursive routine will produce a deeply nested array of profile information.
Nial is a programming language specifically designed for use in an interactive environment. The formal description of the language describes the valid language constructs that can be entered in one interaction; and explains the meaning of one such entry in terms of the environment created by earlier interactions in the same session. The term
program fragment
is used to describe a meaningful piece of program text.
The rules for writing well formed program fragments in Nial are called the
syntax rules
of Nial. A program fragment that is well formed is said to be syntactically correct. The syntax rules are analogous to the rules of grammar that determine the correctness of English.
The operation
putfile
writes the rows of items of
A
as text records to the file with name
Filename. It is used in conjunction with
getfile. The file must not be open. Items of
A
are arrays of characters of any valence.
In the following example, three strings would be written to the file Maillist.
putfile "Maillist ['Now is the time' 'for all good
men','to come to the aid']
Definition
putfile IS OPERATION File A {
IF not isfault (Filenum := open File "w) THEN
ITERATE (Filenum writefile) (link EACH rows A) ;
close Filenum ;
ELSE
Filenum
ENDIF }
The operation
quiet_fault
is a version of operation
fault
that turns off fault triggering before producing the fault and restores the triggering switch to its prior value after producing the fault.
 If the divisor
B
is zero, the result is zero. If it is negative, the result is the fault
?negative divisor. The operation
quotient
converts boolean arguments to integer but otherwise produces a fault if either argument is not an integer.
The first example illustrates all combinations of atom types for the two arguments to
quotient.
The quotient on division by a positive integer
B
is always an integer on the same side of the origin as
A. The result of
A quotient 0
is 0 rather than a fault since this rule is compatible with the choice that
A mod 0
is
A.
The operation
quotient
is used in place of
divide
in situations where the result must be an integer.
Equation
isinteger A ==> A quotient 0 = 0
floor (A - (B*(A quotient B))) =f= A mod B
A quotient B =f= floor (A / B)
The operation
raise
is used to partition an array
A
along its axes by indicating that the first
N
axes are to be retained in the result. The remaining axes become axes of the items of the result.
N
must be an integer in the range from 0 to
valence A. The result is an array of shape given by taking the first
N
items of
shape A. The items of the result have the shape given by dropping the first
N
items of
shape A. Thus, the
2 raise
of an array of shape 3 4 2 is a 3 by 4 table of pairs. The
1 raise
of the same array is a triple of 4 by 2 tables.
 The argument
S
is a shape and
random
generates
product S
numbers storing them in an array of shape
S.
Random
uses a congruential method with period<PHONE_NUMBER>. The random generator generates a sequence of integers using the formula:
R := 16807 * R mod 214783647
starting with R = 314159262. The result is the real R/2147483647. The sequence can be initialized using the operation seed
with an argument
N, where
0. < N < 1.0.
Seed
resets the random number generator to use R = floor (R1 * 214783647.).
The first example shows the five random numbers generated after a seed of 0.314159 is used. The second example illustrates how to use the operation
random
to generate 10 random integers between 0 and 99 and store them in an array of shape 2 5.
The transformer
RANK
applies an operation
f
to arrays formed from the last
N
axes of
A. The results of the applications of
f
must all be the same shape and are combined to form a result array using
mix.
The operation
reach
is used to select an array at an arbitrary path
P
within the nested structure of
A.
P
is a list of addresses where the first item of
P
selects an item of
A, the second selects an item of that item, etc. If
P
is empty, the result is
A. The operation is implemented as an iterated
pick
and hence the addresses in the path are converted using first if they are solitary integers. If one of the picks attempts a selection out of bounds, the result is fault
?path.
In the example, the path (2 0) 1 picks the item with address 1 of the location with address 2 0.
Q’Nial supports a direct notation for doing a
reach
into an array associated with a variable:
Var@@I
(pronounced
Var at path I). The
at path
notation can be used on both the left and right of an assignment expression.
Definition
reach IS P A {
IF empty P THEN
A
ELSE
rest P reach (first P pick A)
ENDIF }
Equations
P reach A = (EACH list P) reach A
items of P are valid addresses where used ==> (P reach A) P deepplace A = A
The operation
read
is used in an interaction with the user running a program to obtain an array value from the keyboard after issuing a prompt given by the string or character
P. After receiving the prompt, the user types a sequence of characters and then presses the
Return
key. The sequence of characters, excluding the
Return, becomes the string entered. The string is executed to give the result of
read. If the string being executed contains an integer representation that would convert to a number outside the range of integers, the corresponding real number is returned.
A := read 'Type an expression : ';
Type an expression : 3 + count 10
A
4 5 6 7 8 9 10 11 12 13
The operation
read
is similar to operation
readscreen. The difference is that in
read
the string received from the keyboard is evaluated, whereas in
readscreen, the string is returned.
The operation
readarray
is used to read the component or components indicated by
N
from the direct access file with file port
Fnum. The file must have been created using
writearray
and must have been opened as a direct file using
open.
If
N
is an integer, the result is the array stored at record position
N
of the file.
If
N
is a list of integers, the result is a list of the corresponding records. If there is no record at position
N, the result is the fault
?missing. If
N
is greater than
filetally A, the result is the fault
?eof.
readarray Fnum [23,24,25];
If
N
is a solitary integer, the result is a solitary holding the selected item.
The expression
Readchar
is used to obtain the result of a single keystroke on the keyboard. The character is not displayed on the screen automatically. If the keystroke corresponds to a single displayable ASCII character, the result is that character. Otherwise, the result is a phrase indicating the function key pressed.
Q’Nial supports all the standard keys of the IBMPC keyboard. On a non-IBMPC version of Q’Nial, these keys are available through use of escape sequences.
The input from
Readchar
can be displayed by the use of
writechars.
writechars Readchar
Backspace
In the example, the user responded by pressing the backspace key.
Readchar
is used when developing an interactive program in which the user uses keystrokes to indicate requested actions. The decision whether or not to display a character on the screen can be made by the program rather than done automatically.
The following table lists the non-character keystrokes recognized on the IBMPC keypad and gives the default sequences accepted by an ASCII terminal when the corresponding keystroke is not present. The two keyboard layouts on SUN workstations behave like an IBMPC keyboard.
IBMPC
IBMPC Keystroke
Sun Console
Ascii Terminal
Return
Return
Return
Backspace
Backspace
Backspace
Del
Del
Del
Ins
Esc Esc i
Esc Esc i
Up_Arrow
Up_Arrow
Up_Arrow
Down_Arrow
Down_Arrow
Down_Arrow
Right_Arrow
Right_Arrow
Right_Arrow
Left_Arrow
Left_Arrow
Left_Arrow
Home
Home
Esc Esc h
End
End
Esc Esc e
PgUp
PgUp
Esc Esc u
PgDn
PgDn
Esc Esc d
Ctrl Left_Arrow
Esc Left_Arrow
Esc Esc c l
Ctrl Right_Arrow
Esc Right_Arrow
Esc Esc c r
Ctrl PgUp
Esc PgUp
Esc Esc c u
Ctrl PgDn
Esc PgDn
Esc Esc c d
Ctrl Home
Esc Home
Esc Esc c h
Ctrl End
Esc End
Esc Esc c e
Tab
Tab
Tab
Shift Tab
Esc Tab
Esc Tab
Esc
Esc Esc Esc
Esc Esc Esc
F1 to F10
F1 to F10
Esc 1 to Esc 0
<Alt F1> to <Alt F10>
Esc F1 to Esc F12
Esc Esc 1 to Esc Esc 0
<Alt A> to <Alt Z>
<Esc a> to <Esc z>
<Esc a> to <Esc z>
The IBMPC keyboard support also recognizes the function keys modified by Shift and Ctrl and <Alt 0> to <Alt 9> but these are not mapped onto corresponding escape sequences in the Unix version.
The keyboard mappings are set by default but may be controlled by description files stored in the
initial
directory.
The operation
readfield
is used to read a portion of an existing host file as a character string. The string or phrase
Filename
is the name of the file, the integer
Start
is the offset to the beginning of the field to be read and integer
Size
is the size of the field to be read. The result is character string of the items read. If the logical field extends across a line boundary the field will include the newline indicator appropriate for the host system (either linefeed or return-linefeed).
Example
readfield "Myfile 80 20
The result is the string beginning at offset 80 in file
Myfile
of length 20.
The operation
readfile
is used to read the next record from the sequential text file designated by file port
Fnum. The file port
Fnum
is the integer returned by
open, which must have been called before executing the
readfile.
A record of text in a text file is a sequence of characters up to but excluding an end-of-line indication. The end-of-line indication is system dependent and may be one or more characters. The interface between Q’Nial and the host system recognizes end-of-line and, on successive readfile requests, returns the records of the text file with the end-of-line indications removed.
If
readfile
is used with a second argument
N, it reads the next
N
bytes of the text file and returns them as a string. In this usage, end-of-line characters are processed as any other character and are not removed. This second form is intended for reading information from device drivers or a communications port, where end-of-line indications may not be given.
The following example shows a text file, Myfile being opened and its records being gathered as a list of strings. The example has the same effect as the expression
getfile "Myfile.
File_number := open "Myfile "r ;
Records := '' ;
Record := readfile File_number ;
WHILE Record ~= ??eof DO
Records gets Records append Record ;
Record := readfile File_number ;
ENDWHILE ;
close File_number ;
The operation
readrecord
is used to read the component or components indicated by
N
from the direct access file with file port
Fnum. If
N
is an integer, the result is the string stored at component position
N
of the file. The file must have been opened as a direct file and must have been created using
writerecord.
If
N
is a list of integers, the result is a list of the corresponding records. If there is no component at
N, the result is the fault
?missing. If
N
is greater than
filetally A, the result is the fault
?eof.
A direct access file is stored as two host system files: a
.ndx
file holding index information and a
.rec
file holding the records.
readrecord Fnum [23,24,25];
convert_records IS OP Filein Fileout {
Fin := open Filein "d ;
Fout := open Fileout "d ;
Num := filetally Fin ;
writerecord Fout (tell Num)
(EACH convert_op (readrecord Fin (tell Num)));
EACH close Fin Fout ; }
The first example shows a call of
readrecord
that returns three records. The second example shows an operation
convert_records
that reads all the records in direct file
Filein, modifies them by
convert_op
and writes them to direct file
Fileout. For a very large file, it may be necessary to replace the use of
EACH
with a loop.
The
readrecord
operation can access fields of a fixed format file created external to Q’Nial. If the component number used in a
readrecord
is beyond the end of file as indicated by the
filetally Fnum, stored in the
.ndx
file, the
.rec
file is checked to see if the host file information indicates that the
.rec
file extends beyond the length expected for a file created by the Q’Nial operations. If so, the
.rec
file is assumed to consist of a sequence of blocks of records, with each block having fields of the sizes indicated by the
.ndx
file. The index information for the record at
(N mod filetally Fnum)
is used to find the requested component within the block of records found from
(N quotient filetally Fnum).
To use this feature, a separate process must be followed to create the
.ndx
file corresponding to one block of records in the external file. This is done by writing a sequence of strings of the lengths of the records to a dummy direct access file and then renaming the resulting index file and the external file to be names expected by the Q’Nial direct access facility.
The operation
readscreen
is used to obtain a string from the keyboard after issuing the prompt
P. The prompt
P
must be a character or a string. After receiving the prompt, the user types a sequence of characters and then presses the
Return
key. The sequence of characters, excluding the
Return, is the string returned as the result of
readscreen.
Name := readscreen 'Your name? : ' ;
Your name? : Mike
Name
Mike
Answer := first 'Do you wish to continue? y/n: ' ;
IF Answer in 'yY' THEN ...
The operation
readscreen
is similar to the operation
read, the difference being that in
read, the string received from the keyboard is evaluated, whereas with
readscreen, the string is returned.
The operation
recover
is prepared by the user of Q’Nial to handle the action to be taken on detection of
<Ctrl c>
or any other situation that forces a return to top level. If a
recover
operation does not exist in the workspace, Q’Nial returns to the top level loop on a
<Ctrl c>. This operation is necessary to provide control over
closed
applications which do not permit the user to access Q’Nial at the top level loop. On its call the argument provided to the operation will be a character string giving the nature of the interruption, for example
'user interrupt'
if a Ctrl-C has occurred or
'programmed interrupt'
if
toplevel
has been called.
recover IS OP Msg { Continue; }
recover IS Op Msg {
IF Msg = 'user interrupt' THEN
IF first readscreen 'Do you wish to restart? y or n : ' in 'yY' THEN
Restart
ELSE
Continue
ENDIF
ELSE
Continue
ENDIF }
In the first example, if
<Ctrl c>
is pressed or some other action forces a jump to top level, the session ends saving the workspace in
continue.nws. The message parameter is ignored.
In the second example of a definition for
recover, a jump to top level by a
<Ctrl-C>
interrupt results in the display of the prompt:
Do you wish to restart? y or n :
If the response is text that begins with y or Y, the expression
Restart
is executed. If the session had begun with a workspace named on the command line, the workspace would be reloaded and its
Latent
expression, if any, evaluated. If the response begins with something other than y or Y, or the interrupt was of some other kind then the current session ends by executing
Continue.
RECUR is a general recursion transformer with five operation arguments:
test
checks that the argument meets an end condition,
endf
is applied to the end argument before starting to build the result,
parta
computes the left value from the argument, which is stacked,
joinf
combines the left and right values as the recursion unwinds, and
partb
gives the value to be recurred on to produce the right value.The recursion terminates provided the repeated application of the operation
partb
results in an array that satisfies
test.
RECUR is TR test endf parta joinf partb OP A {
Elements := Null;
WHILE not test A DO
Elements := Elements append parta A;
A := partb A;
ENDWHILE;
Res := endf A;
FOR E WITH reverse Elements DO
Res := E joinf Res;
ENDFOR;
Res }
Equations
RECUR [test, endf, parta, joinf, partb] A
= FORK [test, endf, joinf [parta, RECUR [test, endf, parta, joinf, partb] partb]] A
RECUR [ 0 =, 1 first, pass, product, -1 +] N = factorial N
RECUR [ empty, 0 first, first, plus, rest ] A = sum A
The transformer
REDUCE
transforms an operation
f
into an operation which, when applied to an array
A, has the same effect as evaluating an expression in which
f
is placed between the items of the array, with grouping done in a right-to-left manner. If
f
is an operation that maps a pair of atoms to an atom,
REDUCE f
reduces a simple array to an atom. For the built-in reductive operations:
sum, product, and, or, max, min
, and
link, REDUCE f
is the same as
f.

The predefined reductive operations include:
Operation
Function
and
logical "and" of a boolean array
link
the list of all the items of the items in the array
max
highest item in the array
min
lowest item in the array
or
logical "or" of a boolean array
product
arithmetic product of an array of numbers
sum
arithmetic sum of an array of numbers
All of the above operations except
link
are also multi pervasive.
The transformer
REDUCE
can be used to produce a reductive operation from a binary one.
The operation
regexp
is used to find the first substring in a string
S
that matches a regular expression pattern given by
R. The pattern can contain subgroups indicated by parentheses. If there are no subgroups in
R
then the result is a pair consisting of a boolean indicating if the search succeeded and the first substring in
S
matched by
R. Otherwise it is a longer list with the additional items being the substrings that match the subgroups in
R.
In the first example, the string
'def'
matches a substring of the right argument. In the second example the string
'deg'
does not match. In the third example, the regular expression pattern searches for a string anchored at the beginning (by using ^) and consisting of any character followed by a "b" or a "g", followed by any two characters followed by a "c" or a "d". The result finds a match and picks out the two substrings corresponding to the two subgroups.
The operation
regexp_match
is used to test whether there is a substring in a string
S
that matches a regular expression pattern given by
R. It returns
true
if a match is found and
false
otherwise. If the optional argument
O
is
"i
then the search is case insensitive.
regexp_match 'def' 'abcdefghi'
l
regexp_match 'deg' 'abcdefghi'
o
regexp_match '^.[bg]..[eh]' 'abcdefghi'
l
regexp_match '^(.)[bg](..)[eh]' 'abcdefghi'
l
regexp_match 'DEF' 'abcdefghi' "i
l
In the first example, the string
'def'
matches a substring of the right argument. In the second example the string
'deg'
does not match. In the third example, the regular expression pattern searches for a string anchored at the beginning (by using ^) and consisting of any character followed by a "b" or a "g", followed by any two characters followed by a "c" or a "d". A match is found. Example four shows that subgroups are allowed in the pattern and do not affect the match. The final example shows a case insensitive search.
The operation
regexp_substitute
is used to replace one or more substrings in string
T
that match the regular expression pattern
R
with string
S. If the optional argument is
"i
then the search is case insensitive. If it is
"g
then all substitutions are done.
In the first example, the string
'def'
is replaced by
'XX'. In the second example the string
'deg'
does not match and
T
is returned. In the third example, the regular expression pattern matches with a case insensitive search and the substitution is done. Examples four and five show multiple substitutions first with a case sensitive pattern and then with a case insensitive one.
A Prototype DLL calling interface has been added to the Windows versions of Q’Nial (Console, GUI and DLL version). This ability allows NIAL code to call external routines in DLL libraries. These libraries can be user written, part of the Operating System, or 3rd party DLLs.
The DLL interface routines manage an internal table that keeps track of the currently installed DLLs and their argument types and result type. This table also tracks if the actual library has been loaded (by a call to
calldllfun). The table is preserved as part of the workspace, and once DLL functions have been registered in a workspace, they are still available if the workspace is saved and subsequently loaded. The CallDLL facility assures that the DLL files are loaded and unloaded appropriately.
Every time a workspace is saved all DLL libraries are unloaded and the internal table is adjusted to reflect that. When a workspace is loaded, none of the DLL libraries will be loaded until a specific call to
calldllfun
brings in the DLL. This process assures that resources are not wasted on registered DLL calls that are never used.
The
registerdllfun
routine must always be called first to enter DLL function into the internal mapping table. This table keeps track of the name and DLL file for the routine and all of the argument types and function results. The DLL file is
not
loaded until the routine is called with the
calldllfun
routine. So errors involving paths to the DLL file or other related problems are not reported until the first call to
calldllfun.
The operation
registerdllfun
initiates an interface to an external routine implemented as a DLL for 32-bit Windows. The name
Nm
is the name associated with the routine,
Dllnm
is the actual name of the DLL routine,
Path
is the path/filename to the DLL executable file,
Restype
is the result type and
Argtypes
is the list of argument types.
Nm
is a phrase or string that is used to identify the requested DLL routine when using the
calldllfun
operation. It can be any name you desire. Duplicates are not allowed and the registration routine will overwrite previous table entries if the same name is used again.
Dllnm
must be the exact name of the routine in the DLL library (case sensitive, including underscores, etc.).
Path
must the full path to the DLL library file. If the DLL library file is in the standard search path for DLLs then the full path is not required.
Restype
must be a string or phrase of one the valid types described below.
Argtypes
must be a list of the valid types described below.
If the only argument is
Nm
and this names an already installed DLL routine, then the routine is unregistered and the DLL is unloaded.
Calling Conventions (C or Pascal)
The facility is able to map in DLL routines compiled for both C and Pascal calling conventions. The facility makes this determination by checking for a leading underscore on the internal name of the DLL routine. If one exists, then it is assumed that the C calling style should be used, otherwise the PASCAL calling style is used. It is possible to create DLL routines that do not follow this standard convention, so be careful.
When using either style calling conventions, the facility has a limit of up to 10 words of arguments (10 - 4 byte integers). Pascal calling convention is strict in that the number of supplied arguments must be the same as the number of required arguments. The limit of 10, is a compiler option and can be extended if necessary.
Arguments to DLL routines
Currently, Q’Nial is able to pass only simple types to DLL routines. The following is a list of the types that can be passed and returned as results:
Type in DLL routine
Nial Type
CHAR
character
SHORT
integer
LONG
integer
WCHAR
integer
LPSTR
string
LPTSTR
string
HANDLE
integer
DWORD
integer
LPDWORD
integer
WORD
integer
BOOL
integer
HWND
integer
UINT
integer
DOUBLE
real
FLOAT
real
LRESULT
integer
LPARAM
integer
LRESULT
integer
HMENU
integer
LPCTSTR
string
SCHAR
char
SDWORD
integer
SWORD
integer
UDWORD
integer
UWORD
integer
SLONG
integer
SSHORT
integer
ULONG
integer
USHORT
integer
INT
integer
These type are the simple base types used in the Windows (Win32) API (plus common types which are bolded). The above type names (in string or phrase form) can be used as the type names required as arguments to the
registerdllfun
operation. Each particular type affects Nial values in a different way. Use of types other than common types generally requires a knowledge of C/C++ types and casting. The Windows types are supplied to easily map in Windows API DLL calls.
When calling DLL routines using
calldllfun, the facility will properly cast the incoming Nial type/value to the type of the particular argument specified by the
registerdllfun
operation. If the Nial type cannot be converted, then
calldllfun
will report an error and exit.
Variable Parameters
Variable parameters are supported. Arguments that are marked as
variable parameters
, have the returning value of the parameter added to the result list with the first element being the normal result of the called DLL routine. The Nial user must then select the portions of the result that are desired.
This feature also provides the ability to pass buffers to DLL routines. Many Windows OS DLL routines require a (char *) type pointer to a pre-allocated buffer and a buffer size. The routine places the result in the buffer up to the specified size. The interface to such a routine can be accomplished by sending a Nial string of the desired buffer size and the accompanying size integer as the arguments, having specified the first argument as a variable parameter. The resulting string is added to the result list with the first element being the normal result of the DLL routine call.
When passing variable parameters, the facility makes an internal copy of the data being passed. The called DLL routine uses this copy. This approach helps prevent the possibility of a DLL routine incorrectly manipulating the actual contents of a Nial array and causing a corruption of the workspace.
To specify that an argument is a variable parameter in the
registerdllfun
call prepend a
*
(asterisk) to the beginning of the type name (i.e. *CHAR). This indicates that the type is to be passed as a pointer (if it is not already a pointer), and that the result for that parameter is to be appended to the result of the entire call. Any, none or all of the parameters may be variable.
Functions Requiring Pointers to Data
Another common requirement of Windows OS DLL calls (and others too), is that they require the
address
of a variable to be passed as an argument instead of the actual value. The DLL function uses the value at the supplied memory address.
This capability is supported, and can be denoted by prepending a
&
(ampersand) to the beginning of the type name (i.e. &INT). At the Nial level, you still pass the
calldllfun
call a plain integer for that particular argument, but the CallDLL interface allocates memory for that argument, copies the value into the new memory, and passes the address of the memory cell to the DLL call. Upon return, the allocated memory is deallocated.
The
&
modifier can be used in conjunction with the
*
modifier. This usage generally denotes an argument that is a pointer to a type and whose value you wish returned along with the result.
Example 1
Let say we have a DLL routine that takes 3 argument. The first argument is the true argument and the next two are pointers to memory locations where two additional results are to be placed. The function also returns an integer result.
The routine squares the first argument and places the result at the memory location specified by the second argument. It also cubes the first argument and place the result in the location specified by the third argument. The function result is what is supplied as the first argument.
So the call passes in 5 as the true argument and two -10 values as dummy values for the next two arguments (it is possible that the function might require all arguments to specify proper values). The result is an array of 3 items, one is the function result and an additional item for every variable parameter in the argument list.
Example 2
The following is a call to a Windows API routines that requires a string buffer to be passed to the call:
registerdllfun "_GetComputerName "GetComputerNameA
'kernel32.dll' "BOOL ['*LPTSTR', 'LPDWORD'];
GetComputerName IS {
res name := calldllfun "_GetComputerName (1000 reshape ' ') 1000;
IF res = 1 THEN
name
ELSE
'Error Getting Computer Name'
ENDIF
}
GetComputerName
INIGO
Notice how the above code calls the DLL routine with an empty string buffer (1000 reshape ' ') and also passes the size of the buffer (1000). The Windows API requires this in order to safely write the Computer name in the buffer without overflowing the string buffer. The buffer argument is marked as a variable argument, so the result is passed back in the
calldllfun
call.
A Word of Warning
Use of DLL routines can cause unexpected results if incorrect parameters as passed. It is reasonable easy to crash the Nial application with a bad DLL call. Additionally, memory allocated inside a DLL function is not deallocated by Nial. Unless there is a symmetric DLL routine to deallocate memory, a memory leak will result if such a routine is used.
The following table gives the standard notations used in regular expressions:
Notation
Matching String
.
any character
*
the previous character zero or more times
+
the previous character 1 or more times
^
when at the beginning of a regular expression, matches the beginning of the string.
$
when at the end of a regular expression, matches the end of the string.
[xyz]
any character in the sequence of characters "xyz" where "xyz" can be almost any sequence of characters.
[^xyz]
any character NOT in the sequence of characters "xyz".
[x-y]
any character in the range of x to y.
|
allows the optional match of the regular expression on the left or the right of the "|".
()
bracketing allows the specification of groups. Anything that is matched between a set of brackets can later be extracted. Also used for bracketing regular expressions to force order of precedence.
The
REPEAT-loop
notation is used when executing an expression sequence repeatedly until a conditional expression returns
true.
F := open Filenm "r;
Lines := '';
Done := o;
REPEAT
Line := readfile F;
IF isfault Line THEN
Done := l;
ELSE
Lines := Lines append Line;
ENDIF;
UNTIL Done ENDREPEAT;
A
reserved word
or
keyword
is one that has a special usage in a Nial construct and must be used only for that purpose. A
block
delimits a local environment. It allows new uses of names which do not interfere with uses of those names outside the block. For example, within a block, a predefined operation name can be redefined and used for a different purpose. Only the reserved words of Q’Nial cannot be reused in this fashion.
An identifier, which is spelled the same, ignoring case, as any of the reserved words given below cannot be used to name a variable or a definition. In a local environment, an identifier can be chosen that is the same as a predefined or user-defined global definition name. Such a choice makes the global use of the name unavailable in the local context.
A reserved word is displayed in upper case in canonical form.
The operation
reshape
is the major mechanism in Nial for creating multivalent arrays. The operation requires
A
to be a shape, either
Null
or a list of integers. If
A
is an integer, it is converted to the solitary holding the integer. If
A
is not a shape, the result is the fault
?shape.
The result of
reshape
is an array of shape
A
with items chosen from the list of items of
B. If the number of items to be used to fill the result is less than the tally of
B, the remaining items of
B
are ignored. If the number is more than the tally of
B, the items of
B
are used cyclically. If
B
has no items, fault
?fill
is used as the items of the result.
The last example illustrates that to replicate an arbitrary array, in this case the pair 3 4,
reshape
is applied to the solitary holding the array.
 It causes the workspace to be cleared and Q’Nial to be reinitialized to be in the same state it was when the session began. If the original invocation used a named workspace or a definition file, it is reloaded as before.
The advantage of
Restart
over
Clearws
is that the user can start the application again using
Restart
and possibly trigger a
Latent
expression in the starting workspace.
When
break debug mode
is initiated by means of the
Break
expression or the operation
breakin
(or by using
<Ctrl b>
while awaiting input in window mode on a console version), a break loop is initiated with output like:
--------------------------------------------------------------
Break debug loop: enter debug commands, expressions or
type: resume to exit debug loop
<Return> executes the indicated debug command
current call stack :
foo
-------------------------------------------------------------
?.. C := A + ( + A + A )
-->[stepv]
In the above example the break loop awaits input with the default command
stepv
assumed. If
resume
is typed then the break loop is exited and control returns to the expression following the break point.
Break
--------------------------------------------------------------
Break debug loop: enter debug commands, expressions or
type: resume to exit debug loop
<Return> executes the indicated debug command
current call stack :
-------------------------------------------------------------
?.. C := A + ( + A + A )
-->[stepv] resume
The operation
reverse
returns an array of the same shape as
A
having the items in reverse order.
reverse 4 5 6 7
7 6 5 4
reverse 'able was I ere I saw Melba'
ableM was I ere I saw elba
EACH reverse 'This' 'seems' 'too' 'wonderful'
+----+-----+---+---------+
|sihT|smees|oot|lufrednow|
+----+-----+---+---------+
reverse count 3 4
+---+---+---+---+
|3 4|3 3|3 2|3 1|
+---+---+---+---+
|2 4|2 3|2 2|2 1|
+---+---+---+---+
|1 4|1 3|1 2|1 1|
+---+---+---+---+
reverse (10 take) reverse sketch 1.23
1.23
The fourth example illustrates that the reverse of a 3 by 4 table is a 3 by 4 table with the list of items reversed. The last example shows the use of
take
and
reverse
to right-justify text in a field.
 The possible roles are: reserved word, variable, expression, operation or transformer. An identifier that corresponds to a reserved word can play no other role in a workspace. An identifier that corresponds to a predefined object in Nial cannot be changed in the global environment, but can take on a different object association in a local environment. In the global environment, once the role of an identifier is established, it must keep the same role, but may have its association changed to another object of the same role.
An external-declaration assigns a role to a name in the global environment so that the name can be used in other definitions before it is completely specified. This mechanism is useful for creating mutually recursive definitions. An external declaration is made only in the global environment.
If the name is already defined with the same role, the declaration has no effect. If the name has another role, a fault is reported.
If the expression on the right of
IS
in a definition uses the name being defined, the definition is assumed to be recursive. The name is assigned a role compatible with its use on the right if it does not already have a role.
If a definition appears within a block, the association between the name of the identifier and its meaning is made in the local environment. Otherwise, the association is made in the global environment and the definition assigns a role to the name as representing that kind of expression.
If the name being associated in a definition is already in use, the new definition must be for a construct of the same role and the earlier definition is replaced. The use of a defined name always refers to its most recent definition.
The operation
rotate
shifts the items of array
B
to the left
A
places, inserting the items that drop off the front of the list on the back. If
A
is a negative number, the shift is to the right.
The second example shows that on a table
rotate
works on the list of items in row major order.
 In this case, it returns a list of lists, with each item being a list of items from one row of the table. For a multidimensional array, the result is an array of lists of length equal to the length of the last axis of
A. The valence of the result is one less than the valence of
A.
A := 2 3 reshape "Bill "Tom "Mary "Jack "Dawn "Alice
Bill Tom Mary
Jack Dawn Alice
rows A
+-------------+---------------+
|Bill Tom Mary|Jack Dawn Alice|
+-------------+---------------+
A := 2 2 2 3 reshape count 32
1 2 3 7 8 9
4 5 6 10 11 12
13 14 15 19 20 21
16 17 18 22 23 24
rows A
+-----+--------+ +--------+--------+
|1 2 3|4 5 6 | |13 14 15|16 17 18|
+-----+--------+ +--------+--------+
|7 8 9|10 11 12| |19 20 21|22 23 24|
+-----+--------+ +--------+--------+
Definition
rows IS OPERATION A ( valence A - 1 max 0 raise A)
Equations
shape rows A = front shape A not empty A ==> shape first rows A = last shape A
not empty A ==> mix rows A = A
T a variable associated with a table ==> I pick rows T = T|[I,]
The operation
save
is used to save a workspace with the name given by the phrase or string
Wsname. The convention in Q’Nial is to name workspaces with the extension
.nws
so that they are easy to find in the directory. When saving or loading a workspace, the extension may be omitted.
The effect of saving a workspace is to place the contents of the current workspace in the named file in an internal format. The contents of the current workspace are unchanged by doing a
save.
save "mywork
A
save
operation may be executed within an operation or expression. The effect is to interrupt the execution and do the save at the top level. If the saved workspace contains a user-defined expression with the name
Checkpoint, at the termination of the
save, Checkpoint
is executed.
Checkpoint
can be used to restart the computation.
If the saved workspace contains an expression with the name
Latent, whenever the workspace is loaded, the
load
operation will execute the
Latent
expression. This can be used to have a workspace automatically begin an application, or to set up desired default values for various switch settings.
The operation
scan
translates Nial program text to internal form. This process is called tokenizing as it results in a list containing tokens. The argument
S
is a string. The result is a list beginning with 99 and followed by an alternating sequence of integer codes and phrases.
scan 'A := 3 * 5.2'
99 2 A 1 := 16 3 2 * 18 5.2
scan 'sum 2 (35 + 42.7)'
99 2 SUM 16 2 1 ( 16 35 2 + 18 42.7 1 )
scan 'foo IS first rest'
99 2 FOO 1 IS 2 FIRST 2 REST
The first example has 5 tokens, the second has 7 and the third has 4. In a token stream, the token for an identifier is in upper case.
The
scan
token codes are given below:
Code
Meaning
1
reserved word or delimiter
2
identifier
14
string
15
phrase
16
integer
18
real number
22
fault
42
atomic character
40
atomic boolean or bitstring
Equations
S a string ==> execute S = eval parse scan S
S a string ==> scan descan scan A = scan A
The use of an assign-expression indicates that a name (identifier) is to be treated as a variable in the context surrounding the assign-expression. This context is called the
scope
of the variable. The context may be global, in which case the variable may be visible at all levels; or it may be local to some region of program text. A local scope is created for the parameters of operation forms and for variables created within a block.
Because operation forms or blocks may appear within other operation forms or blocks, it is possible to have one scope for a name nested within another. A name is said to be visible at a point in a program text if it has a local meaning at that point or has a meaning in some surrounding scope or is a global name. When a name is used in a local scope, it is the local association in the innermost scope that is used, instead of an association with the same name in a surrounding scope.
A block is a scope-creating mechanism that permits an expression-sequence to be created so that it has local definitions and variables which are visible only inside the block. A block may appear as a primary-expression or as the body of an operation-form.
Definitions that appear within the block have local scope. That is, the definitions can be referenced only in the body of the block. Variables assigned within the block may or may not have local scope, depending on the appearance of a local and/or a nonlocal declaration. If there is no declaration, all assigned variables have local scope. Declaring some variables as local does not change the effect on undeclared variables that are used on the left of assignment. They are automatically localized.
If a nonlocal declaration is used, an assigned name that is on the nonlocal list is sought in surrounding scopes. If the name is not found, a variable is created in the global environment.
The operation
see
displays the definition of the user defined object named by the phrase or string
Defname. The canonical form of the definition appears directly on the screen. The operation cannot be used on predefined names. The display is in
sketch/nodecor
mode regardless of the current settings.
foo IS first rest;
see "foo
foo IS first rest
It is possible to capture the display given by
see
using the composition of operations
descan deparse getdef Defname, which returns the list of lines of the display. This technique is used in the operation
defedit.
Definition
see IS OPERATION A {
Settings gets EACH set "sketch "decor;
ITERATE writescreen descan deparse getdef A;
EACH set Settings; }
The operation
seed
sets the initial number for the random number generator used by
random. The argument
Num
is a positive decimal fraction less than 1.
Seed
is used to get predictable results from a program that uses
random.
Seed
returns the value that would have been used to generate the next random number.
The operation
seek
returns a pair of values. The first is a boolean indicating whether or not
A
is an item of
B. The second is the address of the first occurrence of
A
as an item of
B, searching
B
in row major order. If
A
does not occur in
B, the second item of the result is the gage of the shape of
B.
Seek
is the combination of
in
and
find
in one operation.
3 seek 56 34 3 23 57 3
l 2
`a seek 'hello world'
o 11
Seek
is useful when determining whether or not
A
is in
B
and, if it is, obtaining its position in
B. Both results are obtained in one internal computation rather than two separate ones.
Definition
seek IS OPERATION A B { A [in,find] B }
Equations
first (A seek B) = A in B
second (A seek B) = A find B
Pragmatics
The operation
seek
uses a linear search on the items of
B
if the array has not been sorted, or uses a binary search algorithm if it has. The latter fact suggests that an array that is searched frequently should be kept in lexicographical order by applying
sortup
to it when it is created or changed.
The operation
seeprimcalls
sets an internal flag to the setting of the boolean argument
Mode. If
Mode
is
true, subsequent execution will monitor the use of all primitive defined expressions, operations and transformers and will print out a message indicating when the named primitive has completed execution. If
Mode
is
false
it turns off the flag that controls monitoring.
This mode of execution is useful for following the execution flow during debugging.
The operation
seeusercalls
sets an internal flag to the setting of the boolean argument
Mod. If
Mod
is
true, subsequent execution will monitor the use of all user defined expressions, operations and transformers and will print out a message indicating when definitions are entered and executed. If
Mod
is
false
it turns off the flag that controls monitoring.
This mode of execution is useful for following the execution flow during debugging.
The expression
Separator
returns the character used to separate names in the directory structure for the operating system. For DOS and Windows systems, it is the backslash symbol "\". For Unix, it is the slash symbol "/".
The operation
set
changes internal switches in the Q’Nial interpreter. The switches are used to control the behaviour of a variety of optional features of Q’Nial. The argument
Sw
is a string or phrase in either case. The result of set is a phrase giving the setting of the switch as it was prior to the execution of the set operation. The result can be used subsequently to restore the switch to its original value. The valid phrases and their purposes are tabulated below:
Setting
Description of Result
diagram
set default display mode to diagram
sketch
set default display mode to sketch
decor
turn on decoration of atoms and empty arrays
nodecor
turn off decoration of atoms and empty arrays
trace
turn on tracing of expressions at the top level
notrace
turn off tracing of expressions at the top level
log
turn on the automatic logging of the session
nolog
turn off the automatic logging of the session
set "diagram ;
displayinsketchmode IS OPERATION Result {
A := set "sketch; write picture Result; set A ; }
In the above definition, sketch mode is set at the beginning, saving the previous setting in the variable
A. When the definition is ended, the mode is reset to the value in
A.
The default settings are
sketch, nodecor, notrace
and
nolog.
The operation
setdeftrace
changes the trace mode for the user defined expression, operation or transformer definition named by the string or phrase
Defname.
Mode
is an optional argument. If it is omitted, the trace mode is toggled. If
Mode
is present and has the value 1, it turns trace on.  The effect when the trace mode is on is to trace the execution of the body of the operation whenever the operation is executed.
The trace mechanism shows intermediate values in the evaluation of expressions.
The operation returns the previous setting. If the result of
setdeftrace
is assigned to a variable, the previous setting can be restored later.
library "average
setdeftrace "average
0
average 3 4 5
...trace call to operation
...average
...the arguments for the opform are
...A
...A
3 4 5
...A
3 4 5
...sum A
12
...sum A / tally A
4.
...end of operation call
4.
The tracing facility for expressions does not trace all intermediate computations. Parentheses around an expression will force its result to be shown during a trace.
The operation
setformat
controls the format used for picturing real numbers.
String
is a format specification for real numbers using the conventions for the C library routine printf. There are three styles of format:
Control
Effect
%f
displays a fixed number of places after the decimal point in a fixed size space with no scaling of the number
%e
displays the number in scientific notation with an exponent scaling the number to have one digit before the decimal point
%g
displays the number in f format if possible but defaults to e format if the number is not within a suitable range
A code can be inserted between the percent sign and the letter. Format
'%15.5f'
uses a field of width 15 to display a number in
f
format with 5 decimal places. The first digit is the width of the field. For
f
format, the second digit gives the number of places after the decimal while for
e
and
g
it indicates the number of significant digits to be displayed. Either digit can be omitted.
Q’Nial requires that the display of a real number includes a decimal point and that the display of an integer does not include a decimal point. In a
%g
format, printf does not include a decimal point if the real number matches an integer to the specified precision. In this case, Q’Nial makes the field one space wider than that specified in order to accommodate the decimal point. If an
f
format is not wide enough to accommodate the number, it is widened so that the number is displayed.
If the result of an
f
format requires more than
40
digits to the left of the decimal point, it is converted to
e
format.
The format
'%.17g'
is used by operation display in depicting real numbers as this format is accurate enough to reproduce the same number when executed on most systems. The default format is
'%g'. It works as in C except that a period may be added to distinguish the result from an integer constant. If the argument to setformat is
''
then it resets the formatting to the default value.
The operation
setinterrupts
permits or prevents the interruption of computation by the use of
<Ctrl C>. If the argument is
true, interrupts are permitted. If it is
false, they are blocked. The default value is
true.
Setinterrupts
is used in a closed application to prevent the user from interfering with a computation in process. This may be critical if a file is being updated in a sequence of steps, which must all be completed for the data base to remain consistent.
The operation
setlogname
is used to set the name of the host file in which a log of the session is recorded. The current log file is closed and a new one opened. The argument
Filename
must be a phrase or a string.
setlogname "dec17pm
The main purpose of
setlogname
is to capture the history of part of a session in a particular file for later use. The log file can produce documentation, for example.
The operation
setprofile
is used to turn on or turn off the gathering of profiling statistics. The calls must be made at the same scope level in order for profiling to work correctly. This can either be at the top level, or within the body of some expression or operation.
The argument is a boolean value:
true
starts the profiling process and
false
stops it. Profiling can be turned on and off several times within one profiling session.
A detailed explanation of the profiling mechanism is given in the help entry on
profiling.
The operation
setprompt
sets the Q’Nial prompt to be the string or phrase S. The default prompt is the string with 5 blank spaces. The maximum size of a prompt is forty characters.
Setprompt
provides the facility to set a visible prompt which can be distinguished from prompts issued by other software.
The operation
settrigger
sets the action taken when a fault is generated in an operation. If
Switch
is
true, whenever a fault is generated, computation is interrupted, a message is displayed indicating the fault that has occurred and the expression where it occurred. Then the
Callstack
is displayed and a loop is entered that allows you to explore the cause of the fault. If you press
Return
at the prompt, control is sent to the top level loop.
If
Switch
is
false, fault triggering is turned off.
The default setting at the start of a session is fault triggering on. The triggering of an interrupt by fault generation can be turned off for the session by using the -s command line option in console versions or by setting preferences in the GUI version.
The operation
setwidth
sets the width of the display and log lines. The argument is an integer
N. The result is the previous setting. The default setting is 80.
Setwidth
is useful in controlling the format of display on output saved for documentation purposes. For example, a setting of 130 allows wide Nial diagrams to be printed on a line printer even though they may not display properly on the screen. Narrower settings are convenient for use in reports.
The operation
shape
returns an array that describes the rectangular structure of array
A. Every array has a shape. For a single, including atoms, the shape is the empty list
Null. For a list, it is a solitary holding the integer giving the length of the list. For a table or higher valence array, it is a list of integers giving the extent (number of items) along each axis.
shape 5 6 7
3
A := 2 3 reshape 4 5 6 7 8 9;
shape A
2 3
B := 2 3 4 5 0 2 reshape 2 ;
shape B
2 3 4 5 0 2
The operation
shape
always returns a list whereas
tally
returns an integer. An empty array such as
B
is one with a zero in its shape.
Equations
product shape A = tally A
tally shape A = valence A
list shape A = shape A
shape A reshape list A = A
The operation
simple
tests whether or not an array has all atomic items or is an empty array. The result is
true
if the array is simple and
false
if it is not.
(simple 2 3 4) (simple "abc)
ll
simple Null
l
simple (2 3) (4 5 6)
o
(simple tell 2 3)
o
The first two examples show that a list of integers, an atom and the empty list
Null
are all simple arrays. The next two examples show that a pair of lists and the result of a
tell
are not simple.
The operation
single
returns an array with no axes holding
A
as its only item. The result is an array with shape
Null
and is said to be a
single
.
single 2 3
o---+
|2 3|
+---+
3 = single 3
l
The first example illustrates that a single of a pair contains the pair as its item. In both
diagram
and
sketch
modes, the display of a nonatomic single is decorated with an
o
in the upper left corner. The second example illustrates that the single of an atom is the atom itself.
 The details of the display of atoms and empty arrays is affected by the setting of the
decor/nodecor
switch.
sketch tell 1 3
The sketch of an array provides an abbreviated display that often serves as an adequate output format for data.
Definition
sketch IS OPERATION A {
Old_setting := set "sketch;
Result := picture A;
set Old_setting;
Result }
The operation
solitary
returns a list of length one holding
A
as its only item. The result is said to be a
solitary
.
solitary 2 3
+---+
|2 3|
+---+
set "diagram; solitary 3
+-+
|3|
+-+
set "sketch; solitary 3
3
The first example illustrates that a solitary of a pair contains the pair as its item. The second example illustrates that the solitary of an atom is different from the atom itself. However, as the third example shows, in sketch mode, the display of the solitary of an atom is not framed and hence may have the same display as the atom.
Definition
solitary IS OPERATION A { A hitch Null }
Equations
first solitary A = A
solitary A = A hitch Null
solitary A = Null append A
solitary A = list solitary A
The operation
solve
solves the set of linear equations described by the equation
A x = B
in matrix notation, where
A
is an
N
by
N
matrix and
B
is a vector of length
N. Provided that the matrix
A
is not singular, the result is the vector of length
N
satisfying the equation (within roundoff error).
In the computation, a numerical estimate is made of the singularity of
A. If
A
cannot be shown to be non-singular with a safe margin, the result of the operation is the fault
?singular.
The operation is extended to solve more than one right hand side. If
B
is an
N
by
M
matrix, columns of
N
are viewed as right hand sides and the result is an
N
by
M
matrix with each column being the solution of the corresponding column.
seed .314159;
A := ceiling (100. * (3 3 reshape random 9))
8 75 24
63 31 99
20 95 48
B := ceiling (10. * random 3)
4 9 5
X := solve A B
0.787745 0.111836 -0.445402
A innerproduct X
4. 9. 5.
I := 0 1 2 OUTER = 0 1 2 * 1.0
1. 0. 0.
0. 1. 0.
0. 0. 1.
A solve I
0.614865 0.102516 -0.518872
0.0810811 0.00745573 -0.055918
-0.416667 -0.0574713 0.347701
inverse A
0.614865 0.102516 -0.518872
0.0810811 0.00745573 -0.055918
-0.416667 -0.0574713 0.347701
A solve I = inverse A
l
The final example shows that if the right hand side is set to the unit matrix of size
N, solving it with
A
is equivalent to computing the inverse.
For large examples, it is always faster and more accurate to solve a system of equations directly using
A solve B
than to compute the inverse and do the inner product operation
inverse A innerproduct B
. The first form does about half as many arithmetic steps.
The transformer
SORT
returns the list of items of array
A
ordered according to the comparator
f. The comparator
f
is the operation to be used in comparisons. The operations
<=
and
>=
are the usual comparators. If
>=
is the comparator in
SORT, the items are returned in decreasing order. If
<=
is the comparator in
SORT, the items are returned in increasing order.

The operation
split
restructures array
A
by partitioning the items of
A
into arrays using the given axis numbers in
I
to determine the partition. The axis numbers in
I
indicate the axes that are to become axes of the items. The remaining axes are axes of the result. The argument
I
must be an array of integers in
tell valence A
without duplicates.
The first example shows that splitting along the last axis of an array is equivalent to taking its
rows. The second example shows that the
split
of the middle axis of a trivalent array is a table, with its shape given by the first and third axes. The last two examples illustrate two axes being "pushed down". The result is a list with tables as items. The order of the axes in the item is determined by the order of the items in
I.
The operation
blend
uses the same control argument to undo the effect of a
split. A common requirement is to partition an array along its axes, apply some operation
f
to the resulting items and to rebuild the result into its original form. Partition is used to do this without computing all the intermediate structures.
Definition
split IS OPERATION I A {
IF empty A THEN
fault '?empty right arg in split'
ELSEIF not(I allin axes A and diverse I) THEN
fault '?invalid left arg in split'
ELSE
J gets axes A except link I;
tally J raise (J link I fuse A)
ENDIF }
Equations
valence A ~= 0 ==> valence A - 1 split A = rows A
diverse I and (I allin tell valence A) ==> shape first (I split A) = list (I choose shape A)
diverse I and (I allin tell valence A) ==> shape (I split A) = shape A except (I choose shape A)
Q’Nial has a large set of predefined expressions, operations and transformers. Most of these are implemented directly in the interpreter. However, there are a number of them which are defined as standard definitions in the file
defs.ndf. This file is placed in the
initial
subdirectory of nialroot that is used in initializing Q’Nial. The definitions are embedded in the interpreter as a default set of standard definitions in case the interpreter cannot find the file.
When Q’Nial is invoked and the clear workspace is not found, the workspace is initialized with the standard definitions from
defs.ndf. All definitions included in this stage cannot be modified or erased and the operation
see
does not display their text. A user can create their own initial set of definitions by editing
defs.ndf
to add or remove definitions.
The expression
status
provides seven integers indicating the use of memory by Q’Nial. In order of display, the items are as follows:
Index
Quantity
0
Number of free words in the workspace
1
Number of words in the largest free block
2
Number of free blocks
3
Total number of words in workspace
4
Stack size in words
5
Atom table size in words
6
Internal buffer size
The first entry is an indication of how full the workspace is when compared with the fourth entry. A word can contain an integer, a reference to an array item, or 4 characters. The second entry gives an upper limit to the number of integers that can be in a largest size array. The third item is a measure of the fragmentation of memory. The number of free words divided by the number of free blocks gives the average block size. The fourth item gives the current size of the workspace. It can grow provided the system has space available. The remaining entries give the sizes of internal areas that can grow as necessary.
The starting workspace size can be specified as a parameter to the
nial
command that starts a session in console versions or in a dialog box in a GUI version. The workspace and the other areas can grow in size provided there is sufficient space and workspace growth is allowed (the default).
In Q’Nial for Windows the status entries can be displayed in a pop up window by selecting the Status field in the Workspace menu.
The command
step
is used in debugging a definition that has been suspended using Break or <Ctrl B>. The effect of
step
is to execute the next expression in an expression sequence and to suspend execution again. If the current expression involves a call on a defined operation or expression, execution is suspended on the first expression in its body. If the current expression is the last one in the expression sequence where the break began, the effect is the same as using
resume.
The related command
stepv
displays the result of the expression executed on a step before displaying the next expression.
The command
stepin
is used in debugging a definition that has been suspended using Break or <Ctrl B>. The effect of
stepin
is to execute the next expression in an expression sequence tracing the intermediate values generated and to suspend execution again. If the current expression involves a call on a defined operation or expression, execution is suspended on the first expression in its body. If the current expression is the last one in the expression sequence where the break began, the effect is the same as using
resume.
A primary-sequence of length two or greater is called a
strand
. The value of a strand is a list of values. Each item of the list has the value of the primary-expression in the corresponding position in the primary-sequence.
The elements of the primary-sequence can be constants, parenthesized expressions or lists in bracket-comma notation.
The operation
string
is used to convert an atom to a string that corresponds to the display of the atom. The form of the string is not sensitive to the setting of the
decor/nodecor
display mode, returning the sketch of the undecorated atom. The string of a string is the string itself.
The operation
string_split
is used to break string
S
into substrings using the characters in
C
as places to break the string. If two characters in
C
are adjacent in
S
then an empty string is placed in the result. The optional third argument is an integer which limits the number of substrings returned.
string_split ' .' 'The quick brown fox.'
+---+-----+-----+---+
|The|quick|brown|fox|
+---+-----+-----+---+
string_split ' .,' 'The quick, brown fox.'
+---+-----++-----+---+
|The|quick||brown|fox|
+---+-----++-----+---+
The first example shows a string being broken on spaces and the period. The second is also broken on a comma and results in an empty string as one of the items of the result.
The operation
string_translate
is used to translate characters in string
S
based on mapping characters in
C
to the corresponding ones in
D. The optional control argument
O
is a string or phrase where
'd'
deletes the characters in
C, 'c'
complements characters by replacing characters not in
C
by the last character in
D, and
's'
translates characters in
C
to those in
D
but also squeezes many occurrences to one.
string_translate 'abc' 'def' 'hello able cats'
hello dele fdts
string_translate ' ' '_' 'a whole lot of loving' 's'
a_whole_lot_of_loving
string_translate 'xz' '' 'extraneous zeal' 'd'
etraneous eal
string_translate 'xz' 'b' 'extraneous zeal' 'c'
bxbbbbbbbbbzbbb
The first example illustrates the translation of characters directly. The second shows translation with squeezing. The third example shows the use the
'd'
option to delete characters. Example four shows the use of the complement option.
The operation
sublist
returns a list of items of
B
chosen according to the list of booleans given in
A, selecting those items of
B
where the corresponding item of
A
is
true. If the tally of
A
is not the same as the tally of
B, it is coerced to have the same tally as
B
using
reshape. If
B
is not a list, the result is the same as applying
sublist
to the list of
B. The tally of the result is the sum of
A
after it has been extended, if necessary.
The first example shows the use of
sublist
to select the items of the list that are even numbers. The second example uses the left argument cyclically.
Equations
shape (A sublist B) = sum (tally B reshape A)
list (A sublist B) = A sublist B
A sublist list B = A sublist B
l sublist A = list A
o sublist A = Null
The operation
sum
adds the items of a simple array of numbers, reducing them to a single number that is their sum. The type of the result is the highest type of the items. The operation is extended to non-simple arrays by the multi pervasive mechanism. For an empty simple array, the result is 0. The symbol
sum
restricted to use on pairs.
The first example shows the result of
sum
on all combinations of types of atoms. The second last example shows that pervasive extension of
sum
adds a list of triples in an item by item fashion. The last example shows that the sum of an empty array is 0.
Equations
A sum B = B sum A
sum single A = EACH (sum single) A
sum EACH sum A =f= sum link A
tally link A = sum EACH tally A
The operation
symbols
is used to get information on the use of names in the workspace. If
Sw
is 0, the result is a list of pairs giving the names and roles of all the user defined names. If
Sw
is 1, the result is the similar list for both system and user names. The roles and their meaning are as follows:
Role
Meaning
ident
identifier
var
variable
expr
expression
op
operation
tr
transformer
res
reserved word
A := count 5;
foo IS first rest
symbols 0
+-----+------+
|A var|FOO op|
+-----+------+
The operation
take
selects a number of items from
B
as indicated by
A.
If
B
is a list and
A
is a non-negative integer, the result is the list formed from taking
A
items from the front of
B. If
A
is negative, it takes items from the right end of
B.
If
B
is a table and
A
is a pair of non-negative integers, the result is the table formed by taking the number of rows and columns indicated by
A
from the upper left corner of
B. If one or both of the items of
A
are negative, the items are taken from the other end of the axis.
For higher dimensional arrays
B, the tally of
A
must equal the valence of
B
and the result is obtained by taking from the front or back of the extents along each axis according to the sign of the corresponding item of
A.
In all of the above cases, if an item of
abs A
is longer than the extent along the corresponding axis in
B, the corresponding positions in the result are filled with the type of the first item of
B. If
B
is empty then the result is filled with the fault value
?fill.
If
B
is a single, the result is an array of shape
abs A, with all its items equal to the item of
B.
The last example shows that taking from a single repeats the item of the single in every item of the result.
Equations
shape A take A = A
tally A = valence B and (and EACH isinteger A) ==> shape (A take B) = list abs A
valence B = 0 and (and EACH isinteger A) ==> A take B = abs A reshape B
The operation
takeright
is an obsolete operation that takes items from the ends of extents. It is provided to retain compatibility with earlier versions of Q’Nial.
The operation
tally
returns an integer indicating the number of items of the array at the first level of nesting. This is called the tally of the array. The tally of a single is 1. The tally of a list is the number of items in it. The tally of a table is the product of the number of rows and columns. In general, the tally of an array is the product of its shape.
The operation
tanh
implements the hyperbolic tangent function of mathematics. It produces the following results when applied to atoms of the six types:
The transformer
TEAM
applies the operations of atlas
f
to corresponding items in the list of
A. There must be the same number of items in
A
as there are operations in the atlas
f. The result has the shape of
A. If
f
is an operation that is not an atlas,
TEAM f A
is the application of
f
to
A.
TEAM [+,*,max,min] (3 2)(4 5)(2 3 4)(2 3 4)
5 20 4 2
TEAM [+,*,max,min] count 2 2
2 2
2 2
TEAM second count 5
2
The operation
tell
is used to generate an array of addresses from a shape. For a non-negative integer
N, it generates the list of integers from 0 to
N - 1. For a list of non-negative integers,
tell
generates the array of all combinations of tell applied to the items of the list. If
list A
is not a shape, the result is the fault
?shape.
The examples show
tell
being applied to a shape and returning the array of addresses.
Equations
tell shape A choose A = A
isinteger A or isshape A ==> tell A = cart EACH tell A
N an integer and (N >= 0) ==> tell (N + 1) = (tell N append N)
tell Null = single Null
tell 0 = Null
tell 1 = solitary 0
tell solitary 1 = solitary solitary 0
The operation
third
returns the third of the items of
A.
Third
is defined in terms of
pick
and its behaviour is affected by that of
pick. The third of a triple is its last item. The third of a table is the third item in the list of items in row major order. The third of an array with two or fewer items is the fault
?address.
The expression
Time
returns a real number giving the time in seconds spent in executing Q’Nial since the beginning of the session. On systems permitting multi-processing, the time represents central processor time in seconds.
The expression
Time
is useful for estimating the relative costs of Q’Nial operations in terms of processor time.
The operation
times
is the same as
product
(and synonym
*) except that it enforces the rule that it must be applied to a pair.
Product
is multi pervasive and can add up any number of items.
7 times 9
63
(2 3 4) times (12 22 33)
24 66 132
times 2 3 4
?times expects a pair
The expression
Timestamp
gives the current date and time in the standard format for the host system. The details of this expression are implementation dependent.
The result is reported as a string giving the date and time.
Timestamp
is useful for dating reports and messages.
The command
toend
is used in debugging a definition that has been suspended using Break or <Ctrl B>. The effect of
toend
is to execute all the expressions to the end of the current loop or the end of the definition. If there is a loop then execution suspends on the expression following the loop. If the
toend
is issued when not in a loop, execution suspends on the expression being returned from the definition.
The operation
tolower
is used to ensure that a string has all its letters in lower case. Applied to a string
A, it results in a string with all the upper case letters converted to lower case leaving all other characters unchanged. It can also be used on a single character.
tolower 'abcDE*?12xyZ'
abcde*?12xyz
tolower 'STATEMENT IN LOWER CASE'
statement in lower case
Nial program fragments are entered during interactive input with a process called the
top level loop
; or brought into the system under the control of a systems operation,
loaddefs. This systems operation has the effect of loading a sequence of program fragments from a file as though the fragments had been entered interactively in the order they appear in the file.
The global environment is the collection of associations between names and meanings that are known at the top level loop. Such names have global scope in that they can be referenced by any program text. All other names have a local scope that associates a meaning with the name only during execution of a specific portion of a program text.
In direct input at the top level loop, a remark ends at the end of the line unless a backslash symbol ( \ ) is used to extend the line. In a definition file, a remark ends at the first blank line. A remark cannot appear within a definition or expression-sequence.
The expressions in an expression-sequence are evaluated in left-to-right order. If the sequence does not terminate with a semicolon, the array returned is the result of the last expression. If the sequence does end with a semicolon, the array returned is the fault
?noexpr. At the top level loop, if the array returned is the fault
?noexpr, it is not displayed.
In window mode for a console version of Q’Nial when a window is used interactively from the top level loop, the terminal acts as though it has a screen the size of the window. In particular, as the cursor attempts to move below the bottom line of the window, the text is scrolled one line at a time. The speed of scrolling can be changed using the operation
setscroll.
The operation
topic
uses the Q’Nial Help Facility for console versions to display help on the subject given as the argument to the operation.
Name
should be a phrase or string naming a particular subject.
The expression
Toplevel
ends the execution of the definition currently being executed and returns control to the top level loop where an operation named
recover
is executed if it is present in the workspace.
The major purpose for
Toplevel
is to handle an exception condition within a definition by forcing the definition to terminate. Use of a
recover
operation makes it possible to recover gracefully in such cases.
The operation
toraw
converts the simple array
A
of real number, integers, or characters to a boolean array corresponding to the internal bit pattern for the data.
The operation
toupper
is used to ensure that a string has all its letters in upper case. Applied to a string
A, it results in a string with all the lower case letters converted to upper case leaving all other characters unchanged. It can also be used on a single character.
toupper 'abcDE*?12xyZ'
ABCDE*?12XYZ
toupper 'a statement in upper case'
A STATEMENT IN UPPER CASE
A
transformer
is a functional object that is used to construct a new operation from a given operation argument, usually producing a modified version of the given operation. Most transformers used in Nial are provided in the core language. However, there is a mechanism that constructs a named transformer in terms of one or more operation parameters. A user-defined transformer describes the modified operation as a parameterized algorithm for manipulating data.
A transformer usually specifies a general algorithm which can have an operation as a parameter. For example, the
EACH
family of transformers generalizes a number of looping mechanisms for applying an operation to items of arrays.
A user-defined transformer could provide the skeleton for processing the records of a file and allow an arbitrary operation to be applied to each record. Such a transformer is often called a filter.
The process of evaluating an operation call of an operation modified by a transformer requires two steps. The modified operation is formed; and then the modified operation is given the array argument which it uses to produce the result.
TWICE is TRANSFORMER f (f f)
TWICE rest 4 5 6 7 8
6 7 8
A
transformer-form
is the syntactic structure used to describe a transformer in terms of an operation expression involving formal operation parameters. The names that follow the keyword transformer in the transformer-form are called formal operation parameters. The body of a transformer-form is the operation-expression which uses these names. The first rule requires that the operation-expression be an operation-form; the second allows any operation- expression to be used.
The effect of applying a transformer-form to an operation-expression is the effect of an operation formed in the body of the transformer, such that wherever one of the formal operation parameters occurs, it is replaced with the corresponding argument operation-expression.
On the other hand, if the formal operation parameters consist of only one name, the operation formed is associated with the argument operation-expression. If the operation formed has two or more names, the operation-expression must denote an atlas of the same length; and the formal operation parameters are associated with the operations of the atlas in their sequence.
The associations are made with the argument operation-expression in the environment where the transformer is applied. If there is a mismatch between the number of formal operation parameters and the argument, the result of applying the transform is the fault
?tr_parameter.
The operation
transpose
is used to reverse the axes of a table or array of higher valence.  It is the result of comparing two identical arrays for equality.
Definition
True IS (0 equal 0)
Equations
tally True = 1
shape True = Null
single True = True
not True = False
max True = True
abs True = 1
The transformer
TWIG
transforms an operation
f
into an operation that applies
f
to every simple array in the nested structure of
A. The resulting operation is called the
TWIG
transform of
f. The result of applying the
TWIG
transform of
f
to
A
has the same shape as
A. If
f
maps simple arrays to simple arrays of the same shape, the result has the same structure as
A.
The first example shows that the result of applying a
TWIG
transform to a table is a table of the same shape. The items of
T1
are simple and hence have been mapped by tally to 2. In the second example, the structure of the result is not preserved because
tell
maps a pair to a table of pairs.
 It is extended to arbitrary arrays by being unary pervasive.
Atomic type
Representative atom
boolean
o
integer
0
real
0.
character
<blank>
phrase
"
fault
?
set "decor;
type l 3 3.3 `3 '3.3' "33
+-+-+--+--+-----+--+
|o|0|0.|` |' '|""|
+-+-+--+--+-----+--+
The operation
type
is provided as a computational way of transforming an array to a standard value, while preserving structure and type information. Executing a type test predicate,
isboolean, isinteger
, etc., is equivalent to testing that the type of an array is equal to the corresponding representative atom.
A
unary pervasive
operation maps an array to another array with identical structure, mapping each atom by the function's behaviour on atoms. All of the scientific operations and the unary operations of arithmetic and logic are unary pervasive.
The scientific operations are implemented using the library routines provided with the C compiler used to construct Q’Nial. The accuracy of the result is determined by the precision of the floating point number system of the computer and the accuracy of the library routine approximation.
The following table describes the unary pervasive operations:
The operation
up
is used to do a lexicographic comparison of two arrays returning
true
if
A
is lexicographically less than or equal to
B
and
false
otherwise. If
A
and
B
are atoms of the same type, then their values are compared using
<=, otherwise
A
is viewed as less than
B
if
A
has a lower type than
B. The types are ordered lowest to highest by
boolean, integer, real, character, phrase
, and
fault.
If one of
A
and
B
is not atomic then the list of items of
A
are compared lexicographically to those of
B. The comparison is based on the first position where the items differ, and the result is the lexicographic comparison of the two items. If the lists of items agree up to the point where one is exhausted, then the array with the shorter list precedes the longer one.
If
A
and
B
are arrays with the same list of items, then the comparison is decided by comparing their shapes lexicographically.
2 up 1.5
l
[2] up 3
1
5 up 3 4 5
o
tell 10 up tell 2 3
l
(2 2 reshape 'abcd') up (1 4 reshape 'abcd')
o
The first example indicates that 2 precedes 1.5 because the two atoms are of different type and type integer precedes type real. The second example is
true
because the lists of items differ in the first item, and 2 precedes 3. In the third example the lists of items differ in the first item and 5 does not precede 3. In the fourth example, the arrays differ in the first item and the
up
comparison of 0 and 0 0 is
true
because they agree in the first item and 0 is shorter. In the final example, the result is
false
because the items are the same and the
up
comparison of 2 2 and 1 4 is
false.
Equations
(A up B) and (A ~= B) ==> not (B up A)
A = B ==> A up B
(A up B) and (B up A) <==> A = B
The operation
update
provides the semantics of the
Nm@I := A
form of assignment expression.
Nm
must be an existing variable represented by a string, phrase or a cast.
I
is the address of the location to be updated.
A
is the array to be placed in the variable.
The major purpose of
update
is to allow a selective update at one location in the array associated with a global variable without forcing a copy. By passing the name of the variable to the operation that is using
update, rather than its value, no sharing of the internal data is made and hence the update can be done in place.
The operation
updateall
provides the semantics of the
Nm#I := A
form of assignment expression.
Nm
must be an existing variable represented by a string, phrase or a cast.
I
is the array of addresses of the locations to be updated.
A
is the array of values to be placed in the variable.
The major purpose of
updateall
is to allow a selective update at several locations in the array associated with a global variable without forcing a copy. By passing the name of the variable to the operation that is using
update, rather than its value, no sharing of the internal data is made, and hence the update can be done in place.
The Nial Tools software packages for Windows and for Unix provide a capability to extend the Q’Nial interpreters provided with each package by adding new user written primitives corresponding to expressions, operations and transformers. The new routines are written in "C" to meet the abstract machine interface for the Q’Nial interpreter.
This requires that the licensee have a C compiler that can produce object modules compatible with the object files provided with the package. The extended interpreter is built by linking the user written routines with object files.
This approach to extending Nial is appropriate if the new primitive is called frequently and one wants to avoid the overhead of using a DLL interface (under Windows) or the
callc
operation (under Unix).
The operation
vacate
is a renaming of the operation
(0 reshape). It is used in Version 4 of Array Theory to produce an empty array with the same prototype as its argument. In Version 6 of Array Theory it always returns
Null.
The operation
value
provides the semantics of
value of a variable
that is implicit whenever a variable is used in a value context. The argument
Nm
is either a string or phrase denoting a variable or it is the cast of a variable. The result is the value of the variable. If
Nm
is a string or phrase, the value is sought in the environment at the point of application of
value. If
Nm
is a cast, the value is sought in the environment where the cast was created. The major use of
value
is in conjunction with
assign
which allows arbitrary variables to be stored and later retrieved using
value.
A variable is given an association with an array value by its use on the left side of an assign-expression, its appearance in a local or nonlocal declaration, its designation as a variable in an external-declaration or its use as the first argument of the operation assign.
When a variable is used as a primary-expression, its meaning is the array value associated with the identifier. If the variable exists but has not been assigned, it will have as its default value the fault
?no_value. If an identifier is mentioned as a primary-expression but has not yet been given an association, a parse error will occur with the fault
?undefined identifier:.
Role of a Variable
A variable gives a name to the result of a computation. If the same result is needed later in the program, the named variable can be used, thereby avoiding the necessity of repeating the computation. A variable can be assigned different array values throughout the computation.
Although an identifier can be of any length up to 80 characters, a compromise is usually made between choosing explicit variable names and choosing brief names to avoid unnecessary typing. An identifier used as a variable cannot be a Q’Nial reserved word. In a local environment, a variable identifier can be chosen the same as a predefined or user-defined global definition name. Such a choice makes the global use of the name unavailable in the local context.
In any context, an identifier can name only one of: a variable, an array- expression, an operation-expression, or a transformer-expression. During one session, the role of a name, i.e. the class of syntactic object it names, cannot be changed.
Indexed Variable
An
indexed variable
is a variable for which a part of the associated array value is referenced. An
index
is the value of the primary-expression within an indexed-variable which specifies the location or locations of the part or parts of the array that are selected.
The operation
void
is an obsolete operation that has the same effect as the operation
(0 reshape). It is used in Version 4 of Array Theory to produce an empty array with A as its prototype. In Version 6 of Array Theory it always returns
Null.
The operation
watch
provides a means to watch when a variable has its value changed. The first argument to
watch
is a variable reference expressed as a cast. For a global variable
X, the cast is
!X; for a local variable
Y
in definition
G, the cast is
!G:Y.
The second argument is a string of program text to be executed when the variable is changed. If it is empty then it indicates that the watch should be removed. The program text can display the value being set, execute a break, or take some other action.
The result of
watch
is the variable reference and the previous watch expression if any. This can be used to restore the watch setting by storing it and using it as the argument to
watch
at a later time.
X gets count 5;
watch !x 'write ''X changed to: '' X'
+------------------++
|+---+------------+||
||100|2 7926 42354|||
|+---+------------+||
+------------------++
X gets 'abc'
+--------------+---+
|X changed to: |abc|
+--------------+---+
abc
X gets 3 4 5 6
+--------------+-------+
|X changed to: |3 4 5 6|
+--------------+-------+
3 4 5 6
foo is op A { B gets count A; reverse B }
watch !foo:b 'write ''B in foo changed to: '' B'
+--------------------------------------++
|+---+--------------------------------+||
||100|+--+------------+--------------+|||
|| ||50|2 7926 42470|23 7798 7798 B||||
|| |+--+------------+--------------+|||
|+---+--------------------------------+||
+--------------------------------------++
watchlist
+-----------------------------+----------------------------------------+
|+--+------------------------+|+------+-------------------------------+|
||!X|write 'X changed to: ' X|||!FOO:B|write 'B in foo changed to: ' B||
|+--+------------------------+|+------+-------------------------------+|
+-----------------------------+----------------------------------------+
watch !x ''
+------------------+------------------------+
|+---+------------+|write 'X changed to: ' X|
||100|2 7926 42354|| |
|+---+------------+| |
+------------------+------------------------+
watchlist
+----------------------------------------+
|+------+-------------------------------+|
||!FOO:B|write 'B in foo changed to: ' B||
|+------+-------------------------------+|
+----------------------------------------+
The operation
write
displays the picture of an array on the display screen. The result is the no-expression fault
?noexpr. The picture displayed depends on the settings of the
diagram/sketch
and
decor/nodecor
switches. The effect of
write
is the same as applying
writescreen
to the picture of the array.
The operation
writearray
writes array
A
to the direct access file designated by
F
at component number
N. If a component already exists at that component number and
A
can fit in the space taken by that component, the existing one is overwritten. If not, the new component is written at the end of the
.rec
file and the
.ndx
file is updated to indicate the starting point for the new component. The space used by the previous value is recorded for a future compression.
The second argument
N
can be a list of numbers, in which case the items of
A
are written to the components designated by the integers in
N. The file must be open for direct access. The other direct access file operations,
readrecord
and
writerecord, must not be used on a file that is created using
writearray. However,
eraserecord
and
filetally
are used for both kinds of direct access files.
After a
writearray
or
writerecord, if the total unused space is a significant amount of the space occupied by the
.rec
file, the file is compressed with the records being placed in order.
The content of an array component is placed in the file in a binary form independent of the workspace in which it was created. Data that is written to a direct access file, erased, and then read in again, may take up more space in the workspace after this process due to lack of sharing of internal representations.
The operation
writechars
writes
S
to the screen at the current cursor position, without supplying a trailing "new line" character. Thus, the cursor is left at the position just after the text.
S
may be a string, a phrase or a single character. The principal use of
writechars
is in applications where the display screen is managed as a fixed object and scrolling must be avoided.
The operation
writefield
is used to write a character string to a portion of an existing host file. The first argument is the file name, followed by the integer offset to the beginning of the field to be written and by the string to be written. The result is the
?noexpr
fault.
Example
writefield "phonefile 200 'Mike Smith 389-4444'
In the example,
writefield
overwrites the contents of file
phonefile
starting at position 200 by the string given as the third argument.
The operation
writefile
is used to write the character array
S
to the file designated by file designator
N. The file designator is an integer returned by
open. The file must have been opened for writing, appending or communication.
The argument
S
may be a character, a string, or an array of characters of any valence. If it is a character or string, a single record is written to the file. If it is a table, the rows of the table are written as records to the file. The appropriate end-of-line indication is added for each record written. If the argument
S
is empty, an empty record is written.
If
writefile
is successful, the result is the no-expression fault
?noexpr. If the argument is not a character, string or character table, the fault
?not text data
is returned.
If the optional third argument is present, it is used to indicate whether the end-of-line indication should be written. If the third argument is
o
or
0
the indication is omitted; if it is
l
or
1, the indication is placed in the file. This form of
writefile
is used in communications mode to send information to a device driver on the EXTDOS version.
The operation
writerecord
writes string
S
to the direct access file designated by
F
at component number
N.
If a component already exists at that component number and
S
can fit in the space taken by that component, the existing one is overwritten. If not, the new component is written at the end of the
.rec
file and the
.ndx
file is updated to indicate the starting point for the new component.
The second argument
N
can be a list of numbers, in which case the items of
S
must be strings and are written to the components designated by the integers in
N. The file must be open for direct access. The other direct access file operations,
readarray
and
writearray, must not be used on a file that is created using
writerecord. However,
eraserecord
and
filetally
are used for both kinds of direct access files.
The data written in the
.rec
file by
writerecord
does not contain any end-of-line indications unless they explicitly are part of the strings being written.
The operation
writescreen
is used to write character data to the display screen. The argument
S
may be a character, a string, or a character table. If it is a character or string, a single line is displayed. If it is a table, the rows of the table are written as lines. If the argument
S
is empty, an empty line is displayed. In window mode, the data is written within the bounds of the active window.
The result of the operation is the no-expression fault
?noexpr. If the argument is not a character, string or character table, the fault
?not text data
is returned.
After the text is displayed, the cursor is placed on the line following the last line written.
writescreen 'End of Manual' ;
The operation
writescreen
is similar to the operation
writefile. For most host systems,
writefile
may be used with the
standard output device
to get the same effect as a