☰

# NimScript

Strictly speaking, `NimScript` is the subset of Nim that can be evaluated by Nim's builtin virtual machine (VM). This VM is used for Nim's compiletime function evaluation features.

The `nim` executable processes the `.nims` configuration files in the following directories (in this order; later files overwrite previous settings):

1. If environment variable `XDG_CONFIG_HOME` is defined, `$XDG_CONFIG_HOME/nim/config.nims` or `~/.config/nim/config.nims` (POSIX) or `%APPDATA%/nim/config.nims` (Windows). This file can be skipped with the `--skipUserCfg` command line option.
2. `$parentDir/config.nims` where `$parentDir` stands for any parent directory of the project file's path. These files can be skipped with the `--skipParentCfg` command line option.
3. `$projectDir/config.nims` where `$projectDir` stands for the project's path. This file can be skipped with the `--skipProjCfg` command line option.
4. A project can also have a project specific configuration file named `$project.nims` that resides in the same directory as `$project.nim`. This file can be skipped with the same `--skipProjCfg` command line option.

For available procs and implementation details see nimscript.

# Limitations

NimScript is subject to some limitations caused by the implementation of the VM (virtual machine):

- Nim's FFI (foreign function interface) is not available in NimScript. This means that any stdlib module which relies on `importc` can not be used in the VM.
- `ptr` operations are are hard to emulate with the symbolic representation the VM uses. They are available and tested extensively but there are bugs left.
- `var T` function arguments rely on `ptr` operations internally and might also be problematic in some cases.
- More than one level of `ref` is generally not supported (for example, the type `ref ref int`).
- Multimethods are not available.
- `random.randomize()` requires an `int64` explicitly passed as argument, you must pass a Seed integer.

# Standard library modules

At least the following standard library modules are available:

- algorithm
- base64
- bitops
- chains
- colors
- complex
- distros
- std/editdistance
- htmlgen
- htmlparser
- httpcore
- json
- lenientops
- macros
- math
- options
- os
- parsecfg
- parsecsv
- parsejson
- parsesql
- parseutils
- punycode
- random
- ropes
- std/setutils
- stats
- strformat
- strmisc
- strscans
- strtabs
- strutils
- sugar
- unicode
- unidecode
- uri
- std/wordwrap
- xmlparser

In addition to the standard Nim syntax (system module), NimScripts support the procs and templates defined in the nimscript module too.

See also:

- Check the tests for more information about modules compatible with NimScript

# NimScript as a configuration file

A command-line switch `--FOO` is written as `switch("FOO")` in NimScript. Similarly, command-line `--FOO:VAL` translates to `switch("FOO", "VAL")`.

Here are few examples of using the `switch` proc:

    # command-line: --opt:size
    switch("opt", "size")
    # command-line: --define:release or -d:release
    switch("define", "release")
    # command-line: --forceBuild
    switch("forceBuild")
    # command-line: --hint[Conf]:off or --hint:Conf:off
    switch("hint", "[Conf]:off")

NimScripts also support `--` templates for convenience, which look like command-line switches written as-is in the NimScript file. So the above example can be rewritten as:

    --opt:size
    --define:release
    --forceBuild

Note: In general, the define switches can also be set in NimScripts using `switch` or `--`, as shown in above examples. Few `define` switches such as `-d:strip`, `-d:lto` and `-d:lto_incremental` cannot be set in NimScripts.

# NimScript as a build tool

The `task` template that the `system` module defines allows a NimScript file to be used as a build tool. The following example defines a task `build` that is an alias for the `c` command:

    task build, "builds an example":
      setCommand "c"

In fact, as a convention the following tasks should be available:

Task | Description
--- | ---
`help` | List all the available NimScript tasks along with their docstrings.
`build` | Build the project with the required backend (`c`, `cpp` or `js`).
`tests` | Runs the tests belonging to the project.
`bench` | Runs benchmarks belonging to the project.

Look at the module distros for some support of the OS's native package managers.

# Nimble integration

See the Nimble readme for more information.

# Standalone NimScript

NimScript can also be used directly as a portable replacement for Bash and Batch files. Use `nim myscript.nims` to run `myscript.nims`. For example, installation of Nimble could be accomplished with this simple script:

    mode = ScriptMode.Verbose

    var id = 0
    while dirExists("nimble" & $id):
      inc id

    exec "git clone https://github.com/nim-lang/nimble.git nimble" & $id

    withDir "nimble" & $id & "/src":
      exec "nim c nimble"

    mvFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe

On Unix, you can also use the shebang `#!/usr/bin/env nim`, as long as your filename ends with `.nims`:

    #!/usr/bin/env nim
    mode = ScriptMode.Silent

    echo "hello world"

Use `#!/usr/bin/env -S nim e --hints:off` to disable hints and relax the file extension constraint.

# Benefits

## Cross-Platform

It is a cross-platform scripting language that can run where Nim can run, e.g. you can not run Batch or PowerShell on Linux or Mac, the Bash for Linux might not run on Mac, there are no unit tests tools for Batch, etc.

NimScript can detect on which platform, operating system, architecture, and even which Linux distribution is running on, allowing the same script to support a lot of systems.

See the following (incomplete) example:

    import std/distros

    # Architectures.
    if defined(amd64):
      echo "Architecture is x86 64Bits"
    elif defined(i386):
      echo "Architecture is x86 32Bits"
    elif defined(arm):
      echo "Architecture is ARM"

    # Operating Systems.
    if defined(linux):
      echo "Operating System is GNU Linux"
    elif defined(windows):
      echo "Operating System is Microsoft Windows"
    elif defined(macosx):
      echo "Operating System is Apple OS X"

    # Distros.
    if detectOs(Ubuntu):
      echo "Distro is Ubuntu"
    elif detectOs(ArchLinux):
      echo "Distro is ArchLinux"
    elif detectOs(Debian):
      echo "Distro is Debian"

## Uniform Syntax

The syntax, style, and rest of the ecosystem is the same as for compiled Nim, that means there is nothing new to learn, no context switch for developers.

## Powerful Metaprogramming

NimScript can use Nim's templates, macros, types, concepts, effect tracking system, and more, you can create modules that work on compiled Nim and also on interpreted NimScript.

`func` will still check for side effects, `debugEcho` also works as expected, making it ideal for functional scripting metaprogramming.

This is an example of a third party module that uses macros and templates to translate text strings on unmodified NimScript:

    import nimterlingua
    nimterlingua("translations.cfg")
    echo "cat"  # Run with -d:RU becomes "kot", -d:ES becomes "gato", ...

translations.cfg

    [cat]
    ES = gato
    IT = gatto
    RU = kot
    FR = chat

- Nimterlingua

## Graceful Fallback

Some features of compiled Nim may not work on NimScript, but often a graceful and seamless fallback degradation is used.

See the following NimScript:

    if likely(true):
      discard
    elif unlikely(false):
      discard

    proc foo() {.compiletime.} = echo NimVersion

    static:
      echo CompileDate

`likely()`, `unlikely()`, `static:` and `{.compiletime.}` will produce no code at all when run on NimScript, but still no error nor warning is produced and the code just works.

## Evolving Scripting language

NimScript evolves together with Nim, occasionally new features might become available on NimScript, adapted from compiled Nim or added as new features on both.

## Scripting Language with a Package Manager

You can create your own modules to be compatible with NimScript, and check Nimble to search for third party modules that may work on NimScript.

## DevOps Scripting

You can use NimScript to deploy to production, run tests, build projects, do benchmarks, generate documentation, and all kinds of DevOps/SysAdmin specific tasks.

- An example of a third party NimScript that can be used as a project-agnostic tool.