IMPORT FGL module
The IMPORT FGL
instruction imports module symbols.
Syntax
IMPORT FGL modulename
- modulename is an identifier defining the module to be imported (without the file extension).
Usage
With IMPORT FGL modulename
, the symbols of the named
.42m module can be referenced in the current module.
The name specified after the IMPORT FGL
instruction is case-sensitive.
The imported module symbols that can be referenced are:
Compilation with IMPORT FGL
IMPORT FGL
instructs the fglcomp
compiler and fglrun runtime system to load/check the
specified modules.
When using only IMPORT FGL
to define module dependency, there is no longer a
need to link programs or use libraries.
With IMPORT FGL
, the compiler can check the number of parameters and returning
values in functions calls, and the autocompletion in source code editors is improved as it can
suggest all imported symbols.
Auto-compilation of (local) imported modules
It is recommended to compile imported modules before compiling the importing module.
The FGLLDPATH environment variable
specifies the directories to search for the .42m modules used by IMPORT
FGL
.
When the imported module is located in the same directory as the compiled module, if the .42m file of the imported module does not exist, or is older than the corresponding source file, fglcomp will automatically compile the imported module.
The automatic recompilation of imported modules applies recursively: For example, when a
main.4gl module imports module1
which in turn imports
module2
, and module2.4gl is more recent as
module2.42m, fglcomp will automatically compile
module2.4gl.
To avoid implicit compilation of imported modules, use the --implicit=none
option of fglcomp. If the .42m file exists but the
.4gl source file cannot be found, fglcomp imports the
.42m file as is.
Circular module references
Circular references between modules are allowed. A circular reference occurs when several modules
define an interdependence with IMPORT FGL
.
- Direct circular reference: Module A imports module B, which in turn imports module A.
- Indirect circular reference: Module A imports module B, which imports module C, which imports module A.
--implicit=none
option of fglcomp, circular imports is not
possible:$ fglcomp -M --implicit=none module_a.4gl
module_a.4gl:1:1:1:19:error:(-8403) Module module_b does not exist.
module_a.4gl:9:10:9:31:error:(-6606) No member function 'function_b1' for class 'module_b' defined.
The
modules having a circular reference must exist in the same directory. Otherwise, the compilation of
modules having a circular dependency is not possible.IMPORT FGL module_b
MAIN
CALL function_a1()
END MAIN
FUNCTION function_a1()
DISPLAY "In module_a: function_a1()"
CALL module_b.function_b1()
END FUNCTION
FUNCTION function_a2()
DISPLAY "In module_a: function_a2()"
END FUNCTION
IMPORT FGL module_a
FUNCTION function_b1()
DISPLAY "In module_b: function_b1()"
CALL module_a.function_a2()
END FUNCTION
$ fglcomp module_a.4gl && fglrun module_a
In module_a: function_a1()
In module_b: function_b1()
In module_a: function_a2()
--verbose
option of
fglcomp:$ fglcomp --verbose module_a.4gl
[loading fglhelp]
[parsing module_a.4gl]
[parsing module_b.4gl]
[building module_b]
[writing module_b.42m]
[building module_a]
[writing module_a.42m]
[total modules: 4 variables: 6 funtions: 274 types: 9 fields: 10]
Identify used modules with fglrun --print-imports
When migrating existing projects using traditional linking, after compiling all the
.4gl sources, consider using the --print-imports
option of
fglrun, to print the IMPORT FGL
suggestions for all the modules
specified in the fglrun command line.
--print-imports
option will try to resolve all symbols as done during
linking, but instead of producing a .42r program, it will list the
IMPORT FGL
instructions to be added in each module, and thus avoid
linking:$ cat main.4gl
MAIN
CALL func1()
END MAIN
$ cat mod1.4gl
FUNCTION func1()
CALL func2()
END FUNCTION
$ cat mod2.4gl
FUNCTION func2()
CALL func1()
END FUNCTION
$ fglrun --print-imports main.42m mod1.42m mod2.42m
-- in main.4gl
IMPORT FGL mod1
-- in mod1.4gl
IMPORT FGL mod2
-- in mod2.4gl
# Cyclic import: IMPORT FGL mod1
# caused by CALL func1
Scope of module symbols (PRIVATE/PUBLIC)
The PRIVATE
/PUBLIC
modifiers can be used to hide / publish
symbols to other modules.
PUBLIC DEFINE custlist DYNAMIC ARRAY OF RECORD
id INT,
name VARCHAR(50),
address VARCHAR(200)
END RECORD
...
PRIVATE FUNCTION myfunction()
...
END FUNCTION
Resolving symbol name conflicts with module prefix
If a symbol is defined twice with the same name in two different modules, the symbol must be qualified by the name of the module.
This feature overcomes the traditional 4GL limitation, requiring unique function names within a program.
In the following example, both imported modules define the same "init()
"
function, but this can be resolved, by adding the module name followed by a dot before the function
names:
IMPORT FGL orders
IMPORT FGL customers
MAIN
CALL orders.init()
CALL customers.init()
...
END MAIN
If a symbol is defined twice with the same name in the current and the imported module, an unqualified symbol will reference the current module symbol.
The following example calls the "init()
" function with and without a module
qualifier. The second call will reference the local function:
IMPORT FGL orders
MAIN
CALL orders.init() -- orders module function
CALL init() -- local function
...
END MAIN
FUNCTION init()
...
END FUNCTION
Qualifying imported symbols with fglcomp --qualify-imports
The fglcomp compiler provides the --qualify-imports
option to
automatically add module prefixes to all imported symbols that are not yet qualified.
--qualify-imports
option does not compile sources: The imported modules
need to be compiled as .42m pcode modules.The modified source is written to the standard output stream (stdout).
-- module1.4gl
PUBLIC CONSTANT const1 = 500
PUBLIC TYPE type1 INTEGER
PUBLIC FUNCTION func1( p1 type1 )
DISPLAY "func1:", p1
END FUNCTION
-- module2.4gl
PUBLIC DEFINE var2 STRING
PUBLIC CONSTANT const2 = "abc"
IMPORT FGL module1
IMPORT FGL module2
MAIN
DEFINE v type1
LET v = const1
CALL func1( v )
LET var2 = "abc"
DISPLAY const2
END MAIN
IMPORT FGL module1
IMPORT FGL module2
MAIN
DEFINE v module1.type1
LET v = module1.const1
CALL module1.func1( v )
LET module2.var2 = "abc"
DISPLAY module2.const2
END MAIN
If two modules define the same symbol name, fglcomp --qualify-imports will produce the error -8401.
Mixing IMPORT FGL and .42r linking
Traditional linking is still supported for backward compatibility. To ease migration from
traditional linking to imported modules, you can mix IMPORT FGL
usage with
fgllink.
By default, even when IMPORT FGL
is used, fglcomp does not
raise an error, if a referenced function is not found in the imported modules. This is mandatory to
compile the 42m file to be linked later with the module defining the missing
function.
Use the -W implicit
or the --resolve-calls
option to check for
imported functions.
-W implicit
option is used and at least one IMPORT FGL
is defined in the module, fglcomp will print warning -8406 for any referenced function
that cannot be found in the imported modules.-W implicit
option is
silently ignored, if no IMPORT FGL
is used in the module.--resolve-calls
option. This option will force the compiler to check all function symbols referenced in a module,
and raise error -8406, if a
symbol is not found in the imported modules.--resolve-calls
option is
typically used to compile programs that are only based on IMPORT FGL
and no longer
use the link phase.For more details about the linker, see Linking programs.
Example
Module "account.4gl":
PRIVATE DEFINE current_account VARCHAR(20)
PUBLIC FUNCTION set_account(id)
DEFINE id VARCHAR(20)
LET current_account = id
END FUNCTION
Module "myutils.4gl":
PRIVATE DEFINE initialized BOOLEAN
PUBLIC TYPE t_prog_info RECORD
name STRING,
version STRING,
author STRING
END RECORD
PUBLIC FUNCTION init()
LET initialized = TRUE
END FUNCTION
PUBLIC FUNCTION fini()
LET initialized = FALSE
END FUNCTION
PUBLIC FUNCTION tokenize(s STRING,
a DYNAMIC ARRAY OF STRING)
DEFINE tok base.StringTokenizer,
x INTEGER
LET tok = base.StringTokenizer.create(s," \t\n")
CALL a.clear()
LET x=0
WHILE tok.hasMoreTokens()
LET x=x+1
LET a[x] = tok.nextToken()
END WHILE
END FUNCTION
Module "program.4gl":
IMPORT FGL myutils
IMPORT FGL account
DEFINE filename STRING
DEFINE proginfo t_prog_info -- Type is defined in myutils
MAIN
DEFINE arr DYNAMIC ARRAY OF STRING
LET proginfo.name = "program"
LET proginfo.version = "0.99"
LET proginfo.author = "scott"
CALL myutils.init() -- with module prefix
CALL set_account("CFX4559") -- without module prefix
CALL tokenize("aaa bbb ccc",arr)
DISPLAY arr[2]
END MAIN