1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-05-28 13:41:31 +00:00
millfork/docs/lang/modules.md

99 lines
3.9 KiB
Markdown

[< back to index](../doc_index.md)
# Program structure
A Millfork program is build from one or more modules.
Each module is stored in a single file.
All source filenames passed to the compiler are considered to be modules of that program, called _root modules_.
Each module has a name, which is its unique identifier.
A module name is a sequence of slash-separated valid Millfork identifiers.
The name also defines where the module is located:
a module named `a/b` is presumed to exist in `a/b.mfk`
and it's looked up first in the directory that contains the current source file,
then in the current working directory,
and then in the include directories.
A module can import other modules, using the `import` statement.
Importing the same module multiple times merely marks it as imported by multiple modules,
but the program will still contain only one copy of it.
Examples:
import string
import cbm_file
Usually, the imported module will undergo the first phase of compilation first.
This means that the constants in the imported module will be resolved first, allowing you to use them in the importing module.
The only exception to this rule is when the importing graph has a cycle, in which case the order of modules within the cycle is unspecified.
A platform may define starting modules using the `modules=` directive of the `[compilation]` section.
All starting modules are considered to be imported by all source files explicitly mentioned on the command line.
### Module templates
If the first line of a source file starts with the `#template` directive,
then the source is considered to be a _module template_.
Module templates are a tool for generating repetitive code, similar to COBOL copybooks or Go Generate.
The template directive contains a comma-separated list of parameters.
It's recommended that the names of parameters begin and end with non-alphanumeric characters:
#template $P1$, $P2$
A module template cannot be imported as-is.
When importing a module template, you import a concrete instantiation of it.
For example, if the file `temp.mfk` contains the `#template` from above,
you can import it by providing a list of numeric literals or identifiers:
import temp<1, 2>
This instantiates a new module named `temp<1,2>` (if it hasn't been instantiated anywhere else).
The code in that module is generated by replacing every instance of the parameter names with the actual argument content.
Parameters that are numeric literals are normalized to their decimal representations.
Your program may contain multiple modules created from the same template with different parameters. For example,
import temp<3, 4>
import temp<5, 6>
import temp<$5, $6>
instantiates and imports two similar, yet different modules: `temp<3,4>` and `temp<5,6>`.
The third import imports a module that has already been instantiated and imported, so it's redundant.
The instantiation works through simple text replacement. For example, if `temp.mfk` contains:
#template $P1$, $P2$
const byte a$P1$ = $P2$
then the `temp<1,2>` module will contain
const byte a1 = 2
This substitution is performed before preprocessing, so those substitutions are available for the preprocessor directives.
It applies to identifiers, string literals, keywords, preprocesor directives etc.
**Warning:** This mechanism provides no direct way for preventing duplicates of code
that does not depend on the template parameters, or depends on only some template parameters.
In such situations, it might be advisable to put the non-dependent definitions in another module that is not a template,
or in a module template with fewer parameters. For example, instead of writing:
#template $N$
const byte X = 50
array a$N$ [X]
(which would define duplicate `X`s if imported multiple times), consider writing two files:
#template $N$
import define_X
array a$N$ [X]
>
//define_X.mfk:
const byte X = 50