11 the programming language
\r\r
18 Basic constructs and facilities
\r\r
28 Author: Antoni Kreczmar
\r
41 Institute of Informatics, Warsaw University
\r
43 edited by A.Salwicki LITA Pau November 1990
\r
48 {TOC \o|1. Compound statements 4
\r
50 3. Procedures and functions 10
\r
52 5. Adjustable arrays 16
\r
53 6. Coroutines and semicoroutines 19
\r
56 9. Protection techniques 29
\r
57 10. Programmed deallocation 30
\r
58 11. Exception handling 32
\r
59 12. Concurrent processes. 33
\r
62 LOGLAN-82 is a universal programming language designed at the Institute of Informatics, University of Warsaw. Its syntax is patterned upon Pascal's. Its rich semantics includes the classical constructs and facilities offered by the Algol-family programming languages as well as more modern facilities, such as concurrency and exception handling.
\r\r
63 The basic constructs and facilities of the LOGLAN-82 programming language include:
\r\r
64 1) A convenient set of structured statements,
\r\r
65 2) Modularity (with the possibility of module nesting and extending),
\r\r
66 4) Classes (as a generalization of records) which enable to define complex structured types, data structures, packages, etc.,
\r\r
67 5) Adjustable arrays whose bounds are determined at run-time in such a way that multidimensional arrays may be of various shapes, e.g. triangular, k-diagonal, streaked, etc.,
\r\r
68 6) Coroutines and semi-coroutines,
\r\r
69 7) Prefixing - the facility borrowed from Simula-67, substantially generalized in LOGLAN-82 - which enables to build up hierarchies of types and data structures, problem-oriented languages, etc.,
\r\r
70 8) Formal types treated as a method of module parametrization,
\r\r
71 9) Module protection and encapsulation techniques,
\r\r
72 10) Programmed deallocator - a tool for efficient and secure garbage collection, which allows the user to implement the optimal strategy of storage management,
\r\r
73 11) Exception handling which provides facilities for dealing with run-time errors and other exceptional situations raised by the user,
\r\r
74 12) Concurrency easily adaptable to any operating system kernel and allowing parallel programming in a natural and efficient way.
\r\r
75 The language covers system programming, data processing, and numerical computations. Its constructs represent the state-of-art and are efficiently implementable. Large systems consisting of many cooperating modules are easily decomposed and assembled, due to the class concept and prefixing.
\r\r
76 LOGLAN-82 constructs and facilities have appeared and evolved simultaneously with the experiments on the first pilot compiler (implemented on Mera-400 Polish minicomputer). The research on LOGLAN-82 implementation engendered with new algorithms for static semantics, context analysis, code generation, data structures for storage management etc.
\r\r
77 The LOGLAN-82 compiler provides a keen analysis of syntactic and semantic errors at compilation as well as at run time. The object code is very efficient with respect to time and space. The completeness of error checking guarantees full security and ease of program debugging.
\r\r
78 1. Compound statements
\r\r
79 Compound statements in LOGLAN-82 are built up from simple statements (like assignment statement e.g. x:=y+0.5, call statement e.g. call P(7,x+5) etc.) by means of conditional, iteration and case statements.
\r\r
80 The syntax of conditional statement is as follows:
\r\r
81 if boolean expression
\r
83 sequence of statements
\r
85 sequence of statements
\r
87 where "else part" may be omitted:
\r\r
88 if boolean expression
\r \r
90 sequence of statements
\r
92 The semantics of conditional statement is standard. The keyword fi
\r allows to nest conditional statements without appearence of "dangling else" ambiguity.
\r\r\r
97 x2:=sqrt(delta)/a/2;
\r
102 x1:=-b/a/2+x2; x2:=x1-2*x2
\r
109 write(" no real roots")
\r
112 The statements in a sequence of statements are separated with semicolons (semicolon may end a sequence , and then, the last statement in the sequence is the empty statement).
\r\r
113 The short circuit control forms are realized in LOGLAN-82 by the conditional statements with orif (or andif) list. A conditional
\r statement with orif list has the form:
\r orif
\r\r
114 if wb1 orif wb2 ... orif wbk
\r \r
116 sequence of statements
\r
118 sequence of statements
\r
120 and corresponds somehow to a conditional statement:
\r\r
121 if wb1 or wb2 ... or wbk
\r \r
123 sequence of statements
\r
125 sequence of statements
\r
127 The above conditional statement (without orif list) selects for
\r execution one of two sequences of statements, depending on the truth value of the boolean expression:
\r\r
128 wb1 or wb2 or ... wbk
\r \r\r
129 which is always evaluated till the end. For the execution of the conditional statement with orif list the specified conditons
\r wb1,...,wbk are evaluated in succession, until the first one evaluates to true. Then the rest of the sequence wb1,...,wbk is abandoned and "then part" is executed. If none of the conditions wb1,...,wbk evaluates to true "else part" is executed (if any).
\r\r
130 Conditional statements with orif list facilitate to program those con_ditions, which evaluation to the end may raise a run-time error.
\r\r
132 The execution of the statement:
\r\r
133 if i>n or A(i)=0 then i:=i-1 else A(i):=1 fi
\r \r\r
134 where the value of i is greater than n, and A is an array with upper bound n, will raise the run-time error. Then the user can write:
\r\r
135 if i>n orif A(i)=0 then i:=i-1 else A(i):=1 fi
\r\r
136 what allows to avoid this run-time error and probably agrees with his intension.
\r\r
137 Conditional statement with andif list has the form:
\r\r
138 if wb1 andif wb2 ... andif wbk
\r
140 sequence of statements
\r
142 sequence of statements
\r
144 For the execution of this kind of statements, the conditions wb1,...,wbk are evaluated in succession, until the first one evaluates to false; then "else part" (if any) is executed. Otherwise "then part" is executed.
\r\r
145 Iteration statement in LOGLAN-82 has the form:
\r\r
146 do sequence of statements od
\r\r
147 An iteration statement specifies repeated execution of the sequence of statements and terminates with the execution of the simple statement exit
\r\r
153 if abs(t) < 1.0E-10 then exit fi;
\r
156 If two iteration statements are nested, then double exit in the
\r inner one terminates both of them.
\r\r
160 s,t:=1; i:=1; x:=x+0.2;
\r
163 if i > n then exit exit fi; (* termination of both loops *)
\r \r
164 if t < 1 then exit fi; (* termination of the inner loop *)
\r
168 In the example above simultaneous assignment statements are illustrated (e.g. r,x:=0) and comments, which begin with a left parenthesis immediately followed by an asterisk and end with an asterisk immediately followed by a right parenthesis.
\r\r
169 Triple exit terminates three nested iteration statements, four exit terminates four nested iteration statements etc.
\r\r
170 The iteration statement with while condition:
\r while
\r\r
171 while boolean expression
\r
173 sequence of statements
\r
175 is equivalent to:
\r\r
177 if not boolean expression then exit fi;
\r
178 sequence of statements
\r
180 The iteration statements with controlled variables (for statements)
\r have the forms:
\r\r
181 for j:=wa1 step wa2 to wa3
\r \r
183 sequence of statements
\r
186 for j:=wa1 step wa2 downto wa3
\r
188 sequence of statements
\r
190 The type of the controlled variable j must be discrete. The value of this variable in the case of the for statement with to is increased, and in the case of the for statement with downto is decreased. The
\r discrete range begins with the value of wa1 and changes with the step equal to the value of wa2. The execution of the for statement with to terminates when the value of j for the first time becomes greater than the value of wa3 (with downto when the value of j for the first time
\r becomes less than the value of wa3). After the for statement
\r termination the value of its controlled variable is determined and equal to the first value exceeding the specified discrete range. The values of expressions wa1, wa2 and wa3 are evaluated once, upon entry to the iteration statement. Default value of wa2 is equal 1 (when the keyword step and expression wa2 are omitted).
\r\r
191 For or while statements may be combined with exit statement.
\r\r
196 if x=A(j) then exit fi;
\r
198 The above iteration statement terminates either for the least j, 1<=j<=n, such that x=A(j) or for j=n+1 when x=/=A(j), j=1,...,n.
\r\r
199 To enhance the user's comfort, the simple statement repeat is provided. It may appear in an iteration statement and causes the current iteration to be finished and the next one to be continued (something like jump to CONTINUE in Fortran's DO statements).
\r\r
204 if A(i)<0 then repeat fi; (* jump to od,iterations are contd.*)
\r
205 if i > m then exit fi; (* iteration statement is terminated*)
\r
208 Just as exit, repeat may appear in for statement or while statement. Then the next iteration begins with either the evaluation of a new value of the controlled variable (for statement) or with the
\r evaluation of the condition (while statement).
\r\r
209 Case statement in LOGLAN-82 has the form:
\r\r
217 where WA is an expression , L1,...,Lk are constants and I1,..., Ik,I are sequences of statements.
\r\r
218 A case statement selects for execution a sequence of statements Ij, 1{SYMBOL 163 \f "Symbol"}j{SYMBOL 163 \f "Symbol"}k, where the value of WA equals Lj. The choice otherwise covers
\r all values (possibly none) not given in the previous choices. The execution of a case statement chooses one and only one alternative (since the choices are to be exhaustive and mutually exclusive).
\r
220 Modular structure of the language is gained due to the large set of means for module nesting and extending. Program modules (units) are blocks, procedures, functions, classes, coroutines and processes. Block is the simplest kind of unit. Its syntax is the following:
\r\r
222 lists of declarations
\r
224 sequence of statements
\r
226 The sequence of statements commences with the keyword begin (it may
\r be omitted when this sequence is empty). The lists of declarations define the syntactic entities (variables, constants, other units), whose scope is that block. The syntactic entities are identified in the sequence of statements by means of names (identifiers).
\r\r
231 var x,y:real, i,j,k: integer, b: boolean;
\r \r
234 read(i,j); (* read two integers *)
\r
235 x,y:=n/(i+j); (* simultaneous assignment *)
\r
236 read(c) ; (* read a character *)
\r
237 b:= c = 'a'; (* 'a' a character *)
\r
240 write(x+y/k:10:4); (* print the value of x+y/k in the
\r
241 field of 10 characters, 4 digits after the point *)
\r
244 In the lists of declarations semicolons terminate the whole lists, not the lists elements. Any declaration list must begin with the pertinent keyword (var for variables, const for constants etc.). The
\r value of an expression defining a constant must be determinable statically (at compilation time).
\r\r
245 Program in LOGLAN-82 may be a block or alternatively may be of the following form:
\r\r
247 lists of declarations
\r
249 sequence of statements
\r
251 Then the whole program can be identified by that name (the source as well as the object code).
\r\r
252 A block can appear in the sequence of statements (of any unit), thus it is a statement. (Main block is assumed to appear as a statement of the given job control language.)
\r\r
253 For the execution of a block statement the object of block is created in a computer memory, and then, the sequence of statements is performed. The syntactic entities declared in the block are allocated in its object. After a block's termination its object is automatically deallocated (and the corresponding space may be immediately reused).
\r\r
254 The modular structure of the language works "in full steam" when not only blocks, but the other kinds of units are also used. They will be described closer in the following points.
\r\r
255 Unit nesting allows to build up hierarchies of units and supports security of programming. It follows from the general visibility rules; namely, a syntactic entity declared in an outer unit is visible in an inner one (unless hidden by an inner declaration). On the other hand, a syntactic entity declared in an inner unit is not visible from an outer one.
\r\r\r
259 var a,b,c:real, i,j,k:integer;
\r \r
265 j:=a; k:=j+b; write(" this is the inner block ",j,k)
\r
267 write(" this is the outer block ",i,a:20)
\r
269 In this program, first the main block statement is executed (with variables a,b,c,i,j,k). Next, after the read statement, the inner block statement is executed (with variables j,k). In the inner block the global variables j,k are hidden by the local ones.
\r\r
270 3. Procedures and functions
\r\r
271 Procedures and functions are well-known kinds of units. Their syntax is modelled on Pascal's, though with some slight modifications. Procedure (function) declaration consists of a specification part and a body.
\r\r \r
274 unit Euclid: function(i,j:integer):integer;
\r \r
278 if j=0 then exit fi;
\r \r
279 k:=i mod j; i:=j; j:=k
\r \r
283 Procedure or function specification begins with its identifier preceded by the keyword unit. (The same syntax concerns any other
\r module named unit.) Then follows its kind declaration, its formal parameters (if any), and the type of the returned value (only for functions). A body consists of declaration lists for local entities and a sequence of statements. The keyword begin commences the sequence of statements, and is omitted, if this sequence is empty. The value returned by a function equals to the most recent value of the standard variable "result", implicitly declared in any function. This variable can be used as a local auxiliary variable as well.
\r\r
286 unit Newton: function(n,m:integer):integer;
\r \r
289 if m > n then return fi;
\r \r
291 for i:=2 to m do result:=result*(n-i+1) div i od
\r \r
293 The optional identifier at the end of a unit must repeat the identifier of a unit. It is suggested that the compilers check the order of unit nesting, so these optional occurrences of identifiers would facilitate program debugging.
\r\r
294 All the local variables of a unit are initialized (real with 0.0, integer with 0, boolean with false etc.). Thus , for instance, the value of function Newton is 0 for m>n, since "result" is also initialized, as any other local variable.
\r\r
295 The return statement (return) completes the execution of a procedure (function) body,i.e. return is made to the caller. If return does not
\r appear explicitly, return is made with the execution of the final end
\r of a unit. Upon return to the caller the procedure (function) object is deallocated.
\r\r
296 Functions are invoked in expressions with the corresponding list of actual parameters. Procedures are invoked by call statement (also with the corresponding list of actual parameters).
\r\r
299 i:=i*Euclid(k,105)-Newton(n,m+1);
\r
300 call P(x,y+3);
\r \r\r
301 Formal parameters are of four categories: variable parameters, procedure parameters, function parameters and type parameters (cf p.8). Variable parameters are considered local variables to the unit. A variable parameter has one of three transmission modes: input, output or inout. If no mode is explicitly given the input mode is assumed. For instance in the unit declaration:
\r\r
302 unit P: procedure(x,y:real,b:boolean;
\r
303 output c:char,i:integer;inout :integer);
\r\r
304 x,y,b are input parameters , c,i are output parameters , and j is inout parameter.
\r\r
305 Input parameter acts as a local variable whose value is initialized by the value of the corresponding actual parameter. Output parameter acts as a local variable initialized in the standard manner (real with 0.0, integer with 0, boolean with false etc.). Upon return its value is assigned to the corresponding actual parameter, in which case it must be a variable. However the address of such an actual parameter is determined upon entry to the body. Inout parameter acts as an input parameter and output parameter together.
\r\r
308 unit squareeq: procedure(a,b,c:real;output xr,xi,yr,yi:real);
\r \r
309 (* given a,b,c the procedure solves square equation :
\r
311 xr,xi- real and imaginary part of the first root
\r
312 yr,yi- real and imaginary part of the second root *)
\r
313 var delta: real;
\r \r
315 a:=2*a; c:=2*c; delta:=b*b-a*c;
\r
319 if delta=0 then return fi; (*xi=yi=0 by default*)
\r \r
320 delta:=sqrt(-delta);
\r
321 xi:=delta/a; yi:=-xi;
\r
324 delta:=sqrt(delta);
\r
327 xr:=delta/a; yr:=-xr;
\r
330 if b>0 then b:=b+delta else b:=b-delta fi;
\r
331 xr:=-b/a; yr:=-c/b;
\r
333 A procedure call to the above unit may be the following:
\r\r
334 call squareeq(3.75*H,b+7,3.14,g,gi,h,hi);
\r\r
335 where g,h,gi,hi are real variables.
\r\r
336 No restriction is imposed on the order of declarations. In particular, recursive procedures and functions can be declared without additional announcements (in contrast to Pascal).
\r\r
338 For two recursive sequences defined as:
\r\r
339 a(n)=b(n-1)+n+2 n>0
\r
340 b(n)=a(n-1)+(n-1)*n n>0
\r
342 one can declare two functions:
\r\r
343 unit a: function(n:integer):integer;
\r
345 if n>0 then result:=b(n-1)+n+2 fi
\r
347 unit b: function(n:integer):integer;
\r
349 if n>0 then result:=a(n-1)+(n-1)*n fi
\r \r
352 k:=a(100)*b(50)+a(15);
\r\r
353 Functions and procedures can be formal parameters as well.
\r\r
356 unit Bisec: procedure(a,b,eps:real;output x:real;function f(x:real):real);
\r
357 (*this procedures searches for zero of the continous function f in the segment (a,b) *)
\r
358 var h:real,s:integer;
\r
361 if sign(f(b))=s then return fi; (* wrong segment *)
\r \r
365 if h < eps then return fi;
\r
366 if sign(f(x))=s then a:=x else b:=x fi
\r
369 In the above declaration, after the input variable parameters a,b,eps and the output variable parameter x, a function parameter f appears. Note that its specification part is complete. Thus the check of actual-formal parameter compatibility is possible at compilation time. Making use of this syntactic facility is not possible in general, if a formal procedure (function) is again a formal parameter of a formal procedure (function). The second degree of formal procedures (functions) nesting is rather scarce, but LOGLAN-82 admits such a construct. Then formal procedure (function) has no specification part and the full check of actual-formal parameter compatibility is left to be done at run time.
\r\r
372 unit P: procedure(j:integer; procedure G (i:integer;
\r
379 Procedure G is a first degree parameter, therefore it occurs with complete specification part. Procedure H is a second degree parameter and has no specification part. In this case a procedure call can be strongly recursive:
\r\r
380 call P(i+10,P);
\r \r\r
382 Class is a facility which covers such programming constructs as structured type, package, access type, data structure etc. To begin with the presentation of this construct, let us consider a structured type assembled from primitive ones:
\r\r
384 var dollars :real,
\r
386 year,month,day :integer;
\r
388 The above class declaration has the attributes : dollars (real), not_paid (boolean), and year,month,day (integer). Wherever class bill is visibile one can declare variables of type bill:
\r\r
390 The values of variables x, y, z can be the addresses of objects of class bill. These variables are called reference variables. With reference variable one can create and operate the objects of reference variable type.
\r\r
391 An object of a class is created by the class generation statement (new), and thereafter, its attributes are accessed through dot
\r notation.
\r\r
392 x:=new bill; (* a new object of class bill is created *)
\r
393 x.dollars:=500.5; (* define amount *)
\r
394 x.year:=1982; (* define year *)
\r
395 x.month:=3; (* define month *)
\r
396 x.day:=8; (* define day *)
\r
397 y:=new bill; (* create a new object *)
\r \r
398 y.not_paid:=true; (* bill not_paid *)
\r
399 z:=y; (* variable z points the same object as y *)
\r\r
400 If an object of class bill has been created (new bill) and its
\r address has been assigned to variable x (x:=new bill), then the
\r attributes of that object are accessible through dot notation (remote access). The expression x.dollars gives , for instance, the remote access to attribute dollars of the object referenced by x. All attributes of class objects are initialized as usual. For the above example the object referenced by x, after the execution of the specified sequence of statements, has the following structure:
\r\r
414 The object referenced by y and z has the following structure:
\r\r
427 The value none is the default initial value of any reference
\r variable and denotes no object. A remote access to an attribute of none raises a run time error.
\r\r
428 Class may have also formal parameters (as procedures and functions). Kinds and transmission modes of formal parameters are the same as in the case of procedures.
\r\r
433 unit node: class (a:integer);
\r
434 var left,right:node;
\r \r
436 Let, for instance, variables z1, z2, z3 be of type node. Then the sequence of statements:
\r\r
438 z2:=new node(3);
\r \r
439 z3:=new node(7);
\r \r
440 z1.left:=z2; z1.right:=z3;
\r\r
441 creates the structure:
\r\r
448 ³ ³ right ÃÄÄÄÄÄÄÄÄ¿
\r
451 ÚÄÄÄÄÁÄÄÄÄÄ¿ ÚÄÄÄÄÄÁÄÄÄÄ¿
\r
452 z2ÄÄÄÄÄ´ 3 ³ ³ 7 ÃÄÄÄÄÄÄz3
\r
453 ÃÄÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄÄ´
\r
455 ÃÄÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄÄ´
\r
457 ÀÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÙ
\r\r
459 where arrows denote the values of the reference variables.
\r\r
460 Class may also have a sequence of statements (as any other unit). That sequence can initialize the attributes of the class objects.
\r\r
463 unit complex:class(re,im:real);
\r \r
464 var module:real;
\r \r
466 module:=sqrt(re*re+im*im)
\r
468 Attribute module is evaluated for any object generation of class complex:
\r\r
469 z1:=new complex(0,1); (* z1.module equals 1 *)
\r
470 z2:=new complex(2,0); (* z2.module equals 2 *)
\r \r\r
471 For the execution of a class generator, first a class object is created, then the input parameters are transmitted , and finally, the sequence of statements (if any) is performed. Return is made with the execution of return statement or the final end of a unit. Upon return the output parameters are transmitted.
\r\r
472 Procedure object is automatically deallocated when return is made to the caller. Class object is not deallocated , its address can be assigned to a reference variable, and its attributes can be thereafter accessed via this variable.
\r\r
473 The classes presented so far had only variable attributes. In general, class attributes may be also other syntactic entities, such as constants, procedures, functions, classes etc. Classes with procedure and function attributes provide a good facility to define data structures.
\r\r
476 A push_down memory of integers may be implemented in the following way:
\r\r
477 unit push_down :class;
\r \r
478 unit elem:class(value:integer,next:elem);
\r
479 (* elem - stack element *)
\r
482 unit pop: function :integer;
\r \r
486 result:=top.value; top:=top.next
\r
489 unit push:procedure(x:integer); (* x - pushed integer *)
\r
491 top:=new elem(x,top);
\r
494 Assume that somewhere in a program reference variables of type push_down are declared (of course, in place where push_down is visibile):
\r\r
495 var s,t,z:push_down;
\r \r\r
496 Three different push_down memories may be now generated:
\r\r
497 s:=new push_down(100); t:=new push_down(911); z:=new push_down(5);
\r \r\r
498 One can use these push_down memories as follows:
\r\r
499 call s.push(7); (* push 7 to s *)
\r \r
500 call t.push(1); (* push 1 to t *)
\r \r
501 i:=z.pop; (* pop an element from z *)
\r
503 5. Adjustable arrays
\r\r
504 In LOGLAN-82 arrays are adjustable at run time. They may be treated as objects of specified standard type with index instead of identifier selecting an attribute. An adjustable array should be declare somewhere among the lists of declarations and then may be generated in the sequence of statements.
\r\r
508 var n,j:integer;
\r \r
509 var A:arrayof integer; (* here is the declaration of A *)
\r
512 array A dim (1:n); (* here is the generation of A *)
\r \r
519 A variable A is an array variable. Its value should be the reference to an integer array, i.e. a composite object consisting of integer components each one defined by an integer index.
\r
520 Array generation statement:
\r\r
521 array A dim (1:n);
\r \r\r
522 allocates a one-dimensional integer array with the index bounds 1,n , and assigns its address to variable A.
\r
523 The figure below illustrates this situation:
\r\r
525 ÚÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄ¿
\r
529 ÃÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄ´
\r
533 ÃÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄ´
\r
534 ³ A ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ A(n) ³
\r
535 ÀÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÙ
\r
536 Block object Array object
\r\r
538 A general case of array generation statement has the form:
\r
539 array A dim (lower:upper)
\r \r\r
540 where lower and upper are arithmetic expressions which define the range of the array index.
\r\r
542 Two-dimensional array declaration :
\r\r
543 var A: arrayof arrayof integer;
\r \r\r
546 for i:=1 to n do array A(i) dim (1:m) od;
\r \r\r
547 create the structure:
\r
554 ÚÄÄÄÄÄÄÄÄÄÄ¿ ÃÄÄÄÄÄÄÄij
\r
555 ³ A(1) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ A(1,m) ³
\r
556 ³ÄÄÄÄÄÄÄÄÄÄ´ ÀÄÄÄÄÄÄÄÄÙ
\r
560 ÃÄÄÄÄÄÄÄÄÄÄ´ ÚÄÄÄÄÄÄÄÄ¿
\r
561 ³ A(n) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ A(n,1) ³
\r
562 ÀÄÄÄÄÄÄÄÄÄÄÙ ÃÄÄÄÄÄÄÄÄ´
\r
571 var i,j:integer, A,B: arrayof arrayof real, n:integer;
\r
574 array A dim (1:n);
\r
575 for i:=1 to n do array A(i) dim (1:n) od;
\r \r
576 (* A is square array *)
\r
577 array B dim (1:n);
\r \r
578 for i:=1 to n do array B(i) dim(1:i) od;
\r
579 (* B is lower triangular array *)
\r
584 Array A is the square array n by n. Each element A(i) , 1{SYMBOL 163 \f "Symbol"}i{SYMBOL 163 \f "Symbol"}n contains the address of row A(i,j), 1{SYMBOL 163 \f "Symbol"}j{SYMBOL 163 \f "Symbol"}n. Array B is the lower-triangular array. Each element B(i), 1{SYMBOL 163 \f "Symbol"}i{SYMBOL 163 \f "Symbol"}n, contains the address of row B(i,j), 1{SYMBOL 163 \f "Symbol"}j{SYMBOL 163 \f "Symbol"}i. Thus an assignment statement A(n,n):=B(n,n) transmits real value B(n,n) to real variable A(n,n). Assignment B(1):=A(1) transmits the address of the first row of A to variable B(1). Finally assignment B(1):=copy (A(1)) creates a copy of
\r the first row of A and assigns its address to B(1).
\r\r
585 Upper and lower bounds of an adjustable array A are determined by standard operators lower(A) and upper(A).
\r\r
588 unit sort: procedure(A:arrayof integer);
\r
589 (* insertion sort *)
\r
590 var n,i,j:integer; var x:integer;
\r
592 n:=upper(A); (* assume lower bound is 1 *)
\r
597 if x >= A(j) then exit fi;
\r \r
598 A(j+1):=A(j); j:=j-1;
\r
599 if j=0 then exit fi;
\r
604 If an array variable A refers to no array its value is equal none
\r (the standard default value of any array variable). An attempt to access an array element (e.g. A(i)) or a bound (e.g. lower(A)), where A is none, raises a run time error.
\r\r
605 6. Coroutines and semicoroutines
\r\r
606 Coroutine is a generalization of class. A coroutine object is an object such that the execution of its sequence of statements can be suspended and reactivated in a programmed manner. Consider first a simple class with a sequence of statements such that after return some
\r non-executed statements remain. The generation of its object terminates with the execution of return statement, although the object can be later reactivated. If such a class is declared as a coroutine, then its objects may be reactivated. This can be realized by attach
\r statement:
\r\r
608 where X is a reference variable designating the activating coroutine object.
\r\r
609 In general, since the moment of generation a coroutine object is either active or suspended. Any reactivation of a suspended coroutine object X (by attach(X)) causes the active coroutine object to be
\r suspended and continues the execution of X from the statement following the last executed one.
\r\r
610 Main program is also a coroutine. It is accessed through the standard variable main and may be reactivated (if suspended) by the
\r statement attach(main).
\r \r\r
613 In the example below the cooperation of two coroutines is presented. One reads the real values from an input device, another prints these values in columns on a line-printer, n numbers in a line. The input stream ends with 0.
\r\r
615 var prod:producer,cons:consumer,n:integer,mag:real,last:bool;
\r
616 unit producer: coroutine;
\r
620 read(mag); (* mag- nonlocal variable, common store *)
\r
622 then (* end of data *)
\r
630 unit consumer: coroutine(n:integer);
\r
631 var Buf:arrayof real;
\r
632 var i,j:integer;
\r \r
634 array Buf dim(1:n);
\r
641 if last then exit exit fi;
\r
644 do (* print Buf *)
\r \r
645 write(' ',Buf(i):10:2)
\r
649 (* print the rest of Buf *)
\r
650 for j:=1 to i do write(' ',Buf(j):10:2) od;
\r \r
655 prod:=new producer;
\r \r
657 cons:=new consumer(n);
\r \r
661 The above task could be programmed without coroutines at all. The presented solution is, however, strictly modular, i.e. one unit realizes the input process, another realizes the output process, and both are ready to cooperate with each other.
\r\r
662 LOGLAN-82 provides also a facility for the semi-coroutine operations. This is gained by the simple statement detach. If X is the active coroutine object, then detach reactivates that coroutine object
\r at where the last attach(X) was executed. This statement meets the
\r need for the asymetric coroutine cooperations. (by so it is called semi-coroutine operation). Operation attach requires a reactivated coroutine to be defined explicitly by the user as an actual parameter. Operation detach corresponds in some manner to return in procedures. It gives the control back to a coroutine object where the last attach(X) was executed, and that coroutine object need not be known explicitly in X. This mechanism is, however, not so secure as the normal control transfers during procedure calls and returns.
\r\r
663 In fact, the user is able to loop two coroutines traces by :
\r\r
664 attach(Y) in X
\r attach(X) in Y
\r \r\r
665 Then detach in X reactivates Y, detach in Y reactivates X.
\r\r
666 In the example below the application of detach statement is illustrated.
\r\r
668 program reader_writers;
\r
669 (* In this example a single input stream consisting of blocks of numbers, each ending with 0, is printed on two printers of different width. The choice of the printer is determined by the block header which indicates the desired number of print columns. The input stream ends with a double 0. m1 - the width of printer_1, m2 - the width of printer_2 *)
\r
670 const m1=10,m2=20;
\r \r
671 var reader:reading,printer_1,printer_2:writing;
\r \r
672 var n:integer,new_sequence:boolean,mag:real;
\r \r
674 unit writing:coroutine(n:integer);
\r \r
675 var Buf: arrayof real, i,j:integer;
\r \r
677 array Buf dim (1:n); (* array generation *)
\r \r
678 return;(* return terminates coroutine initialization *)
\r \r
680 attach(reader); (* reactivates coroutine reader *)
\r
683 (* a new sequence causes buffer Buf to be cleared up *)
\r
684 for j:=1 to i do write(' ',Buf(j):10:2) od;
\r
686 i:=0; new_sequence:=false; attach(main)
\r \r
688 i:=i+1; Buf(i):=mag;
\r
691 for j:=1 to n do write(' ',Buf(j):10:2) od;
\r
698 unit reading: coroutine;
\r \r
703 if mag=0 then new_sequence:=true; fi;
\r \r
705 (* detach returns control to printer_1 or printer_2 depending which one reactivated the reader *)
\r
709 reader:=new reading;
\r \r
710 printer_1:=new writing(m1); printer_2:=new writing(m2);
\r
715 when m1: attach(printer_1)
\r \r
716 when m2: attach(printer_2)
\r \r
717 otherwise write(" wrong data"); exit
\r \r
722 Coroutines play the substantial role in process simulation. Class Simulation provided in Simula-67 makes use of coroutines at most degree. LOGLAN-82 provides for easy simulation as well. The LOGLAN-82 class Simulation is implemented on a heap what gives lg(n) time cost (in contrast with O(n) cost of the original implementation). It covers also various simulation problems of large size and degree of complexity.
\r\r
724 Classes and prefixing are ingenius inventions of Simula-67(cf [1]). Unfortunately they were hardly ever known and, perhaps, by this have not been introduced into many programming language that gained certain popularity. Moreover, implementation constraints of Simula-67 bind prefixing and classes workableness to such a degree that both facilities cannot be used in all respects. We hope that LOGLAN-82, adopting merits and rooting up deficiencies of these constructs, will smooth their variations and vivify theirs usefulness.
\r\r
725 What is prefixing ? First of all it is a method for unit extending. Consider the simplest example:
\r\r
726 unit bill: class;
\r \r
727 var
\r dollars :real,
\r
729 year,month,day :integer;
\r
731 Assume the user desires to extend this class with new attributes. Instead of writing a completely new class, he may enlarge the existing one:
\r\r
732 unit gas_bill:bill class;
\r \r
733 var cube_meters: real;
\r \r
735 Class gas_bill is prefixed by class bill. This new declaration may appear anywhere within the scope of declaration of class bill. (In Simula-67 such a prefixing is forbidden in nested units.) Class gas_bill has all the attributes of class bill and additionally its own attributes (in this case the only one: cube_meters). The generation statement of this class has the form:
\r\r
736 z:=new gas_bill;
\r \r\r
737 where z is a reference variable of type gas_bill. Remote access to the attributes of prefixed class is standard:
\r\r
738 z.dollars:=500.5; z.year:=1982; z.month:=3; z.day:=8;
\r
739 z.cube_meters:=100000;
\r\r
741 Consider now the example of a class with parameters.
\r\r
742 Assume that in a program a class:
\r\r
743 unit id_card: class(name:string,age:integer);
\r \r
745 and its extension:
\r\r
746 unit idf_card:id card class(first name:string);
\r \r
749 Then for variable z of type id_card and variable t of type idf_card the corresponding generation statement may be the following:
\r\r
750 z:=new id_card("kreczmar",37);
\r \r
751 t:=new idf_card("Kreczmar",37,"Antoni");
\r \r\r
752 Thus the formal parameters of a class are concatenated with the formal parameters of its prefix.
\r\r
753 One can still extend class idf_card. For instance:
\r\r
754 unit idr_card:idf_card class;
\r \r
755 var children_number:integer;
\r \r
756 var birth_place:string;
\r \r
758 Prefixing allows to build up hierarchies of classes. Each one hierarchy has a tree structure. A root of such a tree is a class without prefix. One class is a successor of another class iff the first is prefixed by the latter one.
\r\r
760 Consider the prefix structure:
\r\r
775 Class H has a prefix sequence A, B, E, F, H. Let a, b, ... , h denote the corresponding unique attributes of classes A, B, ... , H, respectively. The objects of these classes have the following forms:
\r\r
777 ÚÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄ¿
\r
778 ³ a ³ ³ a ³ ³ a ³ ³ a ³
\r
779 ÀÄÄÄÄÄÄÄÄÄÄÙ ÃÄÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄÄ´
\r
780 object A ³ b ³ ³ c ³ ³ d ³
\r
781 ÀÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÙ
\r
782 object B object C object D
\r\r
783 ÚÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄ¿
\r
784 ³ a ³ ³ a ³ ³ a ³ ³ a ³
\r
785 ÃÄÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄÄ´
\r
786 ³ b ³ ³ b ³ ³ b ³ ³ b ³
\r
787 ÃÄÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄÄ´
\r
788 ³ e ³ ³ e ³ ³ e ³ ³ e ³
\r
789 ÀÄÄÄÄÄÄÄÄÄÄÙ ³ÄÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄÄ´
\r
790 object E ³ f ³ ³ f ³ ³ f ³
\r
791 ÀÄÄÄÄÄÄÄÄÄÄÙ ÃÄÄÄÄÄÄÄÄÄÄ´ ÃÄÄÄÄÄÄÄÄÄÄ´
\r
792 object F ³ g ³ ³ h ³
\r
793 ÀÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÙ
\r
794 object G object H
\r\r
796 Let Ra, Rb,..., Rh denote reference variables of types A, B,..., H, respectively. Then the following expressions are correct:
\r\r
797 Ra.a, Rb.b, Rb.a, Rg.g, Rg.f, Rh.h, Rh.f, Rh.e, Rh.b, Rh.a etc.
\r\r
798 Variable Ra may designate the object of class B (or C,..., H), i.e. the statement:
\r\r
800 is legal. But then attribute b is not accessible through dot via Ra, i.e. Ra.b is incorrect. This follows from insecurity of such a remote access. In fact, variable Ra may point any object of a class prefixed by A, in particular, Ra may point the object of A itself, which has no attribute b. If Ra.b had been correct, a compiler should have distiguish the cases Ra points to the object of A or not. But this, of course, is undistinguishable at compilation time.
\r\r
801 To allow, however, the user's access to attribute b (after instruction Ra:=new B), the instantaneous type modification is provided within the language:
\r\r
803 The correctness of this expression is checked at run time. If Ra designates an object of B or prefixed ba B, the type of the expression is B. Otherwise the expression is erroneous. Thus, for instance, the expressions:
\r\r
804 Ra qua G.b, Ra qua G.e etc.
\r \r\r
805 enable remote access to the attributes b, c, ... via Ra.
\r\r
806 So far the question of attribute concatenation was merely discussed. However the sequences of statements can be also concatenated.
\r\r
807 Consider class B prefixed with class A. In the sequence of statements of class A the keyword inner may occur anywhere, but only once. The sequence of statements of class B consists of the sequence of statements of class A with inner replaced by the sequence of
\r statements of class B.
\r\r
809 unit A :class unit B:A class
\r \r
813 ³ inner ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ inner
\r \r
816 end A; end B;
\r \r\r\r
819 In this case inner in class B is equivalent to the empty statement.
\r If class B prefixes another class, say C, then inner in B is replaced
\r by the sequence of statements of class C, and so on. If inner does not occur explicitly, an implicit occurrence of inner
\r before the final end of a class is assumed.
\r \r\r
822 Let class complex be declared as usual:
\r\r
823 unit complex: class(re,im:real);
\r \r
825 and assume one desires to declare a class mcomplex with the additional attribute module. In order the generation of class mcomplex define the value of attribute module, one can declare a class:
\r\r
826 unit mcomplex:complex class;
\r \r
827 var module:real;
\r \r
829 module:=sqrt(re*re+im*im)
\r
831 Class mcomplex may be still extended:
\r\r
832 unit pcomplex:mcomplex class;
\r \r
835 alfa:=arccos(re/module)
\r
837 For these declarations each generation of class mcomplex defines the value of attribute module, each generation of class pcomplex defines the values of attributes module and alfa.
\r\r
838 For reference variables z1, z2 z3 of type complex, the following sequence of statements illustrates the presented constructs:
\r\r
839 z1:=new complex(0,1);
\r \r
840 z2:=new mcomplex(4,7);
\r \r
841 z3:=new pcomplex(-10,12);
\r \r
842 if z2 qua mcomplex.module > 1
\r \r
846 if z3 qua pcomplex.alfa < 3.14
\r \r
848 z3.re:=-z3.re; z3.alfa:=z3.alfa+3.14;
\r
850 z1 qua mcomplex.module:= 0;
\r \r
853 Binary search tree (Bst) is a binary tree where for each node x the nodes in the left subtree are less than x, the nodes in the right subtree are greater than x. It is the well-known exercise to program the algorithms for the following operations on Bst:
\r
854 member(x) = true iff x belongs to Bst
\r
855 insert(x), enlarge Bst with x, if x does not yet belong to Bst
\r\r
856 We define both these operations in a class:
\r\r
857 unit Bst: class;
\r \r
858 unit node: class(value:integer); (* tree node *)
\r \r
859 var left,right:node;
\r \r
862 unit help: class(x:integer); (* auxiliary class *)
\r \r
871 repeat (* jump to the beginning of a loop *)
\r \r
875 p:=q; q:=q.right; repeat
\r \r
880 (* virtual instruction to beÿreplaced
\r by the body of
\r
881 a module prefixed by help *)
\r
883 unit member:help function:boolean;
\r \r
884 (* x is a formal parameter derived from the prefix help *)
\r
886 result:=q=/=none
\r \r
888 unit insert:help procedure;
\r \r
889 (* x is a formal parameter derived from the prefix help *)
\r
891 if q=/=none then return fi;
\r \r
893 if p=none then root:=q; return fi;
\r \r
894 if p.value < x then p.right:=q else p.left:=q fi;
\r \r
899 In the example the common actions of member and insert are programmed in class help. Then it suffices to use class help as a prefix of function member and procedure insert, instead of redundant occurrences of the corresponding sequence of statements in both units.
\r\r
900 Class Bst may be applied as follows:
\r\r
903 X:=new Bst; Y:=new Bst;
\r \r
904 call X.insert(5);
\r \r
905 if Y.member(-17) then ....
\r \r
907 As shown in the declaration of Bst, class may prefix not only other classes but also procedures and functions. Class may prefix blocks as well.
\r\r
910 Let class push_down (p. 5) prefix a block:
\r\r
911 pref push_down(1000) block
\r \r
915 call push(50); ...
\r \r
919 In the above block prefixed with class push_down one can use pop and push as local attributes. (They are local since the block is embedded in the prefix push down.)
\r\r
922 pref push down(1000) block
\r \r
927 (* in this block both structures
\r
928 push down and Bst are visible *)
\r
930 call insert(13);
\r \r
931 if member(10) then ...
\r \r
936 In place where classes push_down and Bst are visible together a block prefixed with Bst may be nested in a block prefixed with push_down (or vice versa). In the inner block both data structures are directly accessible. Note that this construct is illegal in Simula 67.
\r\r
938 Formal types serve for unit parametrization with respect to any non-primitive type.
\r\r
940 unit Gsort:procedure(type T; A:arrayof T; function less
\r
941 (x, y: T): boolean);
\r
942 var n,i,j:integer; var x:T;
\r \r
949 if less(A(j),x) then exit fi;
\r \r
950 A(j+1):=A(j); j:=j-1;
\r
951 if j=0 then exit fi;
\r
956 Procedure Gsort (the generalization of procedure sort from p.4) has type parameter T. A corresponding actual parameter may be an arbitrary non-primitive type. An actual parameter corresponding to A should be an array of elements of the actual type T. Function less should define the linear ordering on the domain T.
\r\r
957 For instance, the array A of type bill (cf p.7) may be sorted with respect to attribute dollars , if the function:
\r\r
958 unit less: function(t,u:bill):boolean;
\r \r
960 result:=t.dollars <= u.dollars
\r
962 is used as an actual parameter:
\r\r
963 call Gsort(bill,A,less);
\r \r\r
964 If the user desires to sort A with respect to date, it is sufficient to declare :
\r\r
965 unit earlier:function(t,u:bill):boolean;
\r \r
967 if t.year < u.year then result:= true; return fi;
\r \r
968 if t.year=u.year
\r \r
970 if t.month < u.month then result:=true; return fi;
\r \r
971 if t.month=u.month then result:=t.day<=u.day fi
\r \r
974 and to call: call Gsort(bill,A,earlier);
\r \r\r
975 9. Protection techniques
\r\r
976 Protection techniques ease secure programming. If a program is large, uses some system classes, is designed by a team etc., this is important (and non-trivial) to impose some restrictions on access to non-local attributes.
\r\r
977 Let us consider a data structure declared as a class. Some of its attributes should be accessible for the class users, the others should not. For instance, in class Bst (p.7) the attributes member and insert are to be accessible. On the other hand the attributes root, node and help should not be accessible, even for a meddlesome user. An improper use of them may jeopardize the data structure invariants.
\r\r
978 To forbid the access to some class attributes the three following protection mechanisms are provided:
\r\r
979 close, hidden, and taken.
\r \r\r
980 The protection close defined in a class forbids remote access to the
\r specified attributes. For example, consider the class declaration:
\r\r
983 var x: integer, y,z:real;
\r \r
986 Remote access to the attributes x,y,z from outside of A is forbidden.
\r\r
987 The protection hidden (with akin syntax) does not allow to use the
\r specified attributes form outside of A neither by the remote access nor in the units prefixed by A. The only way to use a hidden attribute is to use it within the body of class A.
\rProtection taken defines these attributes derived from prefix, which
\r the user wishes to use in the prefixed unit. Consider a unit B prefixed by a class A. In unit B one may specify the attributes of A which are used in B. This protects the user against an unconscious use of an attribute of class A in unit B (because of identifier conflict). When taken list does not occur, then by default, all non-hidden attributes of class A are accessible in unit B.
\r\r
988 10. Programmed deallocation
\r\r
989 The classical methods implemented to deallocate class objects are based on reference counters or garbage collection. Sometimes the both methods may be combined. A reference counter is a system attribute holding the number of references pointing to the given object. Hence any change of the value of a reference variable X is followed by a corresponding increase or decrease of the value of its reference counter. When the reference counter becomes equal 0, the object can be deallocated.
\r\r
990 The deallocation of class objects may also occur during the process of garbage collection. During this process all unreferenced objects are found and removed (while memory may be compactified). In order to keep the garbage collector able to collect all the garbage, the user should clear all reference variables , i.e. set to None, whenever possible. This system has many disadvantages. First of all, the programmer is forced to clear all reference variables, even those which are of auxiliary character. Moreover, garbage collector is a very expensive mechanism and thus it can be used only in emergency cases.
\r\r
991 In LOGLAN a dual operation to the object generator, the so-called object deallocator is provided. Its syntactic form is as follows:
\r\r
993 where X is a reference expression. If the value of X points to no object (none) then kill(X) is equivalent to an empty statement. If the
\r value of X points to an object O, then after the execution of kill(X),
\r the object O is deallocated. Moreover all reference variables which pointed to O are set to none. This deallocator provides full security,
\r i.e. the attempt to access the deallocated object O is checked and results in a run-time error.
\r\r
995 Y:=X; kill(X); Y.W:=Z;
\r \r\r
996 causes the same run-time error as:
\r\r
997 X:=none; X.W:=Z;
\r \r\r
998 The system of storage management is arranged in such a way that the frames of killed objects may be immediately reused without the necessity of calling the garbage collector, i.e. the relocation is performed automatically. There is nothing for it but to remember not to use remote access to a killed object. (Note that the same problem appears when remote access X.W is used and X=none).
\r \r\r
1003 Below a practical example of the programmed deallocation is presented. Consider class Bst (p.7). Let us define a procedure that deallocates the whole tree and is called with the termination of the class Bst.
\r\r
1004 unit Bst:class;
\r \r
1005 (* standard declarations list of Bst *)
\r
1006 unit kill_all:procedure(p:node);
\r \r
1007 (* procedure kill_all deallocates a tree with root p *)
\r
1009 if p= none then return fi;
\r \r
1010 call kill_all(p.left);
\r \r
1011 call kill_all(p.right);
\r \r
1016 call kill_all(root)
\r \r
1018 Bst may be applied as a prefix:
\r\r
1022 and automatically will cause the deallocation of the whole tree after return to call kill_all(root) from the prefixed block.
\r \r\r
1023 To use properly this structure by remote accessing one must call kill_all by himself:
\r\r
1024 unit var X,Y:Bst;
\r \r
1027 X:=new Bst; Y:=new Bst;
\r \r
1029 (* after the structures' application *)
\r
1030 call X.kill_all(X.root);
\r \r
1032 call Y.kill_all(Y.root);
\r \r
1036 Finally note that deallocator kill enables deallocation of array
\r objects, and suspended coroutines and processes as well (cf p.13).
\r\r
1037 11. Exception handling
\r\r
1038 Exceptions are events that cause interruption of normal program execution. One kind of exceptions are those which are raised as a result of some run time errors. For instance, when an attempt is made to access a killed object, when the result of numeric operation does not lie within the range, when the dynamic storage allocated to a program is exceeded etc.
\r\r
1039 Another kind of exceptions are those which are raised explicitly by a user (with the execution of the raise statement).
\r\r
1040 The response to exceptions (one or more) is defined by an exception handler. A handler may appear at the end of declarations of any unit. The corresponding actions are defined as sequences of statements preceded by keyword when and an exception identifier.
\r \r\r
1042 In procedure squareeq (p.3) we wish to include the case when a=0. It may be treated as an exception (division by zero).
\r\r
1043 unit squareeq(a,b,c:real;output xr,xi,yr,yi:real);
\r \r
1044 var delta:real;
\r \r
1046 when division_by_zero:
\r \r
1049 xi,yr,yi:=0; xr:=-c/b; terminate
\r \r
1051 raise Wrong_data(" no roots")
\r \r
1056 end squareeq;
\r \r\r
1057 The handler declared in that procedure handles the only one exception (division_by_zero).
\r\r
1058 When an exception is raised, the corresponding handler is searched for, starting from the active object and going through return traces. If there is no object containing the declaration of the handler, then the program (or the corresponding process) is terminated. Otherwise the control is transferred to the first found handler.
\r\r
1059 In our example the handler is declared within the unit itself, so control is passed to a sequence:
\r\r
1061 Therefore, when b=/=0, the unique root of square equation will be determined and the procedure will be normally terminated (terminate).
\r In general, terminate causes that all the objects are terminated,
\r starting from that one where the exception was raised and ending on that one where the handler was found. Then the computation is continued in a normal way.
\r\r
1062 In our example, when b=0, a new exception is raised by the user. For this kind of exceptions , the exception itself should be declared (because it is not predefined as a run time error). Its declaration may have parameters which are transmitted to a handler. The exception declaration need not be visible by the exception handler. However the way the handler is searched for does not differ from the standard one. Consider an example:
\r\r
1064 signal Wrong_data(t:string);
\r \r
1072 Exception Wrong_data may be raised wherever its declaration (signal
\r Wrong_data) is visible. When its handler is found the specified sequence of actions is performed. In the example above different handlers may be defined in inner units to the main block where squereeq is called.
\r\r
1073 The case a=0 could be included , of course, in a normal way, i.e. by a corresponding conditional statement occurring in the procedure body. But the case a=0 was assumed to be exceptional (happens scarcely). Thus the evaluation of condition a=0 would be mostly unnecessary. As can be noticed thanks to exceptions the above problem can be solved with the minimal waste of run time.
\r\r
1074 12. Concurrent processes.
\r\r
1075 Loglan allows to create and execute objects-processes. They can operate simultaneously on different computers linked into a LAN network or a few processes can share one processor (its time-slices).
\r\r
1076 Process modules are different from the classes and coroutines for, they use the keyword process. The syntax of process modules is otherwise the same. In a process one can use a few more instructions: resume (resume a process which is passive), stop - make the current process passive, etc.
\r\r
1077 All processes (even those executed on the same computer) are implemented as distributed, i.e. without any shared memory. This fact implies some restrictions on how processes may be used. Not all restrictions are enforced by the present compiler, so it is the programmer's responsibility to respect them. For the details see the User's Manual.
\r\r
1078 Semantics of the generator new is slightly modified when applied to the processes. The first parameter of the first process unit in the prefix sequence must be of type INTEGER. This parameter denotes the node number of the computer on which this process will be created. For a single computer operation this parameter must be equal to 0.
\r\r
1081 unit A:class(msg:string);
\r
1084 unit P:A process(node:integer, pi:real);
\r
1092 (* Create process on node 4. The first parameter is the *)
\r
1093 (*string required by the prefix A, the second is the node number *)
\r
1094 x := new P("Hello", 4, 3.141592653);
\r
1100 COMMUNICATION MECHANISM
\r
1102 Processes may communicate and synchronize by a mechanism based on rendez-vous. It will be referred to as "alien call" in the following description.
\r\r
1103 An alien call is either:
\r
1104 - a procedure call performed by a remote access to a process object, or
\r
1105 - a call of a procedure which is a formal parameter of a process, or
\r
1106 - a call of a procedure which is a formal parameter of an alien-called procedure (this is a recursive definition).
\r\r
1107 Every process object has an enable mask. It is defined as a subset of all procedures declared directly inside a process unit or any unit from its prefix sequence (i.e. subset of all procedures that may be alien-called).
\r\r
1108 A procedure is enabled in a process if it belongs to that process' enable mask. A procedure is disabled if it does not belong to the enable mask.
\r\r
1109 Immediately after generation of a process object its enable mask is empty (all procedures are disabled).
\r\r
1110 Semantics of the alien call is different from the remote call described in the report. Both the calling process and the process in which the procedure is declared (i.e. the called process) are involved in the alien call. This way the alien call may be used as a synchronization mechanism.
\r\r
1111 The calling process passes the input parameters and waits for the call to be completed.
\r\r
1112 The alien-called procedure is executed by the called process. Execution of the procedure will not begin before certain conditions are satisfied. First, the called process must not be suspended in any way. The only exception is that it may be waiting during the ACCEPT statement (see below). Second, the procedure must be enabled in the called process.
\r\r
1113 When the above two conditions are met the called process is interrupted and forced to execute the alien-called procedure (with parameters passed by the calling process).
\r\r
1114 Upon entry to the alien-called procedure all procedures become disabled in the called process.
\r\r
1115 Upon exit the enable mask of the called process is restored to that from before the call (regardless of how it has been changed during the execution of the procedure). The called process is resumed at the point of the interruption. The execution of the ACCEPT statement is ended if the called process was waiting during the ACCEPT (see below). At last the calling process reads back the output parameters and resumes its execution after the call statement.
\r\r
1116 The process executing an alien-called procedure can easily be interrupted by another alien call if the enable mask is changed.
\r\r
1117 There are some new language constructs associated with the alien call mechanism. The following statements change the enable mask of a process:
\r
1118 ENABLE p1, ..., pn
\r
1119 enables the procedures with identifiers p1, ..., pn. If there are any processes waiting for an alien call of one of these procedures, one of them is chosen and its request is processed. The scheduling is done on a FIFO basis, so it is strongly fair. The statement:
\r
1120 DISABLE p1, ..., pn
\r
1121 disables the procedures with identifiers p1, ..., pn.
\r\r
1122 In addition a special form of the RETURN statement:
\r
1123 RETURN ENABLE p1, ..., pn DISABLE q1, ..., qn
\r
1124 allows to enable the procedures p1, ..., pn and disable the procedures q1,...,qn after the enable mask is restored on exit from the alien-called procedure. It is legal only in the alien-called procedures (the legality is not enforced by the compiler).
\r\r
1125 A called process may avoid busy waiting for an alien call by means of the ACCEPT statement:
\r
1126 ACCEPT p1, ..., pn
\r
1127 adds the procedures p1, ..., pn to the current mask, and waits for an alien call of one of the currently enabled procedures. After the procedure return the enable mask is restored to that from before the ACCEPT statement.
\r\r
1128 Note that the ACCEPT statement alone (i.e. without any ENABLE/DISABLE statements or options) provides a sufficient communication mechanism. In this case the called process may execute the alien-called procedure only during the ACCEPT statement (because otherwise all procedures are disabled). It means that the enable mask may be forgotten altogether and the alien call may be used as a pure totally synchronous rendez-vous. Other constructs are introduced to make partially asynchronous communication patterns possible.
\r\r
1130 Below find a complete listing of a simple example - monitors.
\r\r
1134 (* this an example showing 5 processes: two of them are in fact monitors, one controls the screen=ekran *)
\r
1136 unit ANSI: class;
\r
1137 (* CHECK whether config.sys contains a line
\r
1139 the class ANSI enables operations on cursor,
\r
1140 and bold, blink, underscore etc. *)
\r
1142 unit Bold : procedure;
\r
1144 write( chr(27), "[1m")
\r
1147 unit Blink : procedure;
\r
1149 write( chr(27), "[5m")
\r
1152 unit Reverse : procedure;
\r
1154 write( chr(27), "[7m")
\r
1157 unit Normal : procedure;
\r
1159 write( chr(27), "[0m")
\r
1162 unit Underscore : procedure;
\r
1164 write( chr(27), "[4m")
\r
1167 unit inchar : IIUWgraph function : integer;
\r
1168 (*podaj nr znaku przeslanego z klawiatury *)
\r
1173 if i <> 0 then exit fi;
\r
1178 unit NewPage : procedure;
\r
1180 write( chr(27), "[2J")
\r
1183 unit SetCursor : procedure(row, column : integer);
\r
1184 var c,d,e,f : char,
\r
1191 i := column div 10;
\r
1192 j := column mod 10;
\r
1195 write( chr(27), "[", c, d, ";", e, f, "H")
\r
1200 unit monitor: process(node:integer, size:integer,e: ekran);
\r
1202 var buf: arrayof integer,
\r
1203 nr,i,j,k1,k2,n1,n2: integer;
\r
1206 unit lire: procedure(output k: integer);
\r
1208 call e.druk(13,2+nr*30+k1,0,k2);
\r
1209 call e.druk(13,2+nr*30+(i-1)*6,1,buf(i));
\r
1213 i:= (i mod size)+1;
\r
1216 call e.printtext("i equal j")
\r
1220 unit ecrire: procedure(n:integer);
\r
1222 call e.druk(13,2+nr*30+n1,0,n2);
\r
1223 call e.druk(13,2+nr*30+(j-1)*6,2,n);
\r
1227 j := (j mod size)+1;
\r
1230 call e.printtext("j equal i")
\r
1234 array buf dim(1:size);
\r
1236 for i := 1 to size
\r
1239 call e.druk(13,2+nr*30+(i-1)*6,0,buf(i));
\r
1247 (* end initialize buffer *)
\r
1251 accept lire, ecrire
\r
1255 unit prcs: process(node,nr:integer, mleft,mright:
\r
1256 monitor, e: ekran);
\r
1260 call e.SetCursor(8+(nr-1)*10,29);
\r
1263 call e.printtext("<-- p1 <--");
\r
1265 call e.printtext("--> p2 -->");
\r
1269 call mleft.lire(l) ;
\r
1270 call e.druk(11+(nr-1)*4,31-(nr-1)*8,1,l);
\r
1272 call mright.ecrire(l) ;
\r
1273 call e.druk(10+(nr-1)*6,23+(nr-1)*8,2,l);
\r
1277 if o = -79 then call endrun fi;
\r
1282 unit ekran : ANSI process(nrprocesora: integer);
\r
1283 unit printtext: procedure(s:string);
\r
1289 unit druk: procedure(gdzieW,gdzieK,jak,co:integer);
\r
1291 call SetCursor(gdzieW,gdzieK);
\r
1293 if jak=0 then call Normal else
\r
1294 if jak=1 then call Reverse else
\r
1295 if jak=2 then call Bold
\r
1303 unit print: procedure (i:integer);
\r
1310 do accept inchar,
\r
1311 Normal,NewPage, SetCursor, Bold, Underscore,
\r
1312 Reverse, Blink, print, printtext, druk
\r
1316 var m1,m2:monitor,
\r
1320 begin (* ----- HERE IS THE MAIN PROGRAM ----- *)
\r
1321 (* create a configuration *)
\r
1326 m1 := new monitor(0,4,e);
\r
1327 m2 := new monitor(0,5,e);
\r
1329 p1 := new prcs(0,1,m2,m1,e);
\r
1330 p2 := new prcs(0,2,m1,m2,e);
\r
1340 Bartol,W.M., et al.
\r
1341 Report on the Loglan 82 programming Language,
\r
1342 Warszawa-Lodz, PWN, 1984
\r
1344 O.-J. Dahl, B. Myhrhaug, K. Nygaard,
\r
1345 Simula 67 Common Base Language,
\r
1346 Norwegian Computing Center, Oslo, 1970 the mother of object languages!!
\r
1349 Monitors, an operating system structuring concept.
\r
1350 CACM,vol.17,N.10,October 1974,pp.549-57
\r
1354 Institute of Informatics, University of Warsaw 1983, 1988
\r
1355 LITA, Université de Pau, 1993
\r
1356 (distributed together with this package)
\r
1358 A.Kreczmar, A.Salwicki, M. Warpechowski,
\r
1359 Loglan'88 - Report on the Programming Language,
\r
1360 Lecture Notes on Computer Science vol. 414, Springer Vlg, 1990,
\r
1361 ISBN 3-540-52325-1
\r
1363 /* if you can read polish, there is a good manual of Loglan */
\r
1364 A.Szalas, J.Warpechowska,
\r
1366 Wydawnictwa Naukowo-Techniczne, Warszawa, 1991 ISBN 82-204-1295-1
\r
1368 see also the Readings file of this distribution.
\r
1370 {PAGE|34} A.Kreczmar Nov.1990
\r
1372 Loglan'82 {PAGE|33}
\r