Updated 2017-09-08 14:10:23 by MJ

Nim (formerly Nimrod) is a statically typed, imperative programming language that tries to give the programmer ultimate power without compromising runtime efficiency. It is designed to rival C in execution speed.

It supports soft realtime garbage collection on thread-local heaps and uses asynchronous message passing between threads. There are language features for parallelizing functions via a binding to [OpenMP]. It has system programming features such as direct access to memory. Pointers to garbage collected memory are distinguished from pointers to manually managed memory.

It compiles to commented C code. The compiler can create standard shared libraries linkable from C programs. It can also be made to output JavaScript, C++ or Objective C.

The Nim distribution contains an officially supported wrapper[1] for Tcl that makes calling Tcl code from Nim code easy. Example:
# Example to embed TCL in Nim

import tcl, os

const
  myScript = """puts "Hello, World - In quotes" """
  myScript2 = """
package require Tk
pack [entry .e -textvar e -width 50]
bind .e <Return> {
set e [regsub { *=.*} $e ""] ;# remove evaluation (Chris)
catch {expr [string map {/ *1./} $e]} res
append e " = $res"
}
"""

FindExecutable(getAppFilename())
var interp = CreateInterp()
if interp == nil: quit("cannot create TCL interpreter")
if Init(interp) != TCL_OK:
  quit("cannot init interpreter")
if tcl.Eval(interp, myScript) != TCL_OK:
  quit("cannot execute script.tcl")

MJ: The following example demonstrates starting Tk and registering a Nim procedure (call) for use in Tcl:
import os,tcl

# initialize Tcl and Tk
FindExecutable(getAppFilename())
let interp = CreateInterp()
discard  Eval(interp, "set env(TCL_LIBRARY) [file join [file dir [info nameofexecutable]] tclbridge tcl tcl8.6]")

var result = Init(interp)
if result != tcl.TCL_OK:
  quit("Could't load Tcl " & $GetStringResult(interp))

discard  Eval(interp, "package require Tk")


# close app with main window
discard Eval(interp, "wm protocol . WM_DELETE_WINDOW exit")

# register call proc
proc call(clientData: TClientData, interp: PInterp, argc: int,
                    argv: TArgv): int{.cdecl.} =
  tcl.SetResult(interp, cstring("name called"), cast[TFreeProc](nil))
  return tcl.TCL_OK

discard  CreateObjCommand(interp, cstring("name"), cast[TObjCmdProc](call), cast[TClientData](nil),  cast[TCmdDeleteProc](nil))

# execute call proc from Tcl
result = Eval(interp, "name")
if result != tcl.TCL_OK:
  quit("Could't create command " & $GetStringResult(interp))

echo GetStringResult(interp)

# wait till main window closes
discard  Eval(interp, "vwait forever")

Creating an extension in Nim

The following small piece of Nim of code creates a loadable extension for Windows. Build the dll with:
nim c --app:lib ext.nim

ext.nim

const libName* = "tcl86.dll"
proc Tcl_PkgProvide(interp: pointer, name: cstring, version: cstring) : cint {.cdecl, importc, dynlib: libName.}

proc Ext_Init(interp: pointer): cint {.exportc,dynlib,cdecl.} =
  return Tcl_PkgProvide(interp, "ext", "0.1")

Create a stubs enabled extension

MJ - To create a stubs enabled extension one can use the nimble package at: https://github.com/mpcjanssen/tclstubs-nimble . This is only tested on windows and linux. An example extension would look like the code below. Building is just a very simple nim c --app:lib -d:release tcluuid.nim
import uuids
from tclstubs as Tcl import nil

proc Uuids_Cmd(clientData: Tcl.PClientData, interp: Tcl.PInterp, objc: cint, objv: Tcl.PPObj): cint =
  Tcl.SetObjResult(interp, Tcl.NewStringObj($genUUID(),-1))
  return Tcl.OK


proc Uuids_Init(interp: Tcl.PInterp): cint {.exportc,dynlib.} =
  discard Tcl.InitStubs(interp, "8.5",0)
  if Tcl.CreateObjCommand(interp, "uuid", Uuids_Cmd, nil, nil)!=nil:
    return Tcl.OK
  else:
    return Tcl.ERROR