Skip to content

Extending Morpho

Developers interested in adding new features to morpho can utilize one of the following mechanisms for expansion:

  • Modules are written in the morpho language and are loaded with the import keyword. Creating a module is no different than writing a morpho program!

  • Extensions are written in C or C++ using the morpho C API. These are also loaded with the import keyword; the distinction between modules and extensions is purposefully not visible to the user; a module could be reimplemented as an extension with the same interface, for example.

  • Contributing to the morpho source code. Changes to the core data types, improvements to the compiler, etc. could be incorporated into the morpho source directly. We highly recommend connecting to the morpho developers before doing this to check if the idea is already being worked on, or whether there is guidance or advice on how specific features should work. Bug reports, suggestions for new features are welcome; please see CONTRIBUTING.md in the morpho git repository.

Modules and extensions can be distributed in their own git repository as a package. See Chapter Morpho packages for further information.

We also recommend contributors look at the CONTRIBUTING.md and CODE_OF_CONDUCT.md documents in the morpho git repository for information and advice about contributing to morpho, as well as how ethical standards for participation in our community.

This developer guide is only partially complete and represents a work in progress: We are gradually adding in helpful information to assist developers.

Morpho packages

To facilitate convenient distribution, morpho supports a lightweight notion of a package, which is a folder containing files that collectively provide some enhancement or functionality. A package is simply a git repository that contains some or all of the following file structure:

/share/modules

: for.morpho files that define a module

/share/help

: for .md files containing interactive help (see below).

/bin

: for compiled executables (these may be produced during installation).

/lib

: for compiled extensions (these may be produced during installation).

/src

: for source files

/examples

: for examples

/test

: for test files

/manual

: if a package is sufficiently complex to require a manual

README.md

: information about the package, installation etc.

Morpho searches both its base installation and all known packages when trying to locate resources. A simple and experimental package manager, morphopm, has been created to help users obtain and install packages; the structure above is intended to be sufficiently simple that different installation approaches could be supported.

We recommend naming your package with the morpho- prefix. Please let the morpho development team know about interesting packages!

Morpho help files

Morpho's interactive help system utilizes a subset of the Markdown plain text formatting system. Help files should be put in the /share/help folder of your package so that morpho can find them.

Each entry begins with a heading, for example:

# Entry

Using different heading levels indicates to morpho that a topic should be included as a subtopic. Here, the two heading level 2 entries become subtopics of the main topic, which uses heading level 1:

# Main topic

## Subtopic 1

## Subtopic 2

Morpho's help system supports basic formatting, including emphasized text:

This is *emphasized* text.

and lists can be included like so:

* List entry
* Another list entry

Code can be typeset inline,

Grave accents are used to delimit `some code`

or can be included in a block by indenting the code:

    for (i in 1..10) print i

The terminal viewer will syntax color this automatically.

Morpho (ab)uses the Markdown hyperlink syntax to encode control features. To specify a tag or keyword for a help entry, create a hyperlink where the label begins with the word tag, and include the keyword you'd like to use in the target as follows:

## Min
[tagmin]: # (min)

Finds the minimum value...

This unusual syntax is necessary as Markdown lacks comments or syntax for metadata, and we use hyperlinks to encode text in a way that is valid Markdown but remains transparent to regular Markdown viewers. The # is a valid URL target, and the construction in effect 'hides' the text in parentheses. Since hyperlink labels (the part in square brackets) must be unique per file, add any text you like, typically the name of the tag, after the word tag.

Similarly, to tell the help viewer to show a table of subtopics after an entry, add a line like this:

[showsubtopics]: # (subtopics)

Any characters after showsubtopics in the label are ignored, so you can add additional characters to ensure a unique label.

Extensions

Morpho extensions are dynamic libraries that are loaded at runtime. From the user's perspective, they work just like modules through the import statement:

import myextension

When the compiler encounters an import statement, it first searches to see if a valid extension can be found with that name. If so, the extension is loaded and compilation continues.

Extensions are implemented in C or any language that can be linked with C. A minimal extension looks like this:

// myextension.c

#include <stdio.h>
#include <morpho/morpho.h>
#include <morpho/builtin.h>

value myfunc(vm *v, int nargs, value *args) {
    printf("Hello world!\n");
    return MORPHO_NIL;
}

void myextension_initialize(void) {
    builtin_addfunction("myfunc", myfunc, BUILTIN_FLAGSEMPTY);
}

void myextension_finalize(void) {
}

All morpho extensions must provide an initialize function, and it must be named EXTENSIONNAME_initialize. In this function, you should call the morpho API to define functions and classes implemented by your extension, and set up any global data as necessary. Here, we add a function to the runtime that will be visible to user code as myfunc.

Morpho extensions may but are not required to provide a finalize function, with a similar naming convention to the initializer. This function should deallocate or close anything created by your extension that isn't visible to the morpho runtime. Here, the function data structures are handled by the morpho runtime so there's no finalization to do.

The remaining code implements your extension. Here, we implement a very simple function that conforms to the interface for a "builtin" function. The function just prints some text and returns nil.

Compiling an extension manually

To compile the above code, it's necessary to ensure that the morpho header files are visible to your compiler. They could be copied from the morpho git to /usr/local/include/morpho for example, but may be found in other places if morpho has been installed with homebrew or another package manager.

You need to compile this code as a dynamic library. For example on the macOS with clang,

cc -undefined dynamic_lookup -dynamiclib -o myextension.dylib myextension.c

The -dynamiclib option indicates that the target should be a dynamic library. The -undefined dynamic_lookup option indicates to the linker that any undefined references should be resolved at runtime.

Compiling an extension manually

We highly recommend using the CMake build system for extensions. Examples of how to do this are to be found e.g. in the ZeroMQ extension. We aim to add additional examples in future.

Packaging an extension

As for morpho modules, we advise hosting your extension in a git repository with morpho- as the prefix and with the file structure as suggested in chapter Morpho packages. We recommend including the C source files in /src and compiling your extension to /lib, where it can be found by morpho. We highly recommend including interactive help files in /share/help and examples in /examples as well. All extensions should have a README.md explaining what the extension is for and how the user should install it.

The new morphopm package manager provides an automated way to build extensions that should help with installation. For now, the above recommendations should ensure your basic file structure is future-proof.

We also note that the C API is not yet entirely stable; this will occur at v1.0.0. As we gain experience writing extensions and identify common needs, we anticipate improving the API. We welcome your feedback.