Modules in zeptoforth - tabemann/zeptoforth GitHub Wiki

Modules in zeptoforth provide a user-friendly means of managing namespace which is far less error-prone than managing namespace with set-order and set-current directly. Internally modules are wordlists referenced by constants, but here we will refer to them as modules rather than as wordlists.

Modules are defined using three words, begin-module, continue-module, and private-module. begin-module ( "name" -- ) creates a new module bound to a specified constant with the following name defined within the current module's definition and begins its definition. If a word exists within the current namespace with the same name an exception is raised. continue-module continues the definition of a preexisting named module. If no named module exists within the current namespace an exception is raised. private-module ( -- ) starts the definition of an anonymous module and does not bind it to a constant.

Module definitions are ended with one of two words, end-module ( -- ) and end-module> ( -- module ). The difference between these two words is that end-module> pushes the module that was being defined onto the stack while end-module does not. The common use case for this is to pass the module into import to import it into the containing module.

Modules are imported into the current namespace with import ( module -- ) to enable the use of words defined within in the current namespace. They are automatically unimported at the end of the current module definition. They may also be manually unimported from the current namespace with unimport ( module -- ). Note that modules imported into a given module namespace may not shadow any words defined within the current module definition, even if those words were defined before the import.

Individual words from modules can be imported into the current namespace with import-from ( module "name" -- ), which takes the specified module and imports name (which can incorporate the token separator :: like normal word references) into the namespace of the module currently being defined. These words persist until end-module or end-module> is reached. Note that these words are permanent in the case of the outer-most (i.e. default or forth) module, as it will not be closed by any call to end-module or end-module>, so it is a good idea to use this between begin-module and end-module or end-module>. Within the current module definition stack there is a limit of a total of 128 individually-imported words that may exist.

Namespaces inherit the definitions within their containing namespaces but do not modify them. Importing and unimporting modules within a module definition does not affect the namespace of any containing module definitions.

Names within modules may be looked up using the token separator ::, which takes a token containing the name of a constant referring to a module before it and a name to look up within that module after it. This may be applied recursively, so one can refer to, say, foo::bar::baz, which refers to a name baz within module bar within module foo. Note that such names may be used with constructs such as ', ['], and postpone.

For an example of modules in action, take the following:

begin-module test
  uart import
  private-module
    : config-pins ( -- ) 1 4 uart-pin 1 5 uart-pin ;
    : config-uart ( -- ) 921600 1 uart-baud! ;
    : send-bytes ( -- )
      [:
        [char] Z 1+ [char] A [: 1 >uart 500 ms ;] qcount
      ;] qagain
    ;
  end-module> import
  : init-test ( -- )
    config-uart
    config-pins
    0 ['] send-bytes 320 128 512 [ task ] :: spawn [ task ] :: run
  ;
end-module

Here we begin the definition of the test module, with the word test being defined within the default forth module. Then we import the uart module into the current namespace. Afterwards we create an anonymous module, define config-pins, config-uart, and send-bytes within it, and then end its definition, importing it into the containing namespace. Once we have defined init-test, which refers to words in the anonymous module and which refers to spawn and run within the task module, we end the definition of the test module.