all repos — min @ null

A small but practical concatenative programming language.

site/contents/learn-extending.md

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
-----
content-type: "page"
title: "Learn: Extending min"
-----
{@ _defs_.md || 0 @}

min provides a fairly complete standard library with many useful modules. However, you may feel the need to extend min in order to perform more specialized tasks.

In such situations, you basically have three options:

* Implement new min modules in min
* Embed min in your [Nim](https://nim-lang.org) program
* Implemet min modules as dynamic libraries in Nim

## Implementing new min modules using min itself

When you just want to create more high-level min operator using functionalities that are already available in min, the easiest way is to create your own reusable min modules.

The {#link-operator||lang||module#} (and the **+** sigil) allows you to create a new min module:

```
{
  (dup *)             :pow2

  (dup dup * *)       :pow3

  (dup dup dup * * *) :pow4
  
} +quickpows

```

Save your code to a file (e.g. *quickpows.min*) and you can use it in other nim files using the {#link-operator||lang||load#} operator and the {#link-operator||lang||import#}:

```
'quickpows load
'quickpows import

2 pow3 pow2 puts ;prints 64
```

## Specifying your custom prelude program

By default, when min is started it loads the following *prelude.min* program:

```
; Imports
'str       import
'io        import
'logic     import
'num       import
'sys       import
'stack     import
'seq       import
'dict      import
'time      import
'fs        import
'lite? (
  (
    'crypto    import
    'math      import
    'net       import
    'http      import
  ) ROOT with
) unless
; Unseal prompt symbol
'prompt    unseal
```

Essentially, this causes min to import *all* the modules (except for some if the **lite** flag was defined at compilation time) and unseals the {#link-operator||lang||prompt#} symbol so that it can be customized. If you want, you can provide your own prelude file to specify your custom behaviors, selectively import modules, and define your own symbols, like this:

> %min-terminal%
> [$](class:prompt) min -i -p:myfile.min

## Embedding min in your Nim program

If you'd like to use min as a scripting language within your own program, and maybe extend it by implementing additional operators, you can use min as a Nim library.

To do so:

1. Install min sources using Nifty as explained in the {#link-page||download||Download#} section.
2. Import it in your Nim file.
3. Implement a new `proc` to define the module.

The following code is taken from [HastySite](https://github.com/h3rald/hastysite) and shows how to define a new `hastysite` module containing some symbols (`preprocess`, `postprocess`, `process-rules`, ...):

```
import min

proc hastysite_module*(i: In, hs1: HastySite) =
  var hs = hs1
  let def = i.define()
  
  def.symbol("preprocess") do (i: In):
    hs.preprocess()

   def.symbol("postprocess") do (i: In):
    hs.postprocess()

  def.symbol("process-rules") do (i: In):
    hs.interpret(hs.files.rules)

  # ...

  def.finalize("hastysite")
```

Then you need to:

4. Instantiate a new min interpreter using the `newMinInterpreter` proc.
5. Run the `proc` used to define the module.
6. Call the `interpret` method to interpret a min file or string:

```
proc interpret(hs: HastySite, file: string) =
  var i = newMinInterpreter(file, file.parentDir)
  i.hastysite_module(hs)
  i.interpret(newFileStream(file, fmRead))
```

> %tip%
> Tip
> 
> For more information on how to create new modules with Nim, have a look in the [lib folder](https://github.com/h3rald/min/tree/master/lib) of the min repository, which contains all the min modules included in the standard library.


## Implementing min modules as dynamic libraries

> %warning%
> Warning
> 
> This technique is currently highly experimental, it has not been tested extensively and it may not even work properly.

If you just want to add a new module to min providing functinalities that cannot be built natively with min operators, you can also implement a min module in Nim and compile it to a dynamic library which can be linked dynamically when min is started.

In order to do this, you don't even need to download the whole min source code, you just need to download the [mindyn.nim](https://github.com/h3rald/min/blob/master/mindyn.nim) file and import it in your Nim program. 

The following code shows how to create a simple min module called *dyntest* containing only a single operator *dynplus*, which essentially returns the sum of two numbers:

```
import mindyn

proc dyntest*(i: In) {.dynlib, exportc.} =

  let def = i.define()

  def.symbol("dynplus") do (i: In):
    let vals = i.expect("num", "num")
    let a = vals[0]
    let b = vals[1]
    if a.isInt:
      if b.isInt:
        i.push newVal(a.intVal + b.intVal)
      else:
        i.push newVal(a.intVal.float + b.floatVal)
    else:
      if b.isFloat:
        i.push newVal(a.floatVal + b.floatVal)
      else:
        i.push newVal(a.floatVal + b.intVal.float)

  def.finalize("dyntest")
```

Note that the `mindym.nim` file contains the signatures of all the `proc`s that are commonly used to define min modules, but not their implementation. Such `proc`s will become available at run time when the dynamic library is linked to the min executable.

You can compile the following library by running the following command:

> %min-terminal%
> [$](class:prompt) nim c \-\-app:lib -d:release \
> \-\-noMain dyntest.nim

If you are using [clang](https://clang.llvm.org/) to compile Nim code, you may need to run the following command instead:

> %min-terminal%
> [$](class:prompt) nim c \-\-app:lib -d:release \-\-noMain \
> -l:"-undefined dynamic\_lookup" dyntest.nim

Now you should have a `libdyntest.so|dyn|dll` file. To make min load it and link it automatically when it starts, just run:

> %min-terminal%
> [$](class:prompt) min \-\-install:libdyntest.dyn

This command will copy the library file to `$HOME/.minlibs/` (`%HOMEPATH%\.minlibs\` on Windows). min looks for dynamic libraries in this folder when it starts.

> %note%
> Notes
> 
> * The dynamic library file must have the same name as the module it defines (*dyntest* in this case).
> * At startup, min links all your installed dynamic libraries but does not import the modules automatically.

If you wish to uninstall the library, run the following command instead:

> %min-terminal%
> [$](class:prompt) min \-\-uninstall:libdyntest.dyn