Skip to content

Commit

Permalink
Merge pull request #42 from Clonkk/devel
Browse files Browse the repository at this point in the history
Devel
  • Loading branch information
Clonkk authored Apr 4, 2022
2 parents 7a6924c + 18d6521 commit f1e21fd
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 21 deletions.
12 changes: 4 additions & 8 deletions examples/ex06_tensor.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand All @@ -37,6 +34,5 @@ proc main() =
echo tensor
echo res.shape()


when isMainModule:
main()
2 changes: 1 addition & 1 deletion nimjl.nimble
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
4 changes: 3 additions & 1 deletion nimjl/config.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
16 changes: 10 additions & 6 deletions nimjl/cores.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
Expand Down Expand Up @@ -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
Expand All @@ -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
30 changes: 25 additions & 5 deletions nimjl/glucose.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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()
Expand All @@ -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) =
Expand All @@ -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)

#####################################################
Expand All @@ -110,26 +125,31 @@ 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
for a in 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
Expand Down

0 comments on commit f1e21fd

Please sign in to comment.