Nim Programming Language

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[L1 ] 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