24 the programming language
\r
32 Basic constructs and facilities
\r
35 Author: Antoni Kreczmar
\r
36 Institute of Informatics, Warsaw University
\r
40 LOGLAN-82 is a universal programming language designed at the
\r
41 Institute of Informatics, University of Warsaw. Its syntax is
\r
42 patterned upon Pascal's. Its rich semantics includes the classical
\r
43 constructs and facilities offered by the Algol-family programming
\r
44 languages as well as more modern facilities, such as concurrency and
\r
46 The basic constructs and facilities of the LOGLAN-82 programming
\r
49 1) A convenient set of structured statements,
\r
51 2) Modularity (with the possibility of module nesting and extending),
\r
53 3) Procedures and functions (fully recursive; procedures and functions
\r
54 can be used also as formal parameters),
\r
56 4) Classes (as a generalization of records) which enable to define
\r
57 complex structured types, data structures, packages, etc.,
\r
59 5) Adjustable arrays whose bounds are determined at run-time in such a
\r
60 way that multidimensional arrays may be of various shapes, e.g.
\r
61 triangular, k-diagonal, streaked, etc.,
\r
63 6) Coroutines and semi-coroutines,
\r
65 7) Prefixing - the facility borrowed from Simula-67, substantially
\r
66 generalized in LOGLAN-82 - which enables to build up hierarchies of
\r
67 types and data structures, problem-oriented languages, etc.,
\r
69 8) Formal types treated as a method of module parametrization,
\r
71 9) Module protection and encapsulation techniques,
\r
73 10) Programmed deallocator - a tool for efficient and secure garbage
\r
74 collection, which allows the user to implement the optimal
\r
75 strategy of storage management,
\r
77 11) Exception handling which provides facilities for dealing with
\r
78 run-time errors and other exceptional situations raised by the
\r
81 12) Separate compilation techniques,
\r
83 13) Concurrency easily adaptable to any operating system kernel and
\r
84 allowing parallel programming in a natural and efficient way.
\r
86 The language covers system programming, data processing, and
\r
87 numerical computations. Its constructs represent the state-of-art and
\r
88 are efficiently implementable. Large systems consisting of many
\r
89 cooperating modules are easily decomposed and assembled, due to the
\r
90 class concept and prefixing.
\r
91 LOGLAN-82 constructs and facilities have appeared and evolved
\r
92 simultaneously with the experiments on the first pilot compiler
\r
93 (implemented on Mera-400 Polish minicomputer). The research on
\r
94 LOGLAN-82 implementation engendered with new algorithms for static
\r
95 semantics, context analysis, code generation, data structures for
\r
96 storage management etc.
\r
97 The LOGLAN-82 compiler provides a keen analysis of syntactic and
\r
98 semantic errors at compilation as well as at run time. The object code
\r
99 is very efficient with respect to time and space. The completeness of
\r
100 error checking guarantees full security and ease of program debugging.
\r
102 1. Compound statements
\r
103 ######################
\r
105 Compound statements in LOGLAN-82 are built up from simple statements
\r
106 (like assignment statement e.g. x:=y+0.5, call statement e.g. callP(7,x+5) e
\r
107 The syntax of conditional statement is as follows:
\r
109 if boolean expression
\r
111 sequence of statements
\r
113 sequence of statements
\r
116 where "else part" may be omitted:
\r
118 if boolean expression
\r
120 sequence of statements
\r
123 The semantics of conditional statement is standard. The keyword fi
\r
124 allows to nest conditional statements without appearence of "dangling
\r
132 x2:=sqrt(delta)/a/2;
\r
137 x1:=-b/a/2+x2; x2:=x1-2*x2
\r
144 write(" no real roots")
\r
148 The statements in a sequence of statements are separated with
\r
149 semicolons (semicolon may end a sequence , and then, the last
\r
150 statement in the sequence is the empty statement).
\r
152 The short circuit control forms are realized in LOGLAN-82 by the
\r
153 conditional statements with orif (or andif) list. A conditional
\r
154 statement with orif list has the form: orif
\r
156 if wb1 orif wb2 ... orif wbk
\r
158 sequence of statements
\r
160 sequence of statements
\r
163 and corresponds somehow to a conditional statement:
\r
165 if wb1 or wb2 ... or wbk
\r
167 sequence of statements
\r
169 sequence of statements
\r
172 The above conditional statement (without orif list) selects for
\r
173 execution one of two sequences of statements, depending on the truth
\r
174 value of the boolean expression:
\r
176 wb1 or wb2 or ... wbk
\r
178 which is always evaluated till the end. For the execution of the
\r
179 conditional statement with orif list the specified conditons
\r
180 wb1,...,wbk are evaluated in succession, until the first one evaluates
\r
181 to true. Then the rest of the sequence wb1,...,wbk is abandoned and
\r
182 "then part" is executed. If none of the conditions wb1,...,wbk
\r
183 evaluates to true "else part" is executed (if any).
\r
184 Conditional statements with orif list facilitate to program those
\r
185 conditions, which evaluation to the end may raise a run-time error.
\r
189 The execution of the statement:
\r
191 if i>n or A(i)=0 then i:=i-1 else A(i):=1 fi
\r
193 where the value of i is greater than n, and A is an array with upper
\r
194 bound n, will raise the run-time error. Then the user can write:
\r
196 if i>n orif A(i)=0 then i:=i-1 else A(i):=1 fi
\r
198 what allows to avoid this run-time error and probably agrees with his
\r
201 Conditional statement with andif list has the form:
\r
203 if wb1 andif wb2 ... andif wbk
\r
205 sequence of statements
\r
207 sequence of statements
\r
210 For the execution of this kind of statements, the conditions
\r
211 wb1,...,wbk are evaluated in succession, until the first one evaluates
\r
212 to false; then "else part" (if any) is executed. Otherwise "then part"
\r
215 Iteration statement in LOGLAN-82 has the form:
\r
217 do sequence of statements od
\r
218 An iteration statement specifies repeated execution of the sequence
\r
219 of statements and terminates with the execution of the simple
\r
228 if abs(t) < 1.0E-10 then exit fi;
\r
232 If two iteration statements are nested, then double exit in the
\r
233 inner one terminates both of them.
\r
240 s,t:=1; i:=1; x:=x+0.2;
\r
243 if i > n then exit exit fi; (* termination of both loops *)
\r
244 if t < 1 then exit fi; (* termination of the inner loop *)
\r
249 In the example above simultaneous assignment statements are
\r
250 illustrated (e.g. r,x:=0) and comments, which begin with a left
\r
251 parenthesis immediately followed by an asterisk and end with an
\r
252 asterisk immediately followed by a right parenthesis.
\r
254 Triple exit terminates three nested iteration statements, four exit
\r
255 terminates four nested iteration statements etc.
\r
257 The iteration statement with while condition: w
\r
259 while boolean expression
\r
261 sequence of statements
\r
267 if not boolean expression then exit fi;
\r
268 sequence of statements
\r
271 The iteration statements with controlled variables (for statements)
\r
274 for j:=wa1 step wa2 to wa3
\r
276 sequence of statements
\r
281 for j:=wa1 step wa2 downto wa3
\r
283 sequence of statements
\r
286 The type of the controlled variable j must be discrete. The value of
\r
287 this variable in the case of the for statement with to is increased,
\r
288 and in the case of the for statement with downto is decreased. The
\r
289 discrete range begins with the value of wa1 and changes with the step
\r
290 equal to the value of wa2. The execution of the for statement with to
\r
291 terminates when the value of j for the first time becomes greater than
\r
292 the value of wa3 (with downto when the value of j for the first time
\r
293 becomes less than the value of wa3). After the for statement
\r
294 termination the value of its controlled variable is determined and
\r
295 equal to the first value exceeding the specified discrete range. The
\r
296 values of expressions wa1, wa2 and wa3 are evaluated once, upon entry
\r
297 to the iteration statement. Default value of wa2 is equal 1 (when the
\r
298 keyword step and expression wa2 are omitted).
\r
299 for or while statements may be combined with exit statement.
\r
306 if x=A(j) then exit fi;
\r
309 The above iteration statement terminates either for the least j,
\r
310 1<=j<=n, such that x=A(j) or for j=n+1 when x=/=A(j), j=1,...,n.
\r
312 To enhance the user's comfort, the simple statement repeat is
\r
313 provided. It may appear in an iteration statement and causes the
\r
314 current iteration to be finished and the next one to be continued
\r
315 (something like jump to CONTINUE in Fortran's DO statements).
\r
323 if A(i) < 0 then repeat fi; (* jump to od, iterations are continued *)
\r
324 if i > m then exit fi; (* iteration statement is terminated *)
\r
328 Just as exit, repeat may appear in for statement or while statement.
\r
329 Then the next iteration begins with either the evaluation of a new
\r
330 value of the controlled variable (for statement) or with the
\r
331 evaluation of the condition (while statement).
\r
333 Case statement in LOGLAN-82 has the form:
\r
343 where WA is an expression , L1,...,Lk are constants and I1,..., Ik,I
\r
344 are sequences of statements.
\r
345 A case statement selects for execution a sequence of statements Ij,
\r
346 1<=j<=k, where the value of WA equals Lj. The choice otherwise covers
\r
347 all values (possibly none) not given in the previous choices. The
\r
348 execution of a case statement chooses one and only one alternative
\r
349 (since the choices are to be exhaustive and mutually exclusive).
\r
354 Modular structure of the language is gained due to the large set of
\r
355 means for module nesting and extending. Program modules (units) are
\r
356 blocks, procedures, functions, classes, coroutines and processes.
\r
357 Block is the simplest kind of unit. Its syntax is the following:
\r
360 lists of declarations
\r
362 sequence of statements
\r
365 The sequence of statements commences with the keyword begin (it may
\r
366 be omitted when this sequence is empty). The lists of declarations
\r
367 define the syntactic entities (variables, constants, other units),
\r
368 whose scope is that block. The syntactic entities are identified in
\r
369 the sequence of statements by means of names (identifiers).
\r
376 var x,y:real, i,j,k: integer, b: boolean;
\r
379 read(i,j); (* read two integers *)
\r
380 x,y:=n/(i+j); (* simultaneous assignment *)
\r
381 read(c) ; (* read a character *)
\r
382 b:= c = 'a'; (* 'a' a character *)
\r
385 write(x+y/k:10:4); (* print the value of x+y/k
\r
386 in the field of 10 characters, 4 digits after the point *)
\r
390 In the lists of declarations semicolons terminate the whole lists,
\r
391 not the lists elements. Any declaration list must begin with the
\r
392 pertinent keyword (var for variables, const for constants etc.). The
\r
393 value of an expression defining a constant must be determinable
\r
394 statically (at compilation time).
\r
395 Program in LOGLAN-82 may be a block or alternatively may be of the
\r
399 lists of declarations
\r
401 sequence of statements
\r
404 Then the whole program can be identified by that name (the source as
\r
405 well as the object code).
\r
407 A block can appear in the sequence of statements (of any unit), thus
\r
408 it is a statement. (Main block is assumed to appear as a statement of
\r
409 the given job control language.)
\r
410 For the execution of a block statement the object of block is
\r
411 created in a computer memory, and then, the sequence of statements is
\r
412 performed. The syntactic entities declared in the block are allocated
\r
413 in its object. After a block's termination its object is automatically
\r
414 deallocated (and the corresponding space may be immediately reused).
\r
415 The modular structure of the language works "in full steam" when not
\r
416 only blocks, but the other kinds of units are also used. They will be
\r
417 described closer in the following points.
\r
418 Unit nesting allows to build up hierarchies of units and supports
\r
419 security of programming. It follows from the general visibility rules;
\r
420 namely, a syntactic entity declared in an outer unit is visible in an
\r
421 inner one (unless hidden by an inner declaration). On the other hand,
\r
422 a syntactic entity declared in an inner unit is not visible from an
\r
429 var a,b,c:real, i,j,k:integer;
\r
435 j:=a; k:=j+b; write(" this is the inner block ",j,k)
\r
437 write(" this is the outer block ",i,a:20)
\r
440 In this program, first the main block statement is executed (with
\r
441 variables a,b,c,i,j,k). Next, after the read statement, the inner
\r
442 block statement is executed (with variables j,k). In the inner block
\r
443 the global variables j,k are hidden by the local ones.
\r
446 3. Procedures and functions
\r
447 ###########################
\r
449 Procedures and functions are well-known kinds of units. Their syntax
\r
450 is modelled on Pascal's, though with some slight modifications.
\r
451 Procedure (function) declaration consists of a specification part and
\r
456 unit Euclid: function(i,j:integer):integer;
\r
460 if j=0 then exit fi;
\r
461 k:=i mod j; i:=j; j:=k
\r
466 Procedure or function specification begins with its identifier
\r
467 preceded by the keyword unit. (The same syntax concerns any other module
\r
468 named unit.) Then follows its kind declaration, its formal parameters
\r
469 (if any), and the type of the returned value (only for functions). A
\r
470 body consists of declaration lists for local entities and a sequence
\r
471 of statements. The keyword begin commences the sequence of statements
\r
472 , and is omitted , if this sequence is empty. The value returned by a
\r
473 function equals to the most recent value of the standard variable
\r
474 "result", implicitly declared in any function. This variable can be
\r
475 used as a local auxiliary variable as well.
\r
479 unit Newton: function(n,m:integer):integer;
\r
482 if m > n then return fi;
\r
484 for i:=2 to m do result:=result*(n-i+1) div i od
\r
486 The optional identifier at the end of a unit must repeat the
\r
487 identifier of a unit. It is suggested that the compilers check the
\r
488 order of unit nesting, so these optional occurrences of identifiers
\r
489 would facilitate program debugging.
\r
490 All the local variables of a unit are initialized (real with 0.0,
\r
491 integer with 0, boolean with false etc.). Thus , for instance, the
\r
492 value of function Newton is 0 for m>n, since "result" is also
\r
493 initialized, as any other local variable.
\r
495 The return statement (return) completes the execution of a procedure
\r
496 (function) body,i.e. return is made to the caller. If return does not
\r
497 appear explicitly, return is made with the execution of the final end
\r
498 of a unit. Upon return to the caller the procedure (function) object
\r
500 Functions are invoked in expressions with the corresponding list of
\r
501 actual parameters. Procedures are invoked by call statement (also with
\r
502 the corresponding list of actual parameters).
\r
506 i:=i*Euclid(k,105)-Newton(n,m+1);
\r
509 Formal parameters are of four categories: variable parameters,
\r
510 procedure parameters, function parameters and type parameters (cf
\r
511 p.8). Variable parameters are considered local variables to the unit.
\r
512 A variable parameter has one of three transmission modes: input,
\r
513 output or inout. If no mode is explicitly given the input mode is
\r
514 assumed. For instance in the unit declaration:
\r
516 unit P: procedure(x,y:real,b:boolean;output c:char,i:integer;inout j:integer);
\r
518 x,y,b are input parameters , c,i are output parameters , and j is
\r
521 Input parameter acts as a local variable whose value is initialized
\r
522 by the value of the corresponding actual parameter. Output parameter
\r
523 acts as a local variable initialized in the standard manner (real with
\r
524 0.0, integer with 0, boolean with false etc.). Upon return its value
\r
525 is assigned to the corresponding actual parameter, in which case it
\r
526 must be a variable. However the address of such an actual parameter is
\r
527 determined upon entry to the body. Inout parameter acts as an input
\r
528 parameter and output parameter together.
\r
532 unit squareeq: procedure(a,b,c:real;output xr,xi,yr,yi:real);
\r
533 (* given a,b,c the procedure solves square equation : ax*x+bx+c=0.
\r
534 xr,xi- real and imaginary part of the first root
\r
535 yr,yi- real and imaginary part of the second root *)
\r
538 a:=2*a; c:=2*c; delta:=b*b-a*c;
\r
542 if delta=0 then return fi; (*xi=yi=0 by default*)
\r
543 delta:=sqrt(-delta);
\r
544 xi:=delta/a; yi:=-xi;
\r
547 delta:=sqrt(delta);
\r
550 xr:=delta/a; yr:=-xr;
\r
553 if b>0 then b:=b+delta else b:=b-delta fi;
\r
554 xr:=-b/a; yr:=-c/b;
\r
557 A procedure call to the above unit may be the following:
\r
559 call squareeq(3.75*H,b+7,3.14,g,gi,h,hi);
\r
560 where g,h,gi,hi are real variables.
\r
563 No restriction is imposed on the order of declarations. In
\r
564 particular, recursive procedures and functions can be declared without
\r
565 additional announcements (in contrast to Pascal).
\r
570 For two recursive sequences defined as:
\r
572 a(n)=b(n-1)+n+2 n>0
\r
573 b(n)=a(n-1)+(n-1)*n n>0
\r
576 one can declare two functions:
\r
578 unit a: function(n:integer):integer;
\r
580 if n>0 then result:=b(n-1)+n+2 fi
\r
582 unit b: function(n:integer):integer;
\r
584 if n>0 then result:=a(n-1)+(n-1)*n fi
\r
589 k:=a(100)*b(50)+a(15);
\r
591 Functions and procedures can be formal parameters as well.
\r
596 unit Bisec: procedure(a,b,eps:real;output x:real;function f(x:real):real);
\r
597 (*this procedures searches for zero of continous function f in segment (a,b) *)
\r
598 var h:real,s:integer;
\r
601 if sign(f(b))=s then return fi; (* wrong segment *)
\r
605 if h < eps then return fi;
\r
606 if sign(f(x))=s then a:=x else b:=x fi
\r
610 In the above declaration, after the input variable parameters
\r
611 a,b,eps and the output variable parameter x, a function parameter f
\r
612 appears. Note that its specification part is complete. Thus the check
\r
613 of actual-formal parameter compatibility is possible at compilation
\r
614 time. Making use of this syntactic facility is not possible in
\r
615 general, if a formal procedure (function) is again a formal parameter
\r
616 of a formal procedure (function). The second degree of formal
\r
617 procedures (functions) nesting is rather scarce, but LOGLAN-82 admits
\r
618 such a construct. Then formal procedure (function) has no
\r
619 specification part and the full check of actual-formal parameter
\r
620 compatibility is left to be done at run time.
\r
625 unit P: procedure(j:integer;procedure G(i:integer;procedure H));
\r
632 Procedure G is a first degree parameter, therefore it occurs with
\r
633 complete specification part. Procedure H is a second degree parameter
\r
634 and has no specification part. In this case a procedure call can be
\r
635 strongly recursive:
\r
642 Class is a facility which covers such programming constructs as
\r
643 structured type, package, access type, data structure etc. To begin
\r
644 with the presentation of this construct, let us consider a structured
\r
645 type assembled from primitive ones:
\r
648 var dollars :real,
\r
650 year,month,day :integer;
\r
653 The above class declaration has the attributes : dollars (real),
\r
654 not_paid (boolean), and year,month,day (integer). Wherever class bill
\r
655 is visibile one can declare variables of type bill:
\r
659 The values of variables x, y, z can be the addresses of objects of
\r
660 class bill. These variables are called reference variables. With
\r
661 reference variable one can create and operate the objects of reference
\r
664 An object of a class is created by the class generation statement
\r
665 (new), and thereafter, its attributes are accessed through dot
\r
668 x:=new bill; (* a new object of class bill is created *)
\r
669 x.dollars:=500.5; (* define amount *)
\r
670 x.year:=1982; (* define year *)
\r
671 x.month:=3; (* define month *)
\r
672 x.day:=8; (* define day *)
\r
673 y:=new bill; (* create a new object *)
\r
674 y.not_paid:=true; (* bill not_paid *)
\r
675 z:=y; (* variable z points the same object as variable y *)
\r
677 If an object of class bill has been created (new bill) and its
\r
678 address has been assigned to variable x (x:=new bill), then the
\r
679 attributes of that object are accessible through dot notation (remote
\r
680 access). The expression x.dollars gives , for instance, the remote
\r
681 access to attribute dollars of the object referenced by x.
\r
682 All attributes of class objects are initialized as usual. For the
\r
683 above example the object referenced by x, after the execution of the
\r
684 specified sequence of statements, has the following structure:
\r
698 The object referenced by y and z has the following structure:
\r
712 The value none is the default initial value of any reference
\r
713 variable and denotes no object. A remote access to an attribute of
\r
714 none raises a run time error.
\r
715 Class may have also formal parameters (as procedures and functions).
\r
716 Kinds and transmission modes of formal parameters are the same as in
\r
717 the case of procedures.
\r
722 unit node: class (a:integer);
\r
723 var left,right:node;
\r
726 Let , for instance, variables z1, z2, z3 be of type node. Then the
\r
727 sequence of statements:
\r
732 z1.left:=z2; z1.right:=z3;
\r
734 creates the structure:
\r
741 | | right | ------->
\r
744 ------------ ------------
\r
745 z2---->| 3 | | 7 | <----z3
\r
746 ------------ ------------
\r
748 ------------ ------------
\r
750 ------------ ------------
\r
753 where arrows denote the values of the reference variables.
\r
755 Class may also have a sequence of statements (as any other unit).
\r
756 That sequence can initialize the attributes of the class objects.
\r
761 unit complex:class(re,im:real);
\r
764 module:=sqrt(re*re+im*im)
\r
767 Attribute module is evaluated for any object generation of class
\r
770 z1:=new complex(0,1); (* z1.module equals 1 *)
\r
771 z2:=new complex(2,0); (* z2.module equals 2 *)
\r
773 For the execution of a class generator, first a class object is
\r
774 created, then the input parameters are transmitted , and finally, the
\r
775 sequence of statements (if any) is performed. Return is made with the
\r
776 execution of return statement or the final end of a unit. Upon return
\r
777 the output parameters are transmitted.
\r
778 Procedure object is automatically deallocated when return is made to
\r
779 the caller. Class object is not deallocated , its address can be
\r
780 assigned to a reference variable, and its attributes can be thereafter
\r
781 accessed via this variable.
\r
783 The classes presented so far had only variable attributes. In
\r
784 general, class attributes may be also other syntactic entities, such
\r
785 as constants, procedures, functions, classes etc. Classes with
\r
786 procedure and function attributes provide a good facility to define
\r
792 A push_down memory of integers may be implemented in the following
\r
795 unit push_down :class;
\r
796 unit elem:class(value:integer,next:elem);
\r
797 (* elem - stack element *)
\r
800 unit pop: function :integer;
\r
804 result:=top.value; top:=top.next
\r
807 unit push:procedure(x:integer); (* x - pushed integer *)
\r
810 top:=new elem(x,top);
\r
814 Assume that somewhere in a program reference variables of type
\r
815 push_down are declared (of course, in place where push_down is
\r
818 var s,t,z:push_down;
\r
820 Three different push_down memories may be now generated:
\r
822 s:=new push_down(100); t:=new push_down(911); z:=new push_down(5);
\r
824 One can use these push_down memories as follows:
\r
826 call s.push(7); (* push 7 to s *)
\r
827 call t.push(1); (* push 1 to t *)
\r
828 i:=z.pop; (* pop an element from z *)
\r
831 5. Adjustable arrays
\r
832 ####################
\r
834 In LOGLAN-82 arrays are adjustable at run time. They may be treated
\r
835 as objects of specified standard type with index instead of identifier
\r
836 selecting an attribute. An adjustable array should be declare
\r
837 somewhere among the lists of declarations and then may be generated in
\r
838 the sequence of statements.
\r
845 var A:arrayof integer; (* here is the declaration of A *)
\r
848 new_array A dim (1:n); (* here is the generation of A *)
\r
856 A variable A is an array variable. Its value should be the reference
\r
857 to an integer array, i.e. a composite object consisting of integer
\r
858 components each one defined by an integer index. Array generation
\r
861 new_array A dim (1:n);
\r
863 allocates a one-dimensional integer array with the index bounds 1,n ,
\r
864 and assigns its address to variable A. The figure below illustrates
\r
867 ---------- -----------
\r
871 ---------- -----------
\r
875 ---------- -----------
\r
876 | A | ---------> | A(n) |
\r
877 ---------- -----------
\r
878 Block object Array object
\r
880 A general case of array generation statement has the form:
\r
882 new_array A dim (lower:upper)
\r
884 where lower and upper are arithmetic expressions which define the
\r
885 range of the array index.
\r
890 Two-dimensional array declaration :
\r
892 var A: arrayof arrayof integer;
\r
896 new_array A dim (1:n)
\r
897 for i:=1 to n do new_array A(i) dim (1:m) od;
\r
899 create the structure:
\r
907 ------------ ----------
\r
908 | A(1) | ---------> | A(1,m) |
\r
909 ------------ ----------
\r
913 ------------ ----------
\r
914 | A(n) | ---------> | A(n,1) |
\r
915 ------------ ----------
\r
927 var i,j:integer, A,B: arrayof arrayof real, n:integer;
\r
930 new_array A dim (1:n);
\r
931 for i:=1 to n do new_array A(i) dim (1:n) od;
\r
932 (* A is square array *)
\r
933 new_array B dim (1:n);
\r
934 for i:=1 to n do new_array B(i) dim(1:i) od;
\r
935 (* B is lower triangular array *)
\r
941 Array A is the square array n by n. Each element A(i) , 1<=i<=n
\r
942 contains the address of row A(i,j), 1<=j<=n. Array B is the
\r
943 lower-triangular array. Each element B(i), 1<=i<=n, contains the
\r
944 address of row B(i,j), 1<=j<=i. Thus an assignment statement
\r
945 A(n,n):=B(n,n) transmits real value B(n,n) to real variable A(n,n).
\r
946 Assignment B(1):=A(1) transmits the address of the first row of A to
\r
947 variable B(1). Finally assignment B(1):=copy (A(1)) creates a copy of
\r
948 the first row of A and assigns its address to B(1).
\r
950 Upper and lower bounds of an adjustable array A are determined by
\r
951 standard operators lower(A) and upper(A).
\r
956 unit sort: procedure(A:arrayof integer); (* insertion sort *)
\r
957 var n,i,j:integer; var x:integer;
\r
959 n:=upper(A); (* assume lower bound is 1 *)
\r
964 if x >= A(j) then exit fi;
\r
965 A(j+1):=A(j); j:=j-1;
\r
966 if j=0 then exit fi;
\r
972 If an array variable A refers to no array its value is equal none
\r
973 (the standard default value of any array variable). An attempt to
\r
974 access an array element (e.g. A(i)) or a bound (e.g. lower(A)), where
\r
975 A is none, raises a run time error. - 24 -
\r
978 6. Coroutines and semicoroutines
\r
979 ################################
\r
981 Coroutine is a generalization of class. A coroutine object is an
\r
982 object such that the execution of its sequence of statements can be
\r
983 suspended and reactivated in a programmed manner. Consider first a
\r
984 simple class with a sequence of statements such that after return some
\r
985 non-executed statements remain. The generation of its object
\r
986 terminates with the execution of return statement, although the object
\r
987 can be later reactivated. If such a class is declared as a coroutine,
\r
988 then its objects may be reactivated. This can be realized by attach
\r
993 where X is a reference variable designating the activating coroutine
\r
995 In general, since the moment of generation a coroutine object is
\r
996 either active or suspended. Any reactivation of a suspended coroutine
\r
997 object X (by attach(X)) causes the active coroutine object to be
\r
998 suspended and continues the execution of X from the statement
\r
999 following the last executed one.
\r
1000 Main program is also a coroutine. It is accessed through the
\r
1001 standard variable main and may be reactivated (if suspended) by the
\r
1002 statement attach(main).
\r
1007 In the example below the cooperation of two coroutines is presented.
\r
1008 One reads the real values from an input device, another prints these
\r
1009 values in columns on a line-printer, n numbers in a line. The input
\r
1010 stream ends with 0.
\r
1013 var prod:producer,cons:consumer,n:integer,mag:real,last:bool;
\r
1014 unit producer: coroutine;
\r
1018 read(mag); (* mag- nonlocal variable, common store *)
\r
1020 then (* end of data *)
\r
1029 unit consumer: coroutine(n:integer);
\r
1030 var Buf:arrayof real;
\r
1033 new_array Buf dim(1:n);
\r
1040 if last then exit exit fi;
\r
1043 do (* print Buf *)
\r
1044 write(' ',Buf(i):10:2)
\r
1048 (* print the rest of Buf *)
\r
1049 for j:=1 to i do write(' ',Buf(j):10:2) od;
\r
1055 prod:=new producer;
\r
1057 cons:=new consumer(n);
\r
1062 The above task could be programmed without coroutines at all. The
\r
1063 presented solution is, however, strictly modular, i.e. one unit
\r
1064 realizes the input process, another realizes the output process, and
\r
1065 both are ready to cooperate with each other.
\r
1067 LOGLAN-82 provides also a facility for the semi-coroutine
\r
1068 operations. This is gained by the simple statement detach. If X is the
\r
1069 active coroutine object, then detach reactivates that coroutine object
\r
1070 at where the last attach(X) was executed. This statement meets the
\r
1071 need for the asymetric coroutine cooperations. (by so it is called
\r
1072 semi-coroutine operation). Operation attach requires a reactivated
\r
1073 coroutine to be defined explicitly by the user as an actual parameter.
\r
1074 Operation detach corresponds in some manner to return in procedures.
\r
1075 It gives the control back to a coroutine object where the last
\r
1076 attach(X) was executed, and that coroutine object need not be known
\r
1077 explicitly in X. This mechanism is, however, not so secure as the
\r
1078 normal control transfers during procedure calls and returns.
\r
1080 In fact, the user is able to loop two coroutines traces by :
\r
1086 Then detach in X reactivates Y, detach in Y reactivates X.
\r
1088 In the example below the application of detach statement is
\r
1094 program reader_writers;
\r
1095 (* In this example a single input stream consisting of blocks of
\r
1096 numbers, each ending with 0, is printed on two printers of
\r
1097 different width. The choice of the printer is determined by the
\r
1098 block header which indicates the desired number of print
\r
1099 columns. The input stream ends with a double 0. m1 - the width
\r
1100 of printer_1, m2 - the width of printer_2 *)
\r
1101 const m1=10,m2=20;
\r
1102 var reader:reading,printer_1,printer_2:writing;
\r
1103 var n:integer,new_sequence:boolean,mag:real;
\r
1105 unit writing:coroutine(n:integer);
\r
1106 var Buf: arrayof real, i,j:integer;
\r
1108 new_array Buf dim (1:n); (* array generation *)
\r
1109 return; (* return terminates coroutine initialization *)
\r
1111 attach(reader); (* reactivates coroutine reader *)
\r
1113 then (* a new sequence causes buffer Buf to be cleared up *)
\r
1114 for j:=1 to i do write(' ',Buf(j):10:2) od; writeln;
\r
1115 i:=0; new_sequence:=false; attach(main)
\r
1117 i:=i+1; Buf(i):=mag;
\r
1120 for j:=1 to n do write(' ',Buf(j):10:2) od; writeln;
\r
1127 unit reading: coroutine;
\r
1132 if mag=0 then new_sequence:=true; fi;
\r
1133 detach; (* detach returns control to printer_1 or
\r
1139 reader:=new reading;
\r
1140 printer_1:=new writing(m1); printer_2:=new writing(m2);
\r
1145 when m1: attach(printer_1)
\r
1146 when m2: attach(printer_2)
\r
1147 otherwise write(" wrong data"); exit
\r
1152 Coroutines play the substantial role in process simulation. Class
\r
1153 Simulation provided in Simula-67 makes use of coroutines at most
\r
1154 degree. LOGLAN-82 provides for easy simulation as well. The LOGLAN-82
\r
1155 class Simulation is implemented on a heap what gives lg(n) time cost
\r
1156 (in contrast with O(n) cost of the original implementation). It covers
\r
1157 also various simulation problems of large size and degree of
\r
1164 Classes and prefixing are ingenius inventions of Simula-67 (cf [1]).
\r
1165 Unfortunately they are hardly ever known and, perhaps, by this have
\r
1166 not been introduced into any other programming language. Moreover,
\r
1167 implementation constraints of Simula-67 bind prefixing and classes
\r
1168 workableness to such a degree that both facilities cannot be used in
\r
1169 all respects. We hope that LOGLAN-82, adopting merits and rooting up
\r
1170 deficiencies of these constructs, will smooth their variations and
\r
1171 vivify theirs usefulness.
\r
1172 What is prefixing ? First of all it is a method for unit extending.
\r
1173 Consider the simplest example:
\r
1175 unit bill: class;
\r
1176 var dollars :real,
\r
1177 not_paid :boolean,
\r
1178 year,month,day :integer;
\r
1181 Assume the user desires to extend this class with new attributes.
\r
1182 Instead of writing a completely new class, he may enlarge the existing
\r
1185 unit gas_bill:bill class;
\r
1186 var cube_meters: real;
\r
1189 Class gas_bill is prefixed by class bill. This new declaration may
\r
1190 appear anywhere within the scope of declaration of class bill. (In
\r
1191 Simula-67 such a prefixing is forbidden in nested units.) Class
\r
1192 gas_bill has all the attributes of class bill and additionally its own
\r
1193 attributes (in this case the only one: cube_meters). The generation
\r
1194 statement of this class has the form:
\r
1197 where z is a reference variable of type gas_bill. Remote access to the
\r
1198 attributes of prefixed class is standard:
\r
1200 z.dollars:=500.5; z.year:=1982; z.month:=3; z.day:=8;
\r
1201 z.cube_meters:=100000;
\r
1203 Consider now the example of a class with parameters.
\r
1205 Assume that in a program a class:
\r
1207 unit id_card: class(name:string,age:integer);
\r
1210 and its extension:
\r
1212 unit idf_card:id card class(first name:string);
\r
1220 Then for variable z of type id_card and variable t of type idf_card
\r
1221 the corresponding generation statement may be the following:
\r
1223 z:=new id_card("kreczmar",37);
\r
1224 t:=new idf_card("Kreczmar",37,"Qntoni");
\r
1226 Thus the formal parameters of a class are concatenated with the formal
\r
1227 parameters of its prefix.
\r
1228 One can still extend class idf_card. For instance:
\r
1230 unit idr_card:idf_card class;
\r
1231 var children_number:integer;
\r
1232 var birth_place:string;
\r
1235 Prefixing allows to build up hierarchies of classes. Each one
\r
1236 hierarchy has a tree structure. A root of such a tree is a class
\r
1237 without prefix. One class is a successor of another class iff the
\r
1238 first is prefixed by the latter one.
\r
1240 Consider the prefix structure:
\r
1257 Class H has a prefix sequence A, B, E, F, H. Let a, b, ... , h
\r
1258 denote the corresponding unique attributes of classes A, B, ... , H,
\r
1259 respectively. The objects of these classes have the following forms:
\r
1261 ------------ ------------ ------------ ------------
\r
1262 | a | | a | | a | | a |
\r
1263 ------------ ------------ ------------ ------------
\r
1264 object A | b | | c | | d |
\r
1265 ------------ ------------ ------------
\r
1266 object B object C object D
\r
1270 ------------ ------------ ------------ ------------
\r
1271 | a | | a | | a | | a |
\r
1272 ------------ ------------ ------------ ------------
\r
1273 | b | | b | | b | | b |
\r
1274 ------------ ------------ ------------ ------------
\r
1275 | e | | e | | e | | e |
\r
1276 ------------ ------------ ------------ ------------
\r
1277 object E | f | | f | | f |
\r
1278 ------------ ------------ ------------
\r
1279 object F | g | | h |
\r
1280 ------------ ------------
\r
1283 Let Ra, Rb,..., Rh denote reference variables of types A, B,..., H,
\r
1284 respectively. Then the following expressions are correct:
\r
1286 Ra.a, Rb.b, Rb.a, Rg.g, Rg.f, Rh.h, Rh.f, Rh.e, Rh.b, Rh.a etc.
\r
1288 Variable Ra may designate the object of class B (or C,..., H), i.e.
\r
1293 is legal. But then attribute b is not accessible through dot via Ra,
\r
1294 i.e. Ra.b is incorrect. This follows from insecurity of such a remote
\r
1295 access. In fact, variable Ra may point any object of a class prefixed
\r
1296 by A, in particular, Ra may point the object of A itself, which has no
\r
1297 attribute b. If Ra.b had been correct, a compiler should have
\r
1298 distiguish the cases Ra points to the object of A or not. But this, of
\r
1299 course, is undistinguishable at compilation time.
\r
1300 To allow, however, the user's access to attribute b (after instruc tion Ra:= n
\r
1304 The correctness of this expression is checked at run time. If Ra
\r
1305 designates an object of B or prefixed ba B, the type of the expression
\r
1306 is B. Otherwise the expression is erroneous. Thus, for instance, the
\r
1309 Ra qua G.b, Ra qua G.e etc.
\r
1310 enable remote access to the attributes b, c, ... via Ra.
\r
1312 So far the question of attribute concatenation was merely discussed.
\r
1313 However the sequences of statements can be also concatenated.
\r
1314 Consider class B prefixed with class A. In the sequence of
\r
1315 statements of class A the keyword inner may occur anywhere, but only
\r
1316 once. The sequence of statements of class B consists of the sequence
\r
1317 of statements of class A with inner replaced by the sequence of
\r
1318 statements of class B.
\r
1320 unit A :class unit B:A class
\r
1330 In this case inner in class B is equivalent to the empty statement.
\r
1331 If class B prefixes another class, say C, then inner in B is replaced
\r
1332 by the sequence of statements of class C, and so on.
\r
1333 If inner does not occur explicitly, an implicit occurrence of inner
\r
1334 before the final end of a class is assumed.
\r
1339 Let class complex be declared as usual:
\r
1341 unit complex: class(re,im:real);
\r
1344 and assume one desires to declare a class mcomplex with the additional
\r
1345 attribute module. In order the generation of class mcomplex define the
\r
1346 value of attribute module, one can declare a class:
\r
1348 unit mcomplex:complex class;
\r
1351 module:=sqrt(re*re+im*im)
\r
1354 Class mcomplex may be still extended:
\r
1356 unit pcomplex:mcomplex class;
\r
1359 alfa:=arccos(re/module)
\r
1362 For these declarations each generation of class mcomplex defines the
\r
1363 value of attribute module, each generation of class pcomplex defines
\r
1364 the values of attributes module and alfa.
\r
1365 For reference variables z1, z2 z3 of type complex, the following
\r
1366 sequence of statements illustrates the presented constructs:
\r
1368 z1:=new complex(0,1);
\r
1369 z2:=new mcomplex(4,7);
\r
1370 z3:=new pcomplex(-10,12);
\r
1371 if z2 qua mcomplex.module > 1
\r
1375 if z3 qua pcomplex.alfa < 3.14
\r
1377 z3.re:=-z3.re; z3.alfa:=z3.alfa+3.14;
\r
1379 z1 qua mcomplex.module:= 0;
\r
1385 Binary search tree (Bst) is a binary tree where for each node x the
\r
1386 nodes in the left subtree are less than x, the nodes in the right
\r
1387 subtree are greater than x. It is the well-known exercise to program
\r
1388 the algorithms for the following operations on Bst:
\r
1389 member(x) = true iff x belongs to Bst
\r
1390 insert(x), enlarge Bst with x, if x does not yet belong to Bst
\r
1392 We define both these operations in a class:
\r
1395 unit node: class(value:integer); (* tree node *)
\r
1396 var left,right:node;
\r
1399 unit help: class(x:integer); (* auxiliary class *)
\r
1408 repeat (* jump to the beginning of a loop *)
\r
1412 p:=q; q:=q.right; repeat
\r
1416 inner (* virtual instruction to be
\r
1418 unit member:help function:boolean;
\r
1419 (* x is a formal parameter derived from the prefix help *)
\r
1423 unit insert:help procedure;
\r
1424 (* x is a formal parameter derived from the prefix help *)
\r
1426 if q=/=none then return fi;
\r
1428 if p=none then root:=q; return fi;
\r
1429 if p.value < x then p.right:=q else p.left:=q fi;
\r
1435 In the example the common actions of member and insert are
\r
1436 programmed in class help. Then it suffices to use class help as a
\r
1437 prefix of function member and procedure insert, instead of redundant
\r
1438 occurrences of the corresponding sequence of statements in both units.
\r
1441 Class Bst may be applied as follows:
\r
1445 X:=new Bst; Y:=new Bst;
\r
1446 call X.insert(5);
\r
1447 if Y.member(-17) then ....
\r
1450 As shown in the declaration of Bst, class may prefix not only other
\r
1451 classes but also procedures and functions. Class may prefix blocks as
\r
1456 Let class push_down (p. 5) prefix a block:
\r
1458 pref push_down(1000) block
\r
1462 call push(50); ...
\r
1467 In the above block prefixed with class push_down one can use pop and
\r
1468 push as local attributes. (They are local since the block is embedded
\r
1469 in the prefix push down.)
\r
1473 pref push down(1000) block
\r
1478 (* in this block both structures push down and Bst are visible *)
\r
1481 if member(10) then ...
\r
1487 In place where classes push_down and Bst are visible together a
\r
1488 block prefixed with Bst may be nested in a block prefixed with
\r
1489 push_down (or vice versa). In the inner block both data structures are
\r
1490 directly accessible. Note that this construct is illegal in Simula 67.
\r
1496 Formal types serve for unit parametrization with respect to any
\r
1497 non-primitive type.
\r
1502 unit Gsort:procedure(type T; A:arrayof T; function less(x,y:T):boolean);
\r
1503 var n,i,j:integer; var x:T;
\r
1510 if less(A(j),x) then exit fi; exit fi
\r
1511 A(j+1):=A(j); j:=j-1;
\r
1512 if j=0 then exit fi;
\r
1518 Procedure Gsort (the generalization of procedure sort from p.4) has
\r
1519 type parameter T. A corresponding actual parameter may be an arbitrary
\r
1520 non-primitive type. An actual parameter corresponding to A should be
\r
1521 an array of elements of the actual type T. Function less should define
\r
1522 the linear ordering on the domain T.
\r
1523 For instance, the array A of type bill (cf p.7) may be sorted with
\r
1524 respect to attribute dollars , if the function:
\r
1526 unit less: function(t,u:bill):boolean;
\r
1528 result:=t.dollars <= u.dollars
\r
1531 is used as an actual parameter:
\r
1533 call Gsort(bill,A,less);
\r
1535 If the user desires to sort A with respect to date, it is sufficient
\r
1538 unit earlier:function(t,u:bill):boolean;
\r
1540 if t.year < u.year then result:= true; return fi;
\r
1543 if t.month < u.month then result:=true; return fi;
\r
1544 if t.month=u.month then result:=t.day<=u.day fi
\r
1548 and to call: call Gsort(bill,A,earlier);
\r
1551 9. Protection techniques
\r
1552 ########################
\r
1554 Protection techniques ease secure programming. If a program is
\r
1555 large, uses some system classes, is designed by a team etc., this is
\r
1556 important (and non-trivial) to impose some restrictions on access to
\r
1557 non-local attributes.
\r
1558 Let us consider a data structure declared as a class. Some of its
\r
1559 attributes should be accessible for the class users , the others
\r
1560 should not. For instance, in class Bst (p.7) the attributes member and
\r
1561 insert are to be accessible. On the other hand the attributes root,
\r
1562 node and help should not be accessible, even for a meddlesome user. An
\r
1563 improper use of them may jeopardize the data structure invariants.
\r
1564 To forbid the access to some class attributes the three following
\r
1565 protection mechanisms are provided:
\r
1567 close, hidden, and taken.
\r
1569 The protection close defined in a class forbids remote access to the
\r
1570 specified attributes. For example, consider the class declaration:
\r
1574 var x: integer, y,z:real;
\r
1578 Remote access to the attributes x,y,z from outside of A is
\r
1581 The protection hidden (with akin syntax) does not allow to use the
\r
1582 specified attributes form outside of A neither by the remote access
\r
1583 nor in the units prefixed by A. The only way to use a hidden attribute
\r
1584 is to use it within the body of class A.
\r
1585 Protection taken defines these attributes derived from prefix, which
\r
1586 the user wishes to use in the prefixed unit. Consider a unit B
\r
1587 prefixed by a class A. In unit B one may specify the attributes of A
\r
1588 which are used in B. This protects the user against an unconscious use
\r
1589 of an attribute of class A in unit B (because of identifier conflict).
\r
1590 When taken list does not occur , then by default, all non-hidden
\r
1591 attributes of class A are accessible in unit B.
\r
1594 10. Programmed deallocation
\r
1595 ###########################
\r
1597 The classical methods implemented to deallocate class objects are
\r
1598 based on reference counters or garbage collection. Sometimes the both
\r
1599 methods may be combined. A reference counter is a system attribute
\r
1600 holding the number of references pointing to the given object. Hence
\r
1601 any change of the value of a reference variable X is followed by a
\r
1602 corresponding increase or decrease of the value of its reference
\r
1603 counter. When the reference counter becomes equals 0, the object can
\r
1605 The deallocation of class objects may also occur during the process
\r
1606 of garbage collection. During this process all unreferenced objects
\r
1607 are found and removed (while memory may be compactified). In order to
\r
1608 keep the garbage collector able to collect all the garbage, the user
\r
1609 should clear all reference variables , i.e. set to None, whenever
\r
1610 possible. This system has many disadvantages. First of all, the
\r
1611 programmer is forced to clear all reference variables, even those
\r
1612 which are of auxiliary character. Moreover, garbage collector is a
\r
1613 very expensive mechanism and thus it can be used only in emergency
\r
1615 In LOGLAN a dual operation to the object generator, the so-called
\r
1616 object deallocator is provided. Its syntactic form is as follows:
\r
1620 where X is a reference expression. If the value of X points to no
\r
1621 object (none) then kill(X) is equivalent to an empty statement. If the
\r
1622 value of X points to an object O, then after the execution of kill(X),
\r
1623 the object O is deallocated. Moreover all reference variables which
\r
1624 pointed to O are set to none. This deallocator provides full security,
\r
1625 i.e. the attempt to access the deallocated object O is checked and
\r
1626 results in a run-time error.
\r
1629 Y:=X; kill(X); Y.W:=Z;
\r
1631 causes the same run-time error as:
\r
1635 The system of storage management is arranged in such a way that the
\r
1636 frames of killed objects may be immediately reused without the
\r
1637 necessity of calling the garbage collector, i.e. the relocation is
\r
1638 performed automatically. There is nothing for it but to remember not
\r
1639 to use remote access to a killed object. (Note that the same problem
\r
1640 appears when remote access X.W is used and X=none).
\r
1645 Below a practical example of the programmed deallocation is
\r
1646 presented. Consider class Bst (p.7). Let us define a procedure that
\r
1647 deallocates the whole tree and is called with the termination of the
\r
1651 (* standard declarations list of Bst *)
\r
1652 unit kill_all:procedure(p:node);
\r
1653 (* procedure kill_all deallocates a tree with root p *)
\r
1655 if p= none then return fi;
\r
1656 call kill_all(p.left);
\r
1657 call kill_all(p.right);
\r
1662 call kill_all(root)
\r
1665 Bst may be applied as a prefix:
\r
1671 and automatically will cause the deallocation of the whole tree after
\r
1672 return to call kill_all(root) from the prefixed block.
\r
1674 To use properly this structure by remote accessing one must call
\r
1675 kill_all by himself:
\r
1677 unit var X,Y:Bst;
\r
1680 X:=new Bst; Y:=new Bst;
\r
1682 (* after the structures' application *)
\r
1683 call X.kill_all(X.root);
\r
1685 call Y.kill_all(Y.root);
\r
1690 Finally note that deallocator kill enables deallocation of array
\r
1691 objects, and suspended coroutines and processes as well (cf p.13).
\r
1694 11. Exception handling
\r
1695 #######################
\r
1697 Exceptions are events that cause interruption of normal program
\r
1698 execution. One kind of exceptions are those which are raised as a
\r
1699 result of some run time errors. For instance, when an attempt is made
\r
1700 to access a killed object, when the result of numeric operation does
\r
1701 not lie within the range, when the dynamic storage allocated to a
\r
1702 program is exceeded etc.
\r
1703 Another kind of exceptions are those which are raised explicitly by
\r
1704 a user (with the execution of the raise statement).
\r
1705 The response to exceptions (one or more) is defined by an exception
\r
1706 handler. A handler may appear at the end of declarations of any unit.
\r
1707 The corresponding actions are defined as sequences of statements
\r
1708 preceded by keyword when and an exception identifier.
\r
1713 In procedure squareeq (p.3) we wish to include the case when a=0. It
\r
1714 may be treated as an exception (division by zero).
\r
1716 unit squareeq(a,b,c:real;output xr,xi,yr,yi:real);
\r
1719 when division_by_zero:
\r
1722 xi,yr,yi:=0; xr:=-c/b; terminate
\r
1724 raise Wrong_data(" no roots")
\r
1731 The handler declared in that procedure handles the only one
\r
1732 exception (division_by_zero).
\r
1733 When an exception is raised, the corresponding handler is searched
\r
1734 for, starting from the active object and going through return traces.
\r
1735 If there is no object containing the declaration of the handler, then
\r
1736 the program (or the corresponding process) is terminated. Otherwise
\r
1737 the control is transferred to the first found handler.
\r
1741 In our example the handler is declared within the unit itself, so
\r
1742 control is passed to a sequence:
\r
1747 Therefore, when b=/=0, the unique root of square equation will be
\r
1748 determined and the procedure will be normally terminated (terminate).
\r
1749 In general, terminate causes that all the objects are terminated,
\r
1750 starting from that one where the exception was raised and ending on
\r
1751 that one where the handler was found. Then the computation is
\r
1752 continued in a normal way.
\r
1753 In our example, when b=0, a new exception is raised by the user. For
\r
1754 this kind of exceptions , the exception itself should be declared
\r
1755 (because it is not predefined as a run time error). Its declaration
\r
1756 may have parameters which are transmitted to a handler. The exception
\r
1757 declaration need not be visible by the exception handler. However the
\r
1758 way the handler is searched for does not differ from the standard one.
\r
1759 Consider an example:
\r
1762 signal Wrong_data(t:string);
\r
1771 Exception Wrong_data may be raised wherever its declaration (signal
\r
1772 Wrong_data) is visible. When its handler is found the specified
\r
1773 sequence of actions is performed. In the example above different
\r
1774 handlers may be defined in inner units to the main block where
\r
1775 squereeq is called.
\r
1776 The case a=0 could be included , of course, in a normal way, i.e. by
\r
1777 a corresponding conditional statement occurring in the procedure body.
\r
1778 But the case a=0 was assumed to be exceptional (happens scarcely).
\r
1779 Thus the evaluation of condition a=0 would be mostly unnecessary. As
\r
1780 can be noticed thanks to exceptions the above problem can be solved
\r
1781 with the minimal waste of run time.
\r
1785 12. Separate compilation (this section does not apply to PC version)
\r
1786 ########################
\r
1793 The implementation of processes is different (May 1988) c.f. user's manual.
\r
1795 Process in LOGLAN-82 is a natural generalization of coroutine (cf
\r
1796 p.6). Coroutines are units which once generated may operate
\r
1797 independently, each one treated as a separate process. For coroutines,
\r
1798 however, an essential assumption is established; namely, when one
\r
1799 coroutine object is reactivated, the active one must be suspended
\r
1800 (i.e. there which is onle one control is switched between coroutine
\r
1801 objects). When processes are used, the activation of a new process
\r
1802 does not require the active one to be suspended. So many objects may
\r
1803 be simultaneously active.
\r
1804 The statement that reactivates a suspended process X (without
\r
1805 suspention of the active one) has the form:
\r
1809 The main problem of parallel programming is, of course,
\r
1810 synchronization. Elementary synchronization in LOGLAN-82 is achieved
\r
1811 by two-valued semaphores and some number of simple statements
\r
1812 operating on them.
\r
1813 A semaphore variable controls the entry to a critical region, i.e. a
\r
1814 sequence of statements that may be executed by the one process only.
\r
1815 When a semaphore is open, the corresponding critical region is free.
\r
1816 When a semaphore is closed, it means the corresponding critical region
\r
1817 is just executed by a certain process.
\r
1819 These are the simple indivisible statements that operate on
\r
1822 lock(S) - If semaphore S is open, the given process enters
\r
1823 the critical region guarded by S , and
\r
1824 simultaneously, semaphore S becomes closed. If
\r
1825 semaphore S is already closed, the given process
\r
1826 waits until the critical region is open (by another
\r
1828 unlock(S)- If semaphore S is closed, then it becomes open.
\r
1829 Otherwise the statement is empty.
\r
1830 stop(S) - The statement causes semaphore S to be open, and
\r
1831 simultaneously, it stops the given process
\r
1832 execution. The statement may be used without a
\r
1833 parameter, and then, it stops the given process
\r
1835 Moreover, only those three above statements may change the values of
\r
1836 semaphore variables.
\r
1837 In general, several processes may wait for an entry to the same
\r
1838 critical region. When the process executing this critical region opens
\r
1839 the semaphore (by unlock or stop), one of the waiting processes is
\r
1840 reactivated and enters the region. The way such a process is selected
\r
1841 is not defined by the language. The user must assume that this
\r
1842 selection is performed arbitrarily.
\r
1849 In the example an input stream of real numbers is copied. The
\r
1850 copying process is parallelized in such a way that when process get
\r
1851 reads the next number, the process put writes simultaneously the
\r
1852 number read in the preceding step. The input stream ends with 0.
\r
1855 var in_buf,out_buf:real, completed:boolean, sem:semaphore;
\r
1856 var counter:integer,get:get_type,put:put_type;
\r
1857 unit cobegin:procedure; (* called by the main program *)
\r
1859 lock(sem); (* critical region *)
\r
1860 resume(get); (* activate reading *)
\r
1861 resume(put); (* activate writing *)
\r
1862 stop(sem); (* exit from critical region *)
\r
1864 unit coend: procedure;
\r
1865 begin (* called by get and put *)
\r
1866 lock(sem); (* entry to critical region *)
\r
1868 then (* one process entered *)
\r
1870 else (* two processes entered *)
\r
1872 resume(main) (* reactivate main program *)
\r
1874 stop(sem) (* exit from critical region *)
\r
1877 unit get_type:process;
\r
1883 then (* end of data *)
\r
1890 unit put_type:process;
\r
1899 begin (* main process *)
\r
1901 get:=new get_type;
\r
1902 put:=new put_type;
\r
1906 if completed then exit fi;
\r
1912 Two procedures cobegin and coend synchronize the execution of the
\r
1913 main loop. Procedure cobegin implements fork operator, procedure coend
\r
1914 called from processes put and get implements the end of fork operator.
\r
1915 Variable count defines the number of processes that called procedure
\r
1916 coend. By an easy modification one can generalize these procedures in
\r
1917 order to implement the general k-fork and end of k-fork operators (if
\r
1918 count can assume the values 0,1,...,k-1).
\r
1920 Finally, let us present an example of a class that realizes Hoare's
\r
1921 monitors (cf. [2]). Monitor is a structure that synchronizes the
\r
1922 access to a common pool of data. The number and kinds of these data
\r
1923 are defined by the user. Monitor task is only to give non-conflict
\r
1924 access to it. The access to a monitor is realized by the so-called
\r
1925 entry procedures. Entry procedure has a prefix entry which guarantees
\r
1926 that only one such a procedure may enter the monitor.
\r
1927 In order to allow scheduling of processes that entered the monitor,
\r
1928 two specialized procedures operating on the inner monitor queues are
\r
1931 delay(Q) - stops the execution of the process and puts it
\r
1932 into a queue Q, the entry to the monitor is free,
\r
1933 continue(Q) - resumes the execution of the first process from a
\r
1934 queue Q (if Q is non-empty, otherwise the entry to
\r
1935 the monitor is free).
\r
1937 As can be easily seen, the correct use of these constructs is
\r
1938 achieved when continue is called as the last statement of an entry
\r
1941 The declaration of the class Monitor is as follows:
\r
1944 unit Monitor : queue class;
\r
1945 hidden sem,queue; (* hidden protects attributes sem and queue *)
\r
1946 var sem:semaphore; (* sem is the semaphore guarding the monitor entry *)
\r
1948 unit entry: class; (* all entry procedures must have prefix entry
\r
1949 which realized non-conflict access to Monitor *)
\r
1950 var busy:boolean; (* busy is true iff continue(Q) was executed
\r
1952 unit delay: procedure(Q:queue);
\r
1954 call Q.into(this process);
\r
1955 (* put the active process into queue Q *)
\r
1957 (* free the monitor access, halt the active process *)
\r
1959 unit continue:procedure(Q:queue);
\r
1960 (* continue can be called as the last statement of an entry procedure *)
\r
1965 resume(Q.out); (* resume the next process from queue Q *)
\r
1968 begin (* beginning of the prefix entry *)
\r
1969 lock(sem); (* entry to the critical region *)
\r
1970 inner; (* the virtual body of an entry procedure *)
\r
1973 unlock(sem) (* free the monitor access, unless continue
\r
1979 The mail-box structure which receives and sends the items of type T
\r
1980 may be implemented as the following class prefixed by Monitor:
\r
1982 unit Buffering:Monitor class(type T;size:integer);
\r
1983 var Pool:arrayof T,count,in_index,out_index:integer;
\r
1984 var readers_queue,writers_queue:queue;
\r
1985 unit writer:entry procedure(r:T);
\r
1987 if count=size then call delay(writers_queue) fi; in_index
\r
1988 Pool(in_index):=r; call continue(readers_queue);
\r
1990 unit reader:entry procedure(output r:T);
\r
1992 if count=0 then call delay(readers_queue) fi;
\r
1993 out_index:=out_index mod size +1; count:=count-1;
\r
1994 r:=Pool(out_index); call continue(writers_queue);
\r
1997 new_array Pool dim (1:size);
\r
1998 readers_queue:=new queue; writers_queue:=new queue;
\r
2005 [1] Dahl O-J.,Myhrhaug B,Nygaard K.: Common Base Language . NCC
\r
2006 S-22, October 1970
\r
2007 [2] Hoare C.A.R.: Monitors, an operating system structuring concept.
\r
2008 CACM,vol.17,N.10,October 1974,pp.549-57
\r
2009 [3] LOGLAN-82 Report , Warsaw, 1982
\r