This is generated from a plaintext file,  d4.doc.


Next |End http://www.d4maths.lowtech.org/mirage/install.htm http://www.d4maths.lowtech.org/dna.exe http://www.d4maths.lowtech.org/math2545.tgz (<-c) Tony Goddard, SHEFFIELD 2003 Info: +44 (0)7944 764312 E-mail: tony@lowtech.org D4 is a an array processing language based on APL, but with features from PERL and the C-shell found on UNIX systems. The interpretor runs on DOS/WINDOWS 95 and WINDOWS 98, and also on LINUX/X. A preliminary version ran on SUN/SPARC workstations. DNA.EXE has been downloaded on a variety of WINDOWS/NT style platforms in colleges and offices, and the system works. Some scripts which use #cmd "shell_command" constructions need adjusting for Windows/NT. For example, to create another terminal box, use the respective alias: WINDOWS_NT ntclone|#cmd "cmd /c start d4x v=14 test.afn ", ARGS WINDOWS xclone |#cmd"start \windows\desktop\d4x.pif" LINUX xclone |#cmd #deb "rxvt +sb -e sd4 v=3 tty=rxvt ", ARGS, " &"


Next | Back|Top|End On DOS/WINDOWS D4 comes in two versions: a 16-bit version for creating rescue disks and suchlike, and a 32-bit version for handling large arrays in virtual memory. The 16-bit version boots from floppy disks, and I have used it in Digital Art installations. The 16-bit version is normally D4T.EXE, and is compiled under Borland Turbo-C. D4X.EXE is a 32 bit version available for DOS/WINDOWS. This was compiled with DJGPP, the Gnu C-compiler adapted for DOS. To run under DOS it is necessary to obtain the DPMI extender csdpmi3b.zip from an ftp site. A good place to try for European residents is:- URL=ftp://sunsite.doc.ic.ac.uk/packages/simtelnet/gnu/djgpp/v2 The simplest way is to run D4 from a command line. Use the DOS prompt in MS-Windows, or run in a shell window under UNIX/LINUX. d4x file-name {options} .. or d4x for LINUX. The file should be a .D4 program file (test.afn, dsh.afn). The options consist of a list of name=value pairs or ordinary strings. Some names are reserved and using silly values for them will cause trouble. default S,s stack size 512 N,n names 512 W,w windows 50 F,f files 32 K,k if defined, startup in input mode. Don't try and load int.afn.. I,i if defined supress break-in checking. For use in batch files. The normal startup line is:- d4x filename {var=value} ... If the paramater is of the form name=value then the variable called 'name' is given its designated value. Otherwise the arguments are stored in variables $0 $1 $2 etc. $0 is the name of the .exe file, and $1 is the name of the .afn file, which is a D4 script. There is a default startup file, 'init.afn'. If this file is in the current working directory, it is used as the startup file. When the K= paramater is used, the program comes up with no prompt, and the user must type D4 commands to get the program to work. On UNIX systems some name=value pairs need to be enclosed in single quotes to prevent the shell from destroying the intended effect. For example, to pass the printer device to d4 use a line such as:- d4x '$LP_DEV=/dev/lp1' test.afn Under WINDOWS-95 and WINDOWS-98 the interpretor may be run by clicking on certain files when viewing with a browser. To do this it is necessary to make an entry of the table which associates file types with programs. It is also possible to create a desktop short-cut. All of these methods mean storing a command line somewhere. LINUX users will find a shell script called 'dsh' which starts off like a PERL script with the name of the interpretor following a #! at the beginning of the file. Avaiable only with the LINUX version are 'socket' functions for sending datagrams between processes. At the moment these functions are only adequate to implement 'dumb clients' and a single 'dumb server'. These functions have been put in to facilitate video-wall installations. Environment variables are passed to the program in a virtual array called $ENVP. Type $$ENVP in the editor to see the list of names.


Next | Back|Top|End D4 may be run in command mode. Commands may be either expressions, system commands or user defined functions. The simplest expressions involve operations on numeric or character data, in the form of vectors or matrices. The syntax of expressions is very simple:- E::= + | + E | E + E | L - +::= F | S | U Function or operator -::= ++ | -- Postfix operator F::= Primitive function (+ - * % Etc.) S System function ( #window, #files, #sed, Etc.). U User defined function. D::= N | T Data N Numeric Matrix or Vector T Text Any array of bytes E::= I Identifier E::= +E Monadic operator E::= E+E Dyadic operator E::= (E) Priority of evaluation J:: E | E: | :E Index operator E::= E[J] Indexing. E::= L<-E Assignment L::= I | I[J] L-Value I::= Identifier. An identifier consists of a sequence of upper case letters, digits, the underscore (_), dollar sign ($), and dot (.). The starting character must be either a letter, dollar, or underscore. The use of identifiers allows the combination of various data types without the overhead of creating files. Variables may also take names which are not recognised as identifiers. These variables cannot be used in expressions, but they may be used in cut and paste operations. These names can be passed from shell scripts. A numeric token is a sequence starting with a digit or sign and including an optional decimal point and an exponent string consisting the letter 'E' followed by a number which may have a sign (+ or -). Because the minus sign, '-', is used as an arithmetic operator the negative sign in numeric tokens is the underscore ,'_'. There is a reason for this: the parser recognises vectors as lists of numeric tokens separated by blanks. This means that the parser must be able to distinguish between minus ('-') as an operator and a minus sign ('_') as the sign of a numeric literal. Text literals consist of text embedded between double quotes ("). If the double quote is itself to be included in a string then it must be doubled. The expression """in quotes""" becomes "in quotes". This can be most annoying for users who wish to write scripts with many doublequote characters. There is no mechanism for string literals to contain control characters, tabs, or to extend over more than a single line in the script. It is however possible to initialise string literals to contain multiple line constants in the form of 'here documents' similar to those found in PERL or the UNIX shell. Statements consist of one or more expressions, separated by '&' or ';' symbols. A series of statements may be made into a function. Functions may have 0, 1 or 2 arguments, and local variables as required. This is specified in the first line of the function, which acts as a template. Functions are defined in ".afn" files. These are ASCII files, with function definitions seperated by blank lines. Functions may also be created or modified with the editor #sed. A typical command might be #sed"X:d;$xx;:e;:f". Examples: one parameter two parameters no parameters monadic dyadic niladic Z<-FACT N Z<-A PLUS B Z<-HELP |Factorial |Add two numbers Z<-#sed":r abc.hlp" ->(Z<-N=0)/0 Z<-A+B Z<-#sed":e" Z<-N*FACT N-1 A function definition is a line consisting of two parts: the function definition template, and a list of local variables. <Fd>::=<FT> LV_LST <FT>::= I<-I | I<-I I | I<-I I I <Lv_list>::= <empty> | ; I <Lv_list> Here <empty> stands for a null text string, that is to say a text string with possibly no characters at all. The symbol 'I' stands for an identifier as defined in the section on syntax. Functions can become quite long. Because of the total absence of control structures such as if, then, for, while etc. Long functions are best broken up, or even written in C and added to the binary. The only control structure within a function is the branch, or goto instruction. Many short loops can be coded by recursion. Define a function which calls itself. Most functions consist of sequences of statements and loops of the form:- I<-0 AA: ->(I=N)/BB #mkdir #deb D<-LST[I:] | Restore file system heirarchy I++ ->AA


Next | Back|Top|End The interpretor can read functions and data from scripts. The first parseable line in a file is taken to be the invocation of a startup function. Lines consisting of one or two upper case words will work. These functions use labels and sub-functions. Labels stand at the beginning of a line, and are separated from the statements in a line by a colon. Labels are named variables, and their value is equal to the number of the line on which they stand. By tradition in APL the function definition line is line zero, and the other lines are numbered starting from one. The expression '->0' or goto line zero is equivalent to return from the function. It is possible to chain together statements without writing functions. A text matrix can be treated as a set of APL statements, and run as a sub-program by means of an APL function which manages a line counter, and executes the statements in succession. Alternatively a table of statements may act as a sort of 'case' statement with one particular line selected to give an action. This is achieved by the primitive function 'x' (execute) or #do.


Next | Back|Top|End Traditional APL recognised a small number of fairly homogeneous data types. These were character data, integers and real numbers. D4 represents these in the machine with one, four (or two on 16-bit version) or eight bytes respectively. Given a symbol "A" the function #nc"A" gives the name class of the viriable as 0 for undefined and then 1,4,8 etc for the type of object. A character scalar represents a character with ascii value from 0 to 255. A character vector is any number of characters grouped into a string. A character matrix is a table, or array of characters with a certain number of rows and columns. Atomic data types (int, double, char) are grouped in only two possible ways. These are scalar, vector, and matrix. There is no difference between a scalar and a one element vector. The null object may have any type.


Next | Back|Top|End Run files are ASCII files which define functions and data. Workspaces are collections of functions and data objects. Every workspace contains some pre-defined objects which are created when the interpretor is invoked. The most important of these is the symbol table which contains the names of all other objects, and the data associated with an object. There are at least three pre-defined symbols: a stack, a window list, and a table of file descriptors. A list of the names these objects may be obtained by the name list function, '#nl'. Users should not attempt to modify the stack or other such objects. Further information is in APPENDIX C. Run files are ASCII files consisting of blocks of text, or paragraphs, separated by blank lines. A paragraph consists of a header line followed by zero or more non-blank lines. Perl users will understand that scripts are files divided into paragraphs with the '\n\n' separator. Formatting programs, including many mailing programs will will generally destroy the validity of a run file. When the interpretor reads any text file as a run-file it processes paragraphs and sometimes creates an object. Comment text is skipped. There are three types of object, with the type determined by the first line of the paragraph. The objects are data items, functions and startup text. Functions and text matrices have names. The startup text is simply a set of expressions on a single line. Comments can be either UNIX shell style comments or C++ comments. These are blocks of text where all lines start with '#' or '//'. On UNIX the first line of a file maybe a comment giving the name of the binary. Example: #!/usr/local/bin/d4x or #!/home/d4/cp/d4x. Object type Header Subsequent lines (1) function text <Fd> Function body (2) data text "I" Text matrix "I" >> string Here Document (3) startup text <Fd> none. old style #do Text on following line. A text matrix and also a function body is initialised by a sequence of non blank lines. If a text matrix needs to include blank lines it must be defined as a 'here document'. The header line consists of three tokens: "name_text" >> terminator. The name of the object is in double quotes, and the terminator is any string. The object is made up of subsequent lines of text up to but not including a single line containing the terminator.


Next | Back|Top|End Example: A run file to multiply two integers. "times.afn" contains four lines. TIMES;#quit Z<-TIMES Z<-$2*$3 The parser reads the paragraphs. The first line TIMES;#quit just contains two function calls. The first function, 'TIMES' is defined later in the script. The second is the built in function #quit which exits the program. The body of the function 'TIMES' simply returns the multiplication of the two strings $2, $3 which are arguements passed on the command line for the interpretor. The run file can be activated by typing the command:- d4x times.afn 2 3 6 d4x times.afn 8669133453942347 81668888774549265858 707998495821741647976208724707488726 In the above example the variables $0, $1, $2, $3 represent the positional paramaters of the command line just as in the UNIX shell. Since $0 is the program name and $1 is the name of the runfile arithmetic arguements start at $2, $3. It is also possible to use file redirection. TIMES;#quit Z<-TIMES $VA[0]<-($2*$3),#av 10 13 |Write product with end of line Z<-"" d4x times.afn 8669133453942347 81668888774549265858 > result.num type result.num 707998495821741647976208724707488726 File handle 0 is standard input for reading and standard output for writing. When using standard input it is necessary to set the shell to 'batch' mode. This is done by use of the command line parameter i=0. TIMES;#quit Z<-TIMES;$0;$1 Z<-$VA[0] |Read a line from standard input 0 r #split Z |Split into a pair of numbers $VA[0]<-($0*$1),#av 10 13 |Write to standard output Z<-"" |Do not echo the result of the function. type num.tmp 34555435 7723456 d4x times.afn i=0 < num.tmp 266887381783360


Next | Back|Top|End An ordinary tar file can be made into a self extracting script. Prefix a function whose name is a single upper case string to any UNIX tar file. The contents of the first component of the tarfile is simply a script which contains the definition of a function with that name. The body of the function can include instructions to unpack the tar file, and then run further scripts depending on the contents of the tar file, and the installation of software and data on the current computer. This can be useful for installing new software on the computer, or for exchanging encrypted data between host and client machines. There are several ways of creating workspaces or adding objects to workspaces. The most common ways are to run the D4 interpretor from DOS/WINDOWS or LINUX, or to use the #load or #copy functions when the interpretor is already running. The #sed function can also convert the text buffer into variables and functions. Use the ':f' exit. This means that functions could in theory be loaded in from embedded comments in other types of documents, for example HTML. The '#load' and '#copy' commands both take a file name. Both functions read objects into a workspace from an ASCII file. The '#load' function deletes all existing objects before reading in the runfile and then starts the interpretor with the startup text. The #copy function merely adds functions and variables to the workspace. Numeric data types are not supported by these functions. When the interpretor is run from the operating system (DOS or LINUX) the first paramater on the command line is taken as the run file. The effect of the DOS command 'd4\gc\d4x \d4\cp\test.afn' is equivalent to the D4 commands:- _UFILE<-$1<-"\d4\cp\test.afn" $0<-"\d4\gc\d4x" #copy _UFILE startup_text Runfiles without startup text are often given the file suffix '.d4f'. Runfiles and other files containing definitions can be prepared by using any editor or wordprocessor capable of saving ASCII text. The interpretor will correctly process files stored as either DOS or UNIX text files.


Next | Back|Top|End Arithmetic: Result <- op S #abs absolute value #atan angle between line y=0, x>0 and points y,x. ? Random X<- ?N is a random draw 0<=S < N #ln Log natural logarithm p power R<- pX (#exp X) exponential function. #sqrt root R<- #sqrt X same as #exp 0.5 * #ln X #circle +/ Sum R<- +/X sum vector, or columns of matrix */ Product R<- */X f/ Minimum R<- f/X Minimum of vector, or matrix c/ Maximum R<- c/X If X is a matrix, the operation is done for each column. To get the results for each row, use the transpose operation 'y' : R<- +/yX etc. Arithmetic: Result <- A op B + add These operations work on - subtract integer matrices or vectors. * multiply % divide f minimum floor c maximum cieling #mod modulus remainder #and and bitwise #or or < less > greater = equal also works on character objects != not equal o circular functions.


Next | Back|Top|End Arithmetic operations work on numeric vectors and matrices. APL and its dialects are not strongly typed languages. Declarations are generally not necessary. Data is defined by assignment, or via the side effects of functions. A vector is a set of numbers, arranged in order. The assignment A<-3 4 5 creates a vector with three elements. These can be written as A[0], A[1], A[2]. A vector with a single element is called a scalar. A matrix is a table of numbers with a specified number of rows and columns. If X is a numeric object then its size is given by the function 'r' standing for rank. In the example A<-3 4 5 the size of A, written rA is three. Notice that no space is necessary in the expression rA. Function symbols are either signs such as +,-, *,% etc, or lower case alphabetical letters. Variable names used in expressions cannot contain lower case letters. Arithmetic on vectors and matrices is carried out componentwise. In the case one of the arguments is a vector with a single element (scalar) then that scalar value is used for each component of the larger operand. For eaxample if A<-3 4 5, B<-7 4 1 then the arithmetic operations A+B, A*B, A+1, 2*B give the following results. A+B 10 8 6 A*B 21 16 5 A+1 4 5 6 2*B 14 8 2 If C is given the value C<-12 73 8 3 then the operations A+C and B+C give an errors. Arithmetic operations can only be used on vectors or tables of equal sizes, or between vectors/matrices and scalars. There is no priority of evaluation for arithmetic expressions. Each expression is evaluated from right to left, so that 3*4+5 gives the result 27 (3*9) rather than 17. Numbers are represented in the computer with several different formats depending on the implementation. These correspond to integer and double precision floating point values. Integers may be 16-bit integers (d4t) or 32-bit integers (d4x). Floating point values tend to be represented according to a common IEEE standard. The interpretor decides how numbers are stored depending on the existance of a decimal point. Small floating point numbers can be converted to integers by means of the function #av. Z<-"N" #av 3.412 gives 3. Older versions of APL had boolean values which were integers that were allowed to take the values 0 or 1. Sometimes implementors would try to store vectors and matrices of boolean values as packed bitmaps, but the tradeoff between space and time makes this an unrewarding excercise. Bitwise logical operations on integers are supported with #and and #or. 7 #and 6 gives 6. 12 #or 2 gives 14.


Next | Back|Top|End The operations +, -, * (multiply), % (divide), <, >, and #mod (modulus, or remainder) work on text vectors treated as digit strings. This means that the PC user can simulate calculations made in the 1950s and early sixties, and much more besides. The classical example of recursive functions is the factorial function n!= Not only can this be calculated to hundreds of digits, but the user can really see the improvement of divide and conquer algorithms over straightforward iteration.


Next | Back|Top|End APL provided primitive operators for common functions used in mathematics. The system functions #atan, #exp, #ln and #sqrt and #circle give most of these. The #circle function may be abbreviated by the single letter 'o'. Trignometric functions are given as dyadic functions where the first numeric arguement is a selector. Current values are:- Selector Function call result 0 R<- 0 o X floor (X) 1 R<- 1 o X sin(X) 2 R<- 2 o X cos(X) 3 R<- 3 o X tan(X) _1 R<- _1 o X arcsin(X) _2 R<- _2 o X arccos(X) _3 R<- _3 o X arctan(X) R<- #sqrt X square root of X R<- #ln X natural logarithm R<- #exp X exp(X) R<- #atan YX arctan2 (Y,X) with sign. The #atan function works on pairs of numbers. The result is only half the size of the arguement. It got added to facilitate the conversion of cartesian to polar coordinates. Note: Domain errors are trapped, but ignored. This means that calling a function such as #sqrt with a negative arguement will not stop the program, but there may not be an error message. These functions were all introduced to facilitate low resolution animations of dynamical systems where evaluation will take place at many points. Singular points will give random results but the show will go on. When in doubt operate the brain rather than the program.


Next | Back|Top|End Monadic: Result <- op X r Enquire shape. rX is the shape of X r: Shape of object, as a matrix. i Index generator iN numbers 0 to N-1 iN,M pairs (i,j) i: 0 to M-1, j: 0 to N-1 shape of result is (M*N),2 , Ravel Convert matrix to vector. y Transpose R<- y X Matrix transpose. m #phi R<- m X Flip columns #theta R<- #theta X Flip rows Dyadic: Result <- A op B r Shape X<-4 5 r 1 2 3 Make 4 x 5 matrix , Concatenate Z<-X,Y Join X and Y i Search IDX<- A i B Position of B in A t Take R<- (M,N) t X Make new object, M rows, N columns. d Drop R<-(M,N) d X Get rid of M rows and N columns of X. / Reduce R<-U/X Compress. Keep columns with U[j]>0 /: Reduce R<-U/:X compress rows. j #find R<- STR #find TEXT String search. R is a 0 1 vector. m #phi R<- N m X Rotate each row #theta R<- N #theta X Rotate each column. b #base R<- RADIX b TAB Similar to APL encode n #represent R<- RADIX n Z and decode. z format R<- FW z X Numeric -> ASCII R<- FW #fmt X FW = width, decimal places.


Next | Back|Top|End Postfix operators: R<-X++ X++ increment. X-- decrement. A[J]++ works on subscript vector J.


Next | Back|Top|End Unlike many other languages such as BASIC or C subscripts may be vectors or word lists. The square brackets '[' and ']' are used to indicate subscripting into any array, or table. Only one level of indexing is allowed. Subscripts are normally integers or integer vectors, but strings, or tables of strings may be used as subscripts for associative arrays. When subscripts are used with a table, or matrix a whole row is returned for each index element. All subscripting operations use the value 0 to index the first element of an array. Out of range subscripts do not generate errors. On the right hand side of an expression they give zeros or blanks, depending on the object type; for assignments (X[J]<-DATA) out of range subscripts act as no-ops. R<-V[I] Select elements with subscripts I R<-M[I] Select rows from matrix, columns from vector. V[I] <- DATA Fill positions in the list I from DATA. V[I:]<- DATA Fill rows I in vector as 1 row table. M[I] <- DATA Set rows in positions indexed by I. M[I]++, M[I]-- Increment or decrement elements indexed by I. R<-M[:J] Select columns of vector/matrix. V[:J]<-DATA Set columns of vector/matrix. The colon modifier is a compromise. Normally a table could be indexed by two sets as in TAB[ROWS:COLUMNS], but the parser cannot handle this. In particular, lists of files or directories may have only one row, so it is important to have an easy way of selecting all the elements of such tables. If a table has just one line it is treated as a vector. To force the interpretor to treat it as a one line table use the colon modifier on the rank function 'r'. To process all files in a directory use a construct such as:- FL <- #files DIR, "*.txt" ->(0=r,FL)/XX I<-0 & N<-1tr:FL | r:FL is the shape of FL as a table. AA: ->(I=N)/DONE X<-FUNCTION DIR, FL[I:] I<-I+1 & ->AA XX:


Next | Back|Top|End D4 makes use of associative arrays and lists. There is a list associated with each window. It's lines may be accessed by using $T[Range]. A variable 'NN' may become a symbol table, indexed by strings. Any occurence of NN[name] as an L-value sets the name in the table. The complete list of names is given by the function NAMES<- #use NN. The name list for an associative array can also be extracted with the '$' include command for the editor. R<- size #use NN Declare name as array[size] R<- #use NN List of names in use NN[str] <- val Set value val <- NN[str] Get value. Undefined returns "". When size is the pair (2,N) names are associated with values. When size is a number N, the table is used for storing word counts. The index set may be a matrix. NN[idx]++ Increment word counts. NN[idx] <- RHS Assign value to set of names. The index variable can be a matrix of names, with respective values taken from the RHS if it is a vector. To make a frequency count of the words in a file use a function such as the following:- D<-NFC F;AN;C.0;N;Z |Frequency count items in a list. AN<-#sstomat _CR,LOAD F 1200 #use "C.0" C.0[AN]++ | This does all the work. Z<-#sstomat #use C.0 N<-,C.0[Z] D<-Z," ",zyN D<-D[m #sort N] |Sort by frequency count There is also a virtual array associated with each open file. For file number N use the syntax $VA[N, {RP}] where RP is a record pointer. $VA[0], $VA[1] and $VA[2] have special meanings.


Next | Back|Top|End #goto ->EXPRESSION if the expression is an integer within the line range of a function, then goto that line. If the expression evaluates to an empty object, do nothing. Line numbers outside the range of a function cause a return. If EXPRESSION is a string, then do nothing if the string is empty, or else branch to the first label function which matches EXPRESSION. Labels in the function may be quoted strings followed by a colon. This serves as a sort of case statement. #do #do EXPRESSION EXPRESSION should be a text vector, which contains text to be interpreted. This function is used in the D-shell and also in menu handling. (function call) Call a function simply by writing its name and paramaters as required.


Next | Back|Top|End Because APL parses from right to left this form of program control is effected by functions. IF expression; statements IF is a monadic function. If the arguement evaluates to false, the remaining statements on that line are skipped. WHILE expression; ... ; statements; WEND WHILE is a monadic function. Its arguement is evaluated to true (1) or false(0), and a conditional branch is made to the line following the next WEND within the current function. WEND is a niladic function, giving an unconditional branch to the preceding WHILE statement. BREAKIF and REPEATIF are monadic functions whithin the body of a WHILE ... WEND loop, and effect conditional branches to either the line after the end or the beginning of the loop.


Next | Back|Top|End K<- (WW,K) #qmenu TEXT WW A vector containing a window specifier: y, x, height, width, {starting line } TEXT A matrix giving a list of options. returns an index, or -1. P <- PZERO #zgets WLIST PZERO initial selection WLIST a matrix of points or window specifiers. returns an index. K <- #sed STR STR is a string of editor commands including the command ':e' for full screen editing. K <- k Input a single key. All menu functions work via use of the cursor keys. For 'select' and 'exit' use the keys carriage return (CR) or Escape (ESC). The Meta or Alt key generally has no effect. The LINUX version supports the mouse when running under X windows. Microsoft versions must use #int 51, as shown in some example scripts. The definitions are in ../inc/na.h and ../inc/htext.h while the implementations are defined in the source files ../screen/*.c . Mouse button one generally works as 'select' while other buttons generally cause 'exit'. Mouse support uses the fact that the return value of a keystroke is an integer. Real characters only use one byte, so all other values can have special meanings.


Next | Back|Top|End #files A<-16 #files"*.*" gets directories. A<- #files"\*.*" root directory. The result is a matrix, and it can be used in the menu functions. The left parameter is the search attribute, as known to DOS. This function will display all files on a DOS system, and depends on who is running the program on UNIX systems. This means that the function will find files such as MSDOS.SYS on PCs. Do not try modifying such files unless you know what you are doing. #fstat S <- #fstat name This function returns the UNIX status of a file. S is a bit significant value, and it contains the value returned by the operating system. Examples:- #fstat "xyz" no file 0 (5r8)n #fstat"d4.doc" this file 0 0 6 6 6 (5r8)n #fstat"d4x" executable 0 0 7 5 5 (5r8)n #fstat"/home" a directory 4 0 7 5 5 FILE FUNCTIONS Smaller files. DATA <- #zload F read file R <- DATA #zsave F write block to file Files containing functions: #load filename re-start using functions in file. #copy filename add new functions from filename. Larger files. #close, #read, #fnames, #write, #open, #fsize. A filename F may be a full pathname. Non zero results from #open, and #write indicate errors. End of the input file is indicated when the number of bytes returned by #read is less than requested. Any file is treated as an array of bytes, just like the low level file functions of C. The size of a record is specified by the read function (#read). Any ordinary file consists of a header, and then records. The #read function takes four parameters: the file tie number (N, which is 0 for stdin, stdout), the the offset at beginning of file (start), the record length (size), and the record pointer (rp). On 16-bit systems, this enables large files to be processed. Changing the value of size allows many records to be acessed and displayed on a screen. R <- F #open N, mode Open the file F. N is the tie number, used in #read and #write. The mode has the values N_READ (1) for read only access, N_USE (3) for read/write and N_STR (5) for stream files. DATA <- #read N, start, size, rp fp = start + rp * size DATA<-$VA[N, start, size, rp] DATA <- #read N Next line in stream files. A value of -1 indicates end of file. LINE<- #read 0 Read next line from standard input. R<- DATA #write N, start, size, rp write data at start + rp * size rp = -1 for end. $VA[N, start, size, rp]<-DATA R<- DATA #write 0 Write to standard output file. R <- "" #open N close file N #close NLIST R <- #close n1,n2,...nk close files in list R <- #close close all files #fnames R<- #fnames Give table of numbers, names R<- #fnames N R<- #fsize N, BLOCK R is a pair of integers (NB,R) and the Actual file size is R+NB*BLOCK. A file can be set to zero length by using the #zsave function, before using any of the other file operations. These functions are very primitive and the file size is limited by the size of 16-bit integers on DOS systems. The (offset, size, pointer) triple is subject to restrictions: allocating large blocks does not work well, so 16K is a maximum reasonable value. The #read and #write functions simply translate the (offset, size, pointer) triple to a long integer, giving about 30-bit byte addressing within a file. Although it slows down operations, files are opened and closed for each separate operation. Z<-READ_TTY PROMPT Get a line from terminal with echo. This new function is meant for scripts where full screen IO is impossible, for example on dumb terminals. The optional arguement is the prompt.


Next | Back|Top|End Z <- HOST #socket SNUM, MODE open socket Z <- "" #socket SNUM close socket Z <- #socket return socket table. HOST is a numeric 4-vector such as 127 0 0 1 and (SNUM, MODE) is a pair of integers giving a socket number and a mode. The mode is the same as for files: 1 for read, and 3 for write. To close a socket it is necessary to use the syntax Z<-"" #socket SNUM. The expression Z<-#socket returns the internal table used to manage sockets. Data is passed between processes by the magic variable $SOCKET[] which is syntactically a subscripted variable. DATA<- $SOCKET[SNUM, MAXBYTES] get data from socket $SOCKET[SNUM, TARGET]<-DATA send data to TARGET TARGET is a 4-integer host number.


Next | Back|Top|End Memory mapped video functions. SIZE <- #screen SIZE <- #screen ROWS, COLS Set screen size. This function allows for the size of the text screen to change. Because different fonts can give anything from 18 to 600 rows of text some mechanism is necessary to keep track of the screen size. R <- #wget WW R <- DATA #wput WW These functions read text from a sub-window, or write text to a sub-window. On UNIX these functions work, although a memory mapped screen is simulated. A window specifier works in full screen text mode, with values WW = y, x, height, width in characters. On DOS systems #wput may be used to set attributes such as the colour of the text by making DATA have an integer type. R <- BM #kjput CZ, {colour} R <- #kjget CZ, dy, dx These functions work with bitmaps in graphic mode. The data is rasterised: that is to so consecutive bits in a row are packed as bytes. The pixel size of a bitmap is (rows, columns x 8). The bitmaps are aligned on byte boundaries. EGA/VGA video modes must be selected by the DOS interrupt 16 (10H). There is also a method of associating areas of the screen with transcripts. A transcript is an array of strings. The #window function creates a structure defining a screen area, a language dependent input method and an empty transcript. The lines of a transcript may be accessed either means of subscripting a special array ($T) or by use of the editing function #sed. The expression $T[IX] gives a matrix consisting of the indexed lines in the transcript of the last referenced window. The selected screen area gives a view onto the transcript. The variable $V controls what is seen. $V[0] Current line. The data is $T[$V[0]] $V[1] Horizontal offset. You see (0,$V[1]) d $T[$V[0]] $V[2 3] Cursor position within the window. Once a window number is selected, the value may be omitted from functions such as #window, #sed, #nfind etc. when the last selected number will be used. The window corresponding to 0 is special: it is a default for some functions. Usage: Result <- DATA #window FC, { HANDLE } FC function code HANDLE an optional number specifying a window. FC VALUE comment - - R<- #window usage list. int vector. DEL 0 #window 0 { ,N } delete INIT 1 WW #window 1,N setup window n. MODE 2 A #window 2 set orientation, attributes SIZE 3 WW #window 3, N Re-size. The mode value is used for different script types. At the moment the main options are right to left writing, and selection of an alternative keyboard mapping. The input modes are bit significant for the time being, but that will change. The alternative keyboard mapping is also toggled by the ^A key.


Next | Back|Top|End fc function code wn window number. Less than 'W=n' paramater at startup if omitted, then use the last set value. Niladic form R <- #window Gives a vector R so that R[j] = 1 if window in use, and R[j] = 2 or 3 if window is selected. R[j] = use(j) or selected (j). Monadic form R <- #window i0 USE as niladic form. R <- #window 0, wn DEL delete window IMODE <- #window 2, wn IMODE get input mode WW <- #window 3, wn AREA area for display Dyadic form Z <- WW #window 1, wn INIT setup window R <- IMODE #window 2, wn IMODE set input mode R <- AREA #window 3, wn AREA set area of display


Next | Back|Top|End #sed R<- N #sed CMD R<- #sed CMD KEY<- #sed The edit string contains a command. Some commands work globally on the file. A blank command just shows the contents, in the last assigned window. Some commands give results to D4. Others give return codes, including a (-1) from cursor setting commands to signify end of file. If the command string starts with an 'X', the rest of the string is parsed for semi-colon seperators (;) and each substring is passed as an edit command. For example the command "Xs/cat/dog/*;s/white/black/*" will change all occurrences of 'cat' to 'dog', then all occurrences of 'white' to 'black'. GLOBAL COMMANDS - these work on the whole file :b break lines at newline characters. :c {v|m} cut {vector | matrix} :d{t} delete buffer (also :q). :e edit :f exit and create function. :i file include file at cursor :o name cut to named object :p {n} paragraph size :r file delete transcript, then read new file :s show :u file write as unix file ('\n' line break). :w file write file. DOS style line break. g/str1/ select lines containing str1 v/str1/ delete lines containing str1 s/str1/str2/{g} substitute with regular expressions These functions do not use regular expressions S/str1/str2/{a} global replace The 's' and 'S' commands take the postfix options: * global replace i.e. 'g' and 'a'. g replace once in each line a replace all occurences in a line. String find and replace functions return a count of the number of strings found, or replaced. The delete function (:d) may take a modifier. ':dt'deletes trailing blank lines of a transcript. LOCAL COMMANDS - these work on the current line T tag current line D delete tagged range C copy tagged range to current line M move tagged range to current line o name cut tagged block to named object d move current line to scrap. i.e. delete. d/str/ delete until line containing /str/ y copy current line to scrap p pop line from scrap, before current line j join two lines. i text i/text/ insert string after cursor r/text/ replace current line r text replace line . {number | /str/ } set cursor or goto line . n goto line n LC <-n . /str/ goto line containing string .z end of file LC <- size-1 -n go back n lines +n go forward n lines @ R<-T[LC++ ] @ n R<-T[LC<-n] $ var fetch object Fast string operations. These do not use regular expressions. The delimiter character may be /,|,[ or ]. These commands are repeated via the ^N key, when in the editor. /str1, /str1/ find the string str1. /str1/str2/ replace str1 with str2 #print Print data to buffer R<- {S} #print X S<-S,X #lprint Send data to printer port. R<- #lprint X #rxfind Search string for regular expression R<- STR #rxfind DATA R is an integer pair. R[0] starting position of STR in DATA R[1] count of matching characters. DATA[R[0]+iR[1]] is the string matched.


Next | Back|Top|End ^A alternate language ^B F2 exit ^ stands for the control key. ^C exit (copy range) The ALT key has no function. ^D exit (delete range) INS insert mode (on +, off -) ^E delete to end of line CR - cursor down ^F F3 find / replace CR + break line ^G exit (get object) DOWN ^L exit (follow link to file) DEL delete character ^N find / replace next DEL join two lines ^O exit (make object) END end of file ^P F4 push line ESC command mode, exit ^Q F8 exit (quit) HOME start of file ^R F5 restore line PGDN next screen ^S F10 save changes PGUP previous screen ^T F6 tag line range TAB tab forwards ^X F7 exit (move range) UP ^W exit (width) F9 ^Y exit Find syntax: the command '/str1' will find the first occurrence of 'str1'. Use ^N to find the next occurrence. A command '/str1/str2/' will replace 'str1' with 'str2'. Use ^N to progress through the file. If you wish to change a string containing a slash, then edit the first character of the command. To change '/*' to '|', simply type S'/*'|'. Editing may occur in English or from right to left. In addition a set of rules may be given for composite characters. What happens during editing is controlled by a window mode. Fonts are obtained from bios calls on PCs, or selection of a font for a whole process window (in which D4 runs) on UNIX systems. Keyboard tables may be set so that the alpha-numeric keys give any desired code. The editing mode byte is bit significant: 0X01 map keys into upper character space (0X80-0XFF). 0X02 reverse orientation: right to left input. 0X04 reverse input (push characters forwards). 0X08 Allow composite character formation. ($ALPHA) 0X10 Modify characters on input ($BETA) 0X80 Inverse video current line. The keyboard tables also map all the cursor movement keys to single byte control codes. This means there is a shortage of such keys for the editor, so some keys have a dual meaning, depending on the insert mode. In particular the carriage return splits lines in insert mode. Keyboard translation is achieved by setting the environment variable $KB, a 256 byte table. It is quite possible to disable the keyboard completely by injudicious use of this function. Linux users may enjoy trying to set up the environment variable $KB_SEQ. This is a text matrix where each row defines a single key value followed by a count prefixed string. This structure is set up by scripts which interrogate the terminfo database. The compiled program uses a hard coded version of 'linux' console keys. It would be easy enough to change this.


Next | Back|Top|End #inlin input at cursor position. Force use of existing edit functions. k R <- #inkey {wait_time} R <-k {wait_time} The #inkey function returns an integer, the ASCII value of the last key pressed. This function also returns the key used to exit the #zgets function. When a wait_time paramater is specified the function returns the value 0 on time out, without waiting for further input. #zgets J<- {J_START} #zgets YXLK Allows selection from a series of field specifiers. YXLK is an NF x NC matrix, with at least two columns. This function pushes the last key.


Next | Back|Top|End The Chinese character functions #kjput and #kjget work with packed rasterized bitmaps on byte boundaries for the EGA/VGA type displays. By using #kjput an entire scanline, or group of scanlines may be written. Any image which can be assembled line by line may be written (slowly) to the screen. It seems unneccessary to have any more graphic functions. Because of the nature of a multiscript system, most printing gets done in bit-image mode. This means that the graphics functions enhance the printer rather than the video. The video display functions are included merely to give a preview of what is to be printed. Some line drawing functions are included. They work on sets of points which are represented by Nx2 matrices. Sometimes a set of points can be computed by adding displacements. The video is seen as a grid, with the 'taxi-cab' metric. A move table is a set of displacements: North, South, East, West. D4 is quite suited to doing calculations on these arrays. Two functions return these arrays. DZ <- #kjvec A,B |A|>|B| gives |A| points line Ax-By = 0 DZ <- #kjarc R points on arc (PI/4) with center at (0,R). For compatability with the text handling functions the co-ordinates are represented as (y, x) pairs. The arrays DZ represent move tables. The only possible values are -1, 0 or 1. A set of points may be displayed by means of the #kjplot function. Z<-CM #kjplot Z plot points Z<-CM #kjplot Z,Z+(rZ)rDZ draw rectangles, or squares Z is a set of points (y,x pairs) and CM is a vector containing one or two values: the colour and the drawing mode (PSET, AND, OR, XOR). When Z is an Mx4 matrix the function draws rectangles. There are two block fill functions: #kjbloc and #kjfill. Z<- CC #kjbloc ZB ZB is an Nx4 of rectangle specifiers. CC defines the colour and drawing mode. The fill pattern is taken from the matrix $TILE. If Z is a set of points then HZ<- #kjfill Z gives a set of one line tiles which cover the interior of Z. These are stored as triples: (y, xa, xb) with xa < xb. The function would be very easy to write if there was enough memory to store every pixel in what could be a quarter megabyte array. The difficulty comes from the fact that the list of interior pixels is compressed as a set of horizontal tiles. See the file <delta.d4f> for examples.


Next | Back|Top|End #peek R<- #peek ADDR ADDR is a N x 2 integer matrix, containing segments and offsets. The function returns a vector of byte values, read from successive locations. On 32 bit systems ADDR is simply a vector of virtual addresses. DATA #peek ADDR Set the specified addresses. #nc R<- #nc NAME Name class. An integer, giving the type of the object specified by NAME. 0 for undefined, 1 for text, 2 or 4 for integer depending on 16/32 bit versions, 8 for double (floating point), and so on. The values are fully specified in the header file <na.h>. #varptr ADDR <- #varptr VARIABLE_NAME 16-bit: Segment:offset for data of NAME 32-bit: Integer as virtual address of contents of NAME Use in #call for 16-bit systems. Use as a test for which interpretor is running. #ifdef R <- #ifdef NAME Empty vector if the there is no object called NAME, otherwise the value of NAME. Not useful. It is the same as R<-"" #ifndef NAME. #ifndef R<-VAL #ifndef NAME If there is an object called NAME, then return that value, otherwise use the left argument. This allows functions to take 0, 1 or 2 parameters. #ex Expunge. R<- #ex NAME Release space taken by object called NAME. There is still a slot in the symbol-table for name, but #nc, #ifdef, #ifndef will work as though NAME is now undefined. Note: The functions #nc, #varptr, #ex will also work on text matrices which serve as lists of names. #int Machine interrupt Z<- (AX,BX,CX,DX) #int n Z is value of returned registers AX,BX,CX,DX #call Interface with machine code. Z<-(x1,x2,x3,x4,x5,x6) #call TEXT TEXT is a set of bytes containing 8086 instructions. These instructions should be included: start PUSH BP set base pointer MOV BP,SP access parameters x1, WORD PTR[BP+06] x2, WORD PTR[BP+08] x3, WORD PTR[BP+0A] x4, WORD PTR[BP+0C] etc return POP BP RETF The 80x86 instructions may be created with the 'debug' utility. First a text file is created with the the debug command a100 followed by the assembler instructions, a blank line, and then the commands u100, q. Next debug is invoked with the command line 'debug < file'. A listing appears on the screen with the code bytes somewhere on the left of the screen. These may be cut by means of the 'ww' alias, which cuts a rectangular block from the display screen. After elimination of blanks these bytes give the required machine instructions. #ts Time stamp. R <- #ts DATE-TIME as year, month, day, hour, minute, second. This is in the British format. #port comm port functions R <- {DATA} #port function {portnum} {option} Don't use. Wait for #socket when that gets done.


Next | Back|Top|End #av ascii-codes. R<- #av S Convert R<- "N" #av S Double -> Integer R<- "D" #av S Integer -> Double Also could use number of bytes.


Next | Back|Top|End R <- #atan YX arctan on pairs (y,x) R <- #av x ascii/numeric conversion R <- (X1,X2,X3,X4,X5,X6) #call txt DOS/WINDOWS 80x86 code. R <- #circle X trignometrical functions. R <- #close handle(s) close files R <- #cmd command dos/unix command R <- #copy "filename" add functions R <- #cursor cursor position #cursor Y,X set cursor R <- #del file delete a file R <- A #equiv B A == B (type, size, value) R <- #ex idlist erase objects R <- #exp X exponential function R <- #fi chardata evaluate numeric text R <- pattern #find data string search R <- string #fmt data numeric format R <- #fstat pathname returns file mode bits for pathname R <- #ifdef name (name defined) ? value : empty R <- EXPR #ifndef name (name defined) ? value : EXPR R <- #inkey get key R <- (registers) #int interrupt dos interrupt R <- #ln X Natural logarithm R <- #lprint X send character vector to printer R <- #kjarc RADIUS PI/4 arc of circle at center (R,0). R <- #kjfill Z return interior as horizontal tiles R <- #kjget WJ,SI get bitmap from WJ[], colour SI R <- (SI,M) #kjplot Z plot points at Z, coulour SI R <- BM #kjput CZ,SI put bitmap at CZ, colour SI R <- CM #kjvec DZ draw line segments #load file load function file R <- {FS} #mattoss matrix to delimited string R <- #mkdir DIR make a directory R <- #mouse YX mouse support. R <- #nc idlist name class R <- {WP} #nfind string line numbers of string in $T[WP] R <- {WP} #nxfind pattern line numbers of pattern $T[WP] R <- #nl type name list R <- name #open handle, mode open a file R <- #peek locations memory access R <- {bytes} #peek locations write to memory R <- #read num, start, size, rp read bytes from file R <- #rmdir DIR remove directory R <- pattern #rxfind data returns location of string in text. R <- SUBS #rxsubs STRING Regular expression substitute. R <- #screen {size} get / set current screen size R <- N #sed STR edit transcript[N] R <- #sleep DELAY takes 2-vector: seconds,microseconds R <- HOST #socket handle, mode initialise a socket N <- {FS} #split V Split text into fields $0 $1 .. $N-1 R <- #sqrt X square root N <- {FS} #sstomat V Split text into a matrix R <- {size} #use name hash table as array R <- #val string numeric conversion R <- #varptr idlist like varptr (basic) R <- #ts date and time R <- #wget ww get characters R <- #window vector window functions R <- data #window vector data #wput ww write characters to subwindow R <- data #write h,start,size,rp write file data R <- #zgets wlist form input R <- data #zputs wlist paste text in windows


Next | Back|Top|End


Next | Back|Top|End Some of these operations may need to be included in the script file. Those marked with a * must be translated to #sed commands. This list replaces termcap code in the program. When some cursor keys, or function keys don't work, then the Ctrl+ selected key will do the trick. F1 ^A alternate mode - Right to left language F2 ^B help - exit ^C * copy block ^D * delete block ^E delete to end of line F3 ^F find functions ^G * get object ^H delete character ^I Tab forwards ^J Jump (PgDn) ^K Begin file (Home) ^L * Goto line, or follow link. CR ^M Carriage Return ^N find next ^O * object - Cut F4 ^P delete line -- push F8 ^Q quit F5 ^R restore line F10 ^S save exit F6 ^T tag modes ^U page up (PgUp) ^V Insert mode ^W * width F7 ^X * move block F9 ^Y ^Z End (End key) Ctrl + '^' cursor up. Ctrl + '/' cursor down. Ctrl + '\' cursor forwards Ctrl + ']' cursor backwards. Ctrl + '[' Escape key. Ctrl + '_' Leap into the dark.


Next | Back|Top|End $0 Name of executeable $1 Runfile $2, $3, .. other parameters $$[J] get $[i] as an array element $ALPHA Character composition table. <./text.doc> $BETA Character modification table. $GZ Pixel cursor. $ENVP Virtual array environment variables. $FSTAT Virtual array: gives fstat[file_table] $KB Keyboard mapping. $KB_SEQ UNIX systems. Terminal control sequences. $LP_DEV UNIX systems set to /dev/lp0, /dev/lp1 etc. $MEM Virtual memory as an array $NL system symbol table $QUOTIENT quotient evaluated during R<-A #mod B $REMAINDER remainder calculated in R<-A%B $SCR[] Text screen $SINK All output X is treated as $SINK #print X $SOCKET[] Socket $STACK_TRACE Stack trace $S[] Size of $T $T[] Current transcript as an array. $TILE Fill pattern (packed bitmap). $V[] Viewport for current text window. _UFILE Current run file. D4LIB_PATH search path used in #copy"filename" EV.KB Key-board input event handler. EV.ALX Break-in handler. var1, var2 .. var=val pairs on command line $stack Stack $nfiles File descriptors $nww Window descriptors $vs Virtual screen on 32-bit systems. $inline Line input buffer. $nsock Socket table pointer WHILE start iteration WEND end of iteration BREAKIF conditional breakout REPEATIF conditional restart SRAND set random seed User supplied error handlers may be installed in a script. These deal with break-in, and command input from the key board. Two variables may be defined in the runfile, or even the command line. Each of these variables should hold an expression, which will be done in the case of either the system expecting a command from the user, or when the Control Break keys are pressed. Normally the system only waits for user input when an error has occurred. It is possible to set these variables to refer to functions, or code sequences which will set the key-board to english, type an error message, or return to the main menu. An example of the simplest error stratagy is: EV.KB<-EV.ALX<-"GO" Where GO is some function. $ENVP[] variables are reset in the environment on startup.


| Back|Top|End Write scripts to use /usr/include symbols for writing device drivers. More socket stuff. Package manager. Info style documentation. Autoconf and config style scripts.
Back to the Top