Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
---------------------------
License
-------
0. OpenComal
------------
1. Introduction
---------------
When a Comal program is run it has a global variable space and (unless
changed by the features described in the following sections) all
variable references are resolved to the global variable space:
$ list
10 a:=1
20 aap
30 PRINT a
40
50 PROC aap
60 PRINT a
70 a:=2
80 ENDPROC
$ run
1
2
$ list
10 a:=1
20 aap
30 PRINT a
40
50 PROC aap CLOSED
60 PRINT a
70 a:=2
80 ENDPROC
$ run
60 PRINT a
Error 19: "Unknown identifier a" at line 60
$ del 60
Adding/Modifying/Deleting the current execution line has inhibited CONtinuation
$ run
1
As you can see the reference to "a" in line 60 results in a run time
error. The "a" introduced in line 70 is local and does not modify the
"a" in the global variable space.
4. IMPORT
---------
For these cases Comal provides the IMPORT statement. IMPORT introduces
variables from another (most often the global) variable space into the
local space:
$ list
10 a:=1
20 aap
30 PRINT a
40
50 PROC aap CLOSED
60 IMPORT a
70 PRINT a
80 a:=2
90 ENDPROC aap
$ run
1
2
In some Comal implementations the CLOSED concept encompasses procedures
and functions too. In these Comals you should IMPORT any procedures and
functions that you want to call from your closed routine. In OpenComal
procedures and functions are global entities that need not be imported
(a warning is given if you do).
5. LOCAL
--------
OpenComal extends the Comal language with the LOCAL keyword. LOCAL
creates a local variable. It may only be used in open routines (since it
is unneccesary in closed routines):
$ list
10 a:=1
20 aap
30 PRINT a
40
50 PROC aap
60 LOCAL a
70 PRINT a
80 a:=2
90 ENDPROC aap
$ run
0
1
LOCAL can also be used to introduce local string variables and arrays.
Its syntax is just like DIM:
$ list
10 DIM a$(20) OF 2
20 a$():="x"
30 aap
40 PRINT a$()
50
60 PROC aap
70 LOCAL a$(10) OF 2
80 a$():="y"
90 PRINT a$()
100 ENDPROC aap
$ run
y y y y y y y y y y
x x x x x x x x x x x x x x x x x x x x
$ list
10 a:=1
20 aap
30 PRINT a
40
50 PROC aap CLOSED
60 a:=2
70 tijger
80 PRINT a
90 ENDPROC aap
100
110 PROC tijger
120 PRINT a
130 a:=3
140 ENDPROC tijger
$ run
1
2
3
When routines start calling each other a stack of variable spaces comes
into existence. For instance take the following case:
$ list
10 a:=1
20 aap
30
40 PROC aap CLOSED
50 a:=2
60 tijger
70 ENDPROC aap
80
90 PROC tijger
100 LOCAL b
110 b:=2
120 SYS listvars
130 ENDPROC tijger
When control reaches line 120 there are three variable spaces into
existence:
8. Named IMPORT
---------------
Since OpenComal is at all times aware of the actual variable space stack
it can (and does) allow an IMPORT to specify from which environment a
variable should be imported.
$ list
10 a:=1
20 aap
30 PRINT a
40
50 PROC aap CLOSED
60 a:=2
70 tijger
80 PRINT a
90 ENDPROC aap
100
110 PROC tijger
120 IMPORT aap: a
130 PRINT a
140 a:=3
150 ENDPROC tijger
$ run
2
3
1
The execution of this program proceeds through lines 10, 20, 60, 70 and
120. At that time, two variables "a" exist: one in the global variable
space and one in the closed environment of "aap". Without qualification
an "IMPORT a" would import "a" from the global environment. However,
because the programmer specified "IMPORT aap:a" the "a" IMPORTed is the
one from the (calling) closed environment of "aap". The assignment in
line 140 modifies that instance of "a" as we can see from the result of
the PRINT statement in line 80.
$ tijger
120 IMPORT aap: a
Error 30: "Environment not found" at line 120
All these are strong reasons for disallowing named IMPORT. However, as
we will see later, named IMPORT can be necessary for if and when we
start nesting routines.
9. STATIC
---------
$ list
10 TRACE on
20 a:=100
30 aap
40 aap
50 PRINT a
60
70 PROC aap
80 STATIC a
90 PRINT a
100 a:+1
110 tijger
120 ENDPROC aap
130
140 PROC tijger
150 STATIC a
160 PRINT a
170 a:+10
180 ENDPROC tijger
$ run
10 TRACE on
20 a:=100
30 aap
80 STATIC a
90 PRINT a
0
100 a:+1
110 tijger
150 STATIC a
160 PRINT a
0
170 a:+10
180 ENDPROC tijger
120 ENDPROC aap
40 aap
80 STATIC a
90 PRINT a
1
100 a:+1
110 tijger
150 STATIC a
160 PRINT a
10
170 a:+10
180 ENDPROC tijger
120 ENDPROC aap
50 PRINT a
100
60
70 PROC aap
130
140 PROC tijger
$ run
0
100
1
110
120
For those that really want to make a mess of things, we can use a named
IMPORT to import a STATIC variable into a local environment:
$ list
10 a:=100
20 aap
30 aap
40 PRINT a
50
60 PROC aap CLOSED
70 STATIC a
80 PRINT a
90 a:+1
100 tijger
110 ENDPROC aap
120
130 PROC tijger
140 IMPORT aap: a
150 PRINT a
160 a:+10
170 ENDPROC tijger
$ run
0
1
11
12
100
The message here is that features that are introduced for a good cause
can be misused to create chaotic programs (Wizard's Second Rule: the
rule of unintended results). But, as Larry Wall aptly stated: "Good
programmers can write assembler in any language" :-).
$ list
10 a:=1
20 aap
30 PRINT a
40
50 PROC aap CLOSED
60
70 tijger
80 PRINT a
90
100 PROC tijger
110 PRINT a
120 a:=100
130 ENDPROC tijger
140
150 ENDPROC aap
$ 15 tijger
$ scan
15 tijger
PROCedure tijger not found
$ del 15
$ scan
When we run this program, the execution progresses through lines 10, 20,
70 on to 110. The interesting question then becomes whether the "a"
mentioned there is visible or not? On the one hand, "tijger" is an open
procedure (whatever that may mean in a nested routine) but it is part of
the CLOSED procedure "aap"...
$ trace on
$ run
10 a:=1
20 aap
60
70 tijger
110 PRINT a
110 PRINT a
Error 19: "Unknown identifier a" at line 110
The OpenComal interpreter declares that it does not know about "a"!
So when the OpenComal interpreter finds the reference to "a" in line 110
it first examines the local environment of "tijger". It does not find an
"a" there, thus it progresses to the parent environment ("aap"). It does
not find an "a" there either. Since "aap" is CLOSED the search process
stops there. If "aap" would have been open, the search for "a" would
have progressed into the global (program) variable space. Try it!
$ a=99
$ con
110 PRINT a
99
120 a:=100
130 ENDPROC tijger
80 PRINT a
100
90
100 PROC tijger
140
150 ENDPROC aap
30 PRINT a
1
40
50 PROC aap CLOSED
In this example I assign the value 99 to "a" and CONtinue the execution
of the program. The PRINT statement in line 110 now prints the assigned
value and the program continues. Next comes the PRINT statement in line
80. Will it succeed? It apparently does, which raises the next question:
In which variable environment was the "a" assigned to from the command
line (conceptually in the procedure "tijger", since that was where the
interpreter halted) entered?
The (Open)Comal rules for the environment in which new variables are
entered are as follows:
Let's modify the example program somewhat. Suppose we add an "IMPORT a"
to line 65 and run again:
$ 65 import a
$ list
10 a:=1
20 aap
30 PRINT a
40
50 PROC aap CLOSED
60
65 IMPORT a
70 tijger
80 PRINT a
90
100 PROC tijger
110 PRINT a
120 a:=100
130 ENDPROC tijger
140
150 ENDPROC aap
$ run
1
100
100
The IMPORT statement introduces the global "a" in the local variable
space of "aap". This makes that "a" reachable for "aap" and all its
nested routines. The PRINT statements in line 110 and 80 refer to the
one global "a" and the assignment statement in line 120 modifies it (as
is shown by the PRINT statement in line 30).
$ list
10 a:=1
20 aap
30 PRINT a
40
50 PROC aap CLOSED
60
70 tijger
80 PRINT a
90
100 PROC tijger
110 IMPORT a
120 PRINT a
130 a:=100
140 ENDPROC tijger
150
160 ENDPROC aap
$ run
110 IMPORT a
Error 30: "IMPORTable item "a" not found" at line 110
$ edit 110
110 IMPORT _program:a
$ run
1
80 PRINT a
Error 19: "Unknown identifier a" at line 80
$ a=99
$ con
99
100
Line 10 sets the global "a" to 1. Execution then proceeds through lines
20 and 70 to line 110. There the global "a" is IMPORTed in the local
variable space of "tijger". Line 120 prints that global "a" and line 130
sets it to the value 100. We then leave procedure "tijger" and return to
line 80. There we try to print "a", but there is no "a" in the local
variable environment of "aap"! Program execution therefore stops with an
error message.
12. MODULEs
-----------
Modules are introduced with the MODULE keyword. A program can declare
its intention to use the exported routines in a module with the USE
keyword:
$ list
10 USE aap
20 f
30 g
40
50 MODULE aap
60
70 EXPORT f, g
80
90 PROC f
100 PRINT "Hello from f"
110 ENDPROC f
120
130 PROC g
140 PRINT "hello from g"
150 ENDPROC g
160
170 PROC h
180 PRINT "hello from h"
190 ENDPROC h
200 ENDMODULE aap
$ run
Hello from f
hello from g
Procedure "h" is not exported and can thus not be called. If we try, an
error is thrown:
$ 35 h
$ run
35 h
PROCedure h not found
$ list
10 USE a
20 f
30 f
40
50 MODULE a
60 n:=1
70 EXPORT f
80 PROC f
90 PRINT "In f, n=",n
100 n:+1
110 ENDPROC
120 ENDMODULE
$ run
In f, n=1
In f, n=2
$ list
10 USE a, b
20 FOR i:=1 TO 4 DO
30 f
40 g
50 ENDFOR i
60
70 MODULE a
80 PRINT "Init a"
90 n:=1
100 EXPORT f
110 PROC f
120 n:+1
130 PRINT n
140 ENDPROC f
150 ENDMODULE a
160
170 MODULE b
180 PRINT "Init b"
190 n:=100
200 EXPORT g
210 PROC g
220 n:+1
230 PRINT n
240 ENDPROC g
250 ENDMODULE b
$ run
Init b
Init a
2
101
3
102
4
103
5
104
As you can see, the initialization statements in "a" and "b" are
executed before the main program starts. You will also see that the
order in which they are executed is not easily predicted. It is best not
to depend on the exact order of initialization.
The "n" variables of modules "a" and "b" are completely separate and not
available outside the module. Look at the following example:
$ list
10 USE a
20 f
30
40 MODULE a
50 n:=3
60 EXPORT f
70 PROC f
80 n:+1
90 g
100 ENDPROC f
110 ENDMODULE a
120
130 PROC g
140 PRINT n
150 ENDPROC g
$ run
140 PRINT n
Error 19: "Unknown identifier n" at line 140
By the way, if we really want to mess things up, we can use a named
IMPORT statement in procedure "g" to get at the internal "n" of module
"a":
This is not advised, but possible! Preventing people from writing stupid
and hard-to-understand code is unfortunately impossible in a programming
language that is sufficiently powerful.
(This section delves somewhat deeper than usual; you may skip it if you
are not interested).
Then, just before "run", all executable statements in all loaded modules
are executed. The variable environment created by the initialization is
set aside (remembered).
If the routine is not found then the global table of exported routines
is consulted. If the routine is found there then the static module
environment is pushed onto the call stack and the call is executed.