IMPORT FGL module

The IMPORT FGL instruction imports module symbols.

Syntax

IMPORT FGL modulename 
  1. 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.

Important: At runtime, the imported modules are only loaded on demand, when the program flow reaches an instruction that uses an element of the imported module. For example, when calling a function or when assigning a (public) module variable of the imported 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.

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.

The FGLLDPATH environment variable specifies the directories to search for the .42m modules used by IMPORT FGL. Modules located in other directories and found by FGLLDPATH must already be compiled.

Important: Auto-compilation of imported modules is only supported if the imported module is in the current directory. Modules located in other directories and found by FGLLDPATH must already be compiled.
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.
Note: The fglcomp --implicit=none option is provided for specific cases. Do not use this option when not really needed: Auto-compilation of imported modules is the recommended default.

Circular module references

Circular references between modules are allowed. A circular reference occurs when several modules define an interdependence with IMPORT FGL.

Circular module references can be direct or indirect, for example:
  1. Direct circular reference: Module A imports module B, which in turn imports module A.
  2. Indirect circular reference: Module A imports module B, which imports module C, which imports module A.
Important: When .42m files are not yet available, compiling modules with circular dependency is only possible when these modules exist in the same directory and auto-compilation is used. Otherwise, when inter-dependent modules are located in different directories, or when using the --implicit=none option, all the inter-dependent modules must be specified in the fglcomp command.
Code example: Module "module_a.4gl":
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
Module "module_b.4gl":
IMPORT FGL module_a

FUNCTION function_b1()
    DISPLAY "In module_b: function_b1()"
    CALL module_a.function_a2()
END FUNCTION
Compile and execute:
$ fglcomp module_a.4gl && fglrun module_a
In module_a: function_a1()
In module_b: function_b1()
In module_a: function_a2()
Tip: To get detailed information about the compilation process, use the --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]

Identifying modules to be imported

When migrating existing projects using traditional linking, after compiling all the .4gl sources, consider using the --print-missing-imports option of fglrun, to print the IMPORT FGL suggestions for all the modules specified in the fglrun command line.

Just add the --print-missing-imports to the fgllink or fglrun -l commands, to identify what modules should be imported:
$ head *.4gl
==> main.4gl <==
MAIN
    CALL func1()
END MAIN

==> mod1.4gl <==
FUNCTION func1()
    CALL func2()
END FUNCTION

==> mod2.4gl <==
FUNCTION func2()
END FUNCTION

$ fglcomp *.4gl

$ fgllink --print-missing-imports -o prog.42r *.42m
-- in main.4gl
IMPORT FGL mod1

-- in mod1.4gl
IMPORT FGL mod2

The fglrun command also provides the --print-imports option to identify all imported modules that are really used.

Note: --print-imports and --print-missing-imports are options of fglrun, not fglcomp.

The --print-missing-imports option will try to resolve all (function) 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.

The --print-imports option prints all IMPORT FGL instructions, that are really used by a module, where at least one symbol (function, variable, constant, type, etc) is used by the importing module.

Note: Use the --print-missing-imports options instead of --print-imports: If you are missing an IMPORT FGL for a non-function symbol like a variable, constant, or type, the compiler will produce a compilation error. Regarding function symbols, the compilation is possible even when the module was not imported. Therefore, --print-missing-imports is useful to identify modules to be imported to resolve function symbols and avoid linking.
In the next example, mod1 can be imported in main, but mod2 already imports mod1:
$ head *.4gl
==> main.4gl <==
MAIN
    CALL func1()
END MAIN

==> mod1.4gl <==
FUNCTION func1()
   CALL func2()
END FUNCTION

==> mod2.4gl <==
IMPORT FGL mod1
FUNCTION func2()
   CALL func1()
END FUNCTION

$ fglcomp main.4gl mod1.4gl mod2.4gl

$ fglrun --print-imports main.42m mod1.42m mod2.42m
-- in main.4gl
IMPORT FGL mod1

-- in mod1.4gl
IMPORT FGL mod2

-- in mod2.4gl
IMPORT FGL mod1

$ fglrun --print-missing-imports main.42m mod1.42m mod2.42m
-- in main.4gl
IMPORT FGL mod1

-- in mod1.4gl
IMPORT FGL mod2

Scope of module symbols (PRIVATE/PUBLIC)

The PRIVATE/PUBLIC modifiers can be used to hide / publish symbols to other modules.

Note: Functions are by default public, for backward compatibility. Module variables, types and constants are by default private.
The following example declares a module variable that can be used by other modules, and a private function to be used only locally:
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.

Note: The --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).

For example, with two modules defining public types, constants, variables and functions:
-- 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"
Assuming the following importing module:
IMPORT FGL module1
IMPORT FGL module2
MAIN
    DEFINE v type1
    LET v = const1
    CALL func1( v )
    LET var2 = "abc"
    DISPLAY const2
END MAIN
The fglcomp --qualify-imports will produce this output from the above source file:
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.

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