diff --git a/examples/ex06_tensor.nim b/examples/ex06_tensor.nim index ae5069c..2397c3e 100644 --- a/examples/ex06_tensor.nim +++ b/examples/ex06_tensor.nim @@ -2,18 +2,15 @@ import arraymancer import nimjl proc main() = - Julia.init() # Initialize Julia VM. This should be done once in the lifetime of your program. + Julia.init: + Pkg: + add "LinearAlgebra" + # Initialize Julia VM. This should be done once in the lifetime of your program. # Just be aware that Nim Seq/Tensor are Row Major. # Julia usually work in Column major. var tensor = randomTensor[float](2, 3, 10.0) - # Uncomment this block if you haven't installed LinearAlgebra on Julia yet - ################################################## - # Install LinearAlgebra package - # let evRes = jlEval("""using Pkg; Pkg.add("LinearAlgebra")""") - # doAssert not isNil(evRes) - ################################################## # Use the module jlUseModule("LinearAlgebra") @@ -37,6 +34,5 @@ proc main() = echo tensor echo res.shape() - when isMainModule: main() diff --git a/nimjl.nimble b/nimjl.nimble index 4f82e30..9b6e3c4 100644 --- a/nimjl.nimble +++ b/nimjl.nimble @@ -1,6 +1,6 @@ # Nimjl # Licensed and distributed under MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). -version = "0.7.0" +version = "0.7.1" author = "Regis Caillaud" description = "Nim Julia bridge" license = "MIT" diff --git a/nimjl/config.nim b/nimjl/config.nim index 21d90a0..5000741 100644 --- a/nimjl/config.nim +++ b/nimjl/config.nim @@ -6,7 +6,9 @@ const JuliaBinPath = gorge("julia -E Sys.BINDIR").replace("\"", "") # JuliaPath should be parent folder of Julia-bindir # This is resolved AT COMPILE TIME. Therefore, using the environment of the machine that compile. # If you want to ship a binary, you need to install in a fixed path and pass this path using -d:JuliaPath="/path/to/Julia" -const JuliaPath* {.strdefine.} = if not existsEnv("JULIA_PATH"): JuliaBinPath.parentDir() else: getEnv("JULIA_PATH") +const JuliaPath* {.strdefine.} = if not existsEnv("JULIA_PATH"): JuliaBinPath.parentDir().normalizedPath().absolutePath() else: getEnv("JULIA_PATH") +static: + echo JuliaPath const JuliaIncludesPath* = JuliaPath / "include" / "julia" const JuliaHeader* = "julia.h" const JuliaLibPath* = JuliaPath / "lib" diff --git a/nimjl/cores.nim b/nimjl/cores.nim index cb450c9..0e271e5 100644 --- a/nimjl/cores.nim +++ b/nimjl/cores.nim @@ -3,46 +3,48 @@ import ./private/jlcores import ./config import std/[strformat, os, macros, tables] -# Convert a string to Julia Symbol proc jlSym*(symname: string): JlSym = + ## Convert a string to Julia Symbol result = jl_symbol(symname.cstring) proc jlExceptionHandler*() = let excpt: JlValue = jl_exception_occurred() + ## Convert a Julia exception to Nim exception if not isNil(excpt): let msg = $(jl_exception_message()) raise newException(JlError, msg) else: discard -# Eval function that checkes error proc jlEval*(code: string): JlValue = + ## Eval function that checks JuliaError result = jl_eval_string(code) jlExceptionHandler() -# Include file or use module -# Check for nil result proc jlInclude*(filename: string) = + ## Include Julia file let tmp = jlEval(&"include(\"{filename}\")") if tmp.isNil: raise newException(JlError, "&Cannot include file {filename}") proc jlUseModule*(modname: string) = + ## Call using module let tmp = jlEval(&"using {modname}") if tmp.isNil: raise newException(JlError, "&Cannot use module {modname}") -# Just for convenience since Julia funciton is called using proc jlUsing*(modname: string) = + ## Alias for conveniece jlUseModule(modname) -# Import can be useful proc jlImport*(modname: string) = + ## Import Julia file let tmp = jlEval(&"import {modname}") if tmp.isNil: raise newException(JlError, "&Cannot import module {modname}") proc jlGetModule*(modname: string): JlModule = + ## Get Julia module. Useful to resolve ambiguity let tmp = jlEval(modname) if tmp.isNil: raise newException(JlError, "&Cannot load module {modname}") @@ -105,6 +107,7 @@ proc private_addKeyVal*(key, value: string) = staticContents[key] = value macro jlEmbedDir*(dirname: static[string]): untyped = + ## Embed all Julia files from specified directory result = newStmtList() let path = getProjectPath() / dirname # echo path @@ -122,5 +125,6 @@ macro jlEmbedDir*(dirname: static[string]): untyped = # echo result.repr proc jlEmbedFile*(filename: static[string]) = + ## Embed specific Julia file const jlContent = staticRead(getProjectPath() / filename) staticContents[filename] = jlContent diff --git a/nimjl/glucose.nim b/nimjl/glucose.nim index 2803dac..ed09239 100644 --- a/nimjl/glucose.nim +++ b/nimjl/glucose.nim @@ -13,22 +13,26 @@ proc init*(jl: type Julia) = jlVmInit() template init*(jl: type Julia, body: untyped) = - # Pkg installation interface - var packages : seq[string] + ## Init Julia VM + var packages: seq[string] template Pkg(innerbody: untyped) = + ## Pkg installation API proc add(pkgname: string) = packages.add pkgname innerbody - # Embedding ressources interface template Embed(innerbody: untyped) = + ## Emded Julia file explicitly of from a directory template file(filename: static[string]) = + ## Embed file jlEmbedFile(filename) template dir(dirname: static[string]) = + ## Embed directory jlEmbedDir(dirname) template thisDir() = + ## Embed current dir jlEmbedDir("") block: @@ -44,6 +48,7 @@ template init*(jl: type Julia, body: untyped) = let pkg = Julia.getModule("Pkg") for pkgname in packages: discard jlCall(pkg, "add", pkgname) + jlUsing(pkgname) # Eval Julia code embedded loadJlRessources() @@ -52,15 +57,19 @@ template init*(jl: type Julia, body: untyped) = raise newException(JlError, "Error Julia.init() has already been initialized") proc exit*(jl: type Julia, exitcode: int = 0) = + ## Exit Julia VM jlVmExit(exitcode.cint) proc useModule*(jl: type Julia, modname: string) = + ## Alias for jlUseModule. "using" is a keyword in Nim and so wasn't available jlUseModule(modname) -proc getModule*(jl: type Julia, modname: string) : JlModule = +proc getModule*(jl: type Julia, modname: string): JlModule = + ## Alias for jlGetModule jlGetModule(modname) proc includeFile*(jl: type Julia, fname: string) = + ## Alias for jlInclude jlInclude(fname) # macro loadModule*(jl: type Julia, modname: untyped) = @@ -86,21 +95,27 @@ proc `$`*(val: JlSym): string = # typeof is taken by Nim already proc jltypeof*(x: JlValue): JlValue = + ## Call the Julia function typeof jlCall("typeof", x) proc len*(val: JlValue): int = + ##Call length jlCall("length", val).to(int) proc firstindex*(val: JlValue): int = + ## Call firstindex jlCall("firstindex", val).to(int) proc lastindex*(val: JlValue): int = + ## Call lastindex jlCall("lastindex", val).to(int) template getproperty*(val: JlValue, propertyname: string): JlValue = + ## Call getproperty jlCall("getproperty", val, jlSym(propertyname)) template setproperty*(val: JlValue, propertyname: string, newval: untyped) = + ## Call setproperty discard jlCall("setproperty!", val, jlSym(propertyname), newval) ##################################################### @@ -110,7 +125,7 @@ import std/macros {.experimental: "dotOperators".} -macro unpackVarargs_first(callee, arg_first: untyped; arg_second: untyped, args: varargs[untyped]):untyped = +macro unpackVarargs_first(callee, arg_first: untyped; arg_second: untyped, args: varargs[untyped]): untyped = result = newCall(callee) result.add arg_first result.add arg_second @@ -118,18 +133,23 @@ macro unpackVarargs_first(callee, arg_first: untyped; arg_second: untyped, args: result.add a template `.()`*(jl: type Julia, funcname: untyped, args: varargs[JlValue, toJlVal]): JlValue = + ## Alias to call a Julia function jlCall(astToStr(funcname), args) template `.()`*(jlmod: JlModule, funcname: untyped, args: varargs[JlValue, toJlVal]): JlValue = + ## Alias to call a Julia function jlCall(jlmod, astToStr(funcname), args) template `.()`*(jlval: JlValue, funcname: untyped, args: varargs[JlValue, toJlVal]): JlValue = + ## Alias to call a Julia function unpackVarargs_first(jlCall, astToStr(funcname), jlval, args) template `.`*(jlval: JlValue, propertyname: untyped): JlValue = + ## Alias for getproperty getproperty(jlval, astToStr(propertyname)) template `.=`*(jlval: var JlValue, fieldname: untyped, newval: untyped) = + ## Alias for setproperty setproperty(jlval, astToStr(fieldname), newval) # Re-export