Skip to content

Commit

Permalink
Merge pull request #46 from Clonkk/devel
Browse files Browse the repository at this point in the history
Devel
  • Loading branch information
Clonkk authored Aug 30, 2023
2 parents b010273 + fee17a2 commit 6ab5e95
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 61 deletions.
1 change: 1 addition & 0 deletions examples/config.nims
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
switch("outdir", "bin")
switch("path", "..")
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.6"
version = "0.8.0"
author = "Regis Caillaud"
description = "Nim Julia bridge"
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion nimjl/conversions/dict_tuples.nim
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ proc jlTupleToNim*(val: JlValue, tup: var tuple) =
else:
raise newException(JlError, "Tuple conversion from Julia to Nim failed ! Fields must identical")

proc jlDictToNim*[U, V: string|SomeNumber|bool](val: JlValue, tab: var Table[U, V]) =
proc jlDictToNim*[U, V](val: JlValue, tab: var Table[U, V]) =
# julia> collect(keys(val))
var keys = jlCall("keys", val)
keys = jlCall("collect", keys)
Expand Down
12 changes: 9 additions & 3 deletions nimjl/cores.nim
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ template JlNothing*(): JlValue = jlEval("nothing")

template JlCode*(body: string) =
block:
discard jleval(body)
discard jlEval(body)

proc jlVmIsInit*(): bool =
bool(jl_is_initialized())
Expand All @@ -78,6 +78,9 @@ proc jlVmExit*(exit_code: cint = 0.cint) =
# Do nothing -> atexit_hook must be called once
# raise newException(JlError, "jl_atexit_hook() must be called once per process")

# proc jlVmSaveExit*(fpath: string) =
# discard jlEval(fmt"exit_save_sysimage({fpath})")

#########################################
var staticContents: Table[string, string]

Expand All @@ -94,10 +97,13 @@ proc jlVmInit*() =
## Subsequent calls after the first one will be ignored
if not jlVmIsInit():
jl_init()
loadJlRessources()
# loadJlRessources()
return
# raise newException(JlError, "jl_init() must be called once per process")

# proc jlVmInitWithImg*(fpath: string) =
# jl_init_with_image(JuliaBinDir.cstring, fpath.cstring)

proc jlVmInit*(nthreads: int) =
putEnv("JULIA_NUM_THREADS", $nthreads)
jlVmInit()
Expand All @@ -108,7 +114,7 @@ proc jlVmInit(pathToImage: string) {.used.} =
if not jlVmIsInit():
let jlBinDir = cstring(JuliaPath / "bin")
jl_init_with_image(jlBinDir, pathToImage.cstring)
loadJlRessources()
# loadJlRessources()
return

# raise newException(JlError, "jl_init_with_image(...) must be called once per process")
Expand Down
135 changes: 82 additions & 53 deletions nimjl/glucose.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is named glucose because it gives you sugar ;)
# It contains most syntactic sugar to ease using Julia inside Nim
import std/[os, strutils, strformat]
import std/[os, strutils, strformat, tables]
import ./types
import ./cores
import ./functions
Expand All @@ -10,6 +10,50 @@ import ./private/jlcores

type Julia* = object

#####################################################
# Interop and utility
#####################################################
proc `$`*(val: JlValue): string =
jlCall("string", val).to(string)

proc `$`*(val: JlModule): string =
jlCall("string", val).to(string)

proc `$`*[T](val: JlArray[T]): string =
jlCall("string", val).to(string)

proc `$`*(val: JlFunc): string =
jlCall("string", val).to(string)

proc `$`*(val: JlSym): string =
jlCall("string", val).to(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)


proc init*(jl: type Julia, nthreads: int = 1) =
jlVmInit(nthreads)

Expand All @@ -36,6 +80,24 @@ type
name, url, path, subdir, rev, version, mode, level: string
JlPkgs = seq[JlPkgSpec]

proc checkJlPkgSpec(installed: Table[string, string], package: JlPkgSpec) : bool =
# Check if package is installed with the correct version

result = false
if installed.contains(package.name):
let installedVer = installed[package.name]
var verCheck = ""
if installedVer != "nothing":
# Split + symbol for some reason Julia.Pkg sometimes use it even if it's outside of semver
verCheck = installedVer.split('+')[0]

if package.version.isEmptyOrWhitespace():
# If no Pkg version is specified, package presence is enough
result = true
else:
# Else result is true if semver matches
result = (verCheck == package.version)

# Workaround because named parameters do not work inside closure for proc defined in template
# TODO : Should string be static ?
proc addImpl(pkgs: var JlPkgs, name: static string, url: static string = "", path: static string = "", subdir: static string = "", rev: static string = "", version: static string = "", mode: static string = "", level: static string = "") =
Expand Down Expand Up @@ -94,16 +156,26 @@ template init*(jl: type Julia, nthreads: int, body: untyped) =
jl_init()
# Module installation
Julia.useModule("Pkg")

let
jlExistingPkgStr = "Dict(x[2].name => string(x[2].version) for x in Pkg.dependencies())"
jlPkgsExisting = jlEval(jlExistingPkgStr)
installed = jlPkgsExisting.to(Table[string, string])

for pkgspec in packages:
if not checkJlPkgSpec(installed, pkgspec):
var exprs: seq[string] = @[jlExpr(":.", ":Pkg", "QuoteNode(:add)")]
for key, field in pkgspec.fieldPairs():
let fname = ":" & key
if not isEmptyOrWhitespace(field):
exprs.add jlExpr(":kw", fname, field)

let strexpr = jlExpr(":call", exprs)
var jlexpr = jlEval(strexpr)
# Will crash if version are invalid
discard jlTopLevelEval(jlexpr)

for pkgspec in packages:
var exprs: seq[string] = @[jlExpr(":.", ":Pkg", "QuoteNode(:add)")]
for key, field in pkgspec.fieldPairs():
let fname = ":" & key
if not isEmptyOrWhitespace(field):
exprs.add jlExpr(":kw", fname, field)
let strexpr = jlExpr(":call", exprs)
var jlexpr = jlEval(strexpr)
# Will crash if version are invalid
discard jlTopLevelEval(jlexpr)
# TODO : handle precompilation ?
# Julia.precompile()
jlUsing(pkgspec.name)
Expand Down Expand Up @@ -133,49 +205,6 @@ proc includeFile*(jl: type Julia, fname: string) =
# macro loadModule*(jl: type Julia, modname: untyped) =
# TODO generate a proc ``modname`` that returns module

#####################################################
# Interop and utility
#####################################################
proc `$`*(val: JlValue): string =
jlCall("string", val).to(string)

proc `$`*(val: JlModule): string =
jlCall("string", val).to(string)

proc `$`*[T](val: JlArray[T]): string =
jlCall("string", val).to(string)

proc `$`*(val: JlFunc): string =
jlCall("string", val).to(string)

proc `$`*(val: JlSym): string =
jlCall("string", val).to(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)

#####################################################
# Syntactic sugar
#####################################################
Expand Down
14 changes: 11 additions & 3 deletions nimjl/private/jlarrays.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import ../config
import ../types
import ./jlcores
import std/strformat
import std/[strformat, complex]

# template jlType*(T: typedesc[Complex32]): JlDataType = jlEval("ComplexF32")
# template jlType*(T: typedesc[Complex64]): JlDataType = jlEval("ComplexF64")

template julia_type(arg: typedesc): ptr jl_datatype =
jlType(arg)
Expand All @@ -28,8 +31,13 @@ proc jl_array_eltype*(x: ptr jl_value): ptr jl_datatype {.importc: "jl_array_elt

{.pop.}

proc julia_apply_array_type*[T: SomeNumber|bool|char](dim: int): ptr jl_value =
let jl_type = cast[ptr jl_value](julia_type(T))
proc julia_apply_array_type*[T: Complex32|Complex64|SomeNumber|bool|char](dim: int): ptr jl_value =
when T is Complex32:
let jl_type = jlEval("ComplexF32")
elif T is Complex64:
let jl_type = jlEval("ComplexF64")
else:
let jl_type = cast[ptr jl_value](julia_type(T))
jl_apply_array_type(jl_type, dim.csize_t)

proc julia_make_array*[T](data: ptr UncheckedArray[T], dims: openArray[int]): ptr jl_array =
Expand Down

0 comments on commit 6ab5e95

Please sign in to comment.