Lunar Programming Language

by David A. Moon
January 2017 - January 2018



Modules

A module is a global scope that maps from names to definitions. The outermost of the nested sequence of scopes is always a module. Unlike a local scope, definition names in a module never have hygienic context.

The name of a module has a constant definition as a member of the class module. A top-level module's definition is in the modules module. A "nested" module's definition is in some other module.

A module has no parent scope, but when lookup does not find a name directly defined in a module, it tries the module's imports in the order they were specified. Each import specifies a module and either a name in that module or a pattern string that uses asterisks as wildcards. An import with a name makes that particular name's definition visible. An import with a pattern makes visible the definition of every name exported by the other module that matches the pattern.

Import does not support renaming, so a definition has the same name in every module where it is visible. Of couse you can always

def newname = module.oldname
to get the effect of renaming.

A module has a set of exported names. This is only used by import with a pattern.

A module knows its own name, for debugging and to allow finding a referenced module when loading a "fasl file" produced by separate compilation.

The slots of a module are accessed using function calls, because the . operator has a special meaning when its left-hand parameter is a module. Its result is the value of the definition visible in that module named by the right-hand parameter.

You can define a module using the defmodule statement, whose syntax is

$defmodule { module_path_name & "." }* name
  { ^ ( "export:" { export_name & "," }+ |
        "import:" { { import_path_name "." }* ( import_name | import_string ) & "," }+ |
        "local: " { ( local_name | local_string ) & "," }+ |
        "no_default_import"
      ) }*
The result is the module object.

Except for looking up the prefix operator defmodule, nothing in a defmodule statement depends on the current module.

If no module_path_names are specified, the name of the module is name in the modules module. Otherwise the module_path_names start in the modules module and the name of the module is name in the last module in that path.

Each of the export_names is exported from the module that is being defined.

If no import_path_names are specified, the import_name or import_string refers to exports from the modules module. Otherwise the import_path_names start in the modules module and import from the last module in that path.

Thus import: lunar.defclass imports defclass from the lunar module and import: lunar."def*" imports from the lunar module every exported name that starts with "def".

import: module_name is useful because it allows the syntax module_name.definition_name to be used to refer to definitions in the module named module_name without importing them into the current module and risking name clashes.

local: is followed by one or more names or patterns. Any matching name will never be imported from another module.

Unless no_default_import is specified, import: lunar."*" is appended to the module definition, to import all the names defined by the Lunar language and exported from the lunar module.

The module class could have been defined by

defclass module(module_name name) global_scope, basic_name_context
  imports = stack[import_specifier]() reader: module_imports
  exports = stack[name]()             reader: module_exports
  locals  = stack[name | string]()    reader: module_locals
  name    = module_name name          reader: module_name

constant: sealed:
defclass import_specifier(module module, pattern name | string)

;; TODO should this only allow access to exports of the specified module?
def \.(m module, n name)
  scope_definitions(m)[n, default: false] or
    search_imports(m, n) or
    error("$n is not defined in $(module_name(m))")

def search_imports(m module, n name)
  importable?(m, n) and
    for spec in module_imports(m) using return
      if spec.pattern eq n
        return \.(spec.module, n)
      else if spec.pattern in string and
             asterisk_match?(n.spelling, spec.pattern) and
             n in module_exports(spec.module)
        return \.(spec.module, n)

def importable?(m module, n name)
  for local in module_locals(m) using never
    never local eq n
    if local in string
      never asterisk_match?(n.spelling, local)

def asterisk_match?(value string, pattern string)
  ;; true if value = pattern with asterisks replaced by appropriate substrings

Module-Related Modifiers

The module:, import:, and export: modifiers are recognized at top level.

module: is followed by an expression whose result is a module, which becomes the current module for parsing subsequent code in the source file. The expression is parsed in the modules module unless it starts with defmodule. The expression is typically a module name, a path of alternating names and dots to a module explicitly defined inside another module, or a defmodule statement.

import: is followed by a path of alternating names and dots, parsed in the modules module. The last name can be a string, which is a pattern that uses asterisks as wildcards. The imported name or names become visible in the current module.

export: is followed by a name or a definition, parsed in the current module. The name or the definition's name is exported from the current module. export: defclass exports all names defined by that statement, except for names other than the class name that start with an underscore character.

Built-In Modules

The following modules are built-in:

Module Name Exports
lunar names defined by the language
compiler additional names useful for writing macros and parsers
modules all modules not explicitly defined inside another module

There can be other modules defined by libraries that have been loaded.


Previous page   Table of Contents   Next page



Creative Commons License
Lunar by David A. Moon is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Please inform me if you find this useful, or use any of the ideas embedded in it.
Comments and criticisms to dave underscore moon atsign alum dot mit dot edu.