mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-26 12:29:26 +00:00
98 lines
3.8 KiB
Markdown
98 lines
3.8 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 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
|
||
|
|
||
|
|