Skip to content

Commit 6ab5e95

Browse files
authored
Merge pull request #46 from Clonkk/devel
Devel
2 parents b010273 + fee17a2 commit 6ab5e95

File tree

6 files changed

+105
-61
lines changed

6 files changed

+105
-61
lines changed

examples/config.nims

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
switch("outdir", "bin")
2+
switch("path", "..")

nimjl.nimble

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Nimjl
22
# Licensed and distributed under MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
3-
version = "0.7.6"
3+
version = "0.8.0"
44
author = "Regis Caillaud"
55
description = "Nim Julia bridge"
66
license = "MIT"

nimjl/conversions/dict_tuples.nim

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ proc jlTupleToNim*(val: JlValue, tup: var tuple) =
3434
else:
3535
raise newException(JlError, "Tuple conversion from Julia to Nim failed ! Fields must identical")
3636

37-
proc jlDictToNim*[U, V: string|SomeNumber|bool](val: JlValue, tab: var Table[U, V]) =
37+
proc jlDictToNim*[U, V](val: JlValue, tab: var Table[U, V]) =
3838
# julia> collect(keys(val))
3939
var keys = jlCall("keys", val)
4040
keys = jlCall("collect", keys)

nimjl/cores.nim

+9-3
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ template JlNothing*(): JlValue = jlEval("nothing")
6161

6262
template JlCode*(body: string) =
6363
block:
64-
discard jleval(body)
64+
discard jlEval(body)
6565

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

81+
# proc jlVmSaveExit*(fpath: string) =
82+
# discard jlEval(fmt"exit_save_sysimage({fpath})")
83+
8184
#########################################
8285
var staticContents: Table[string, string]
8386

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

104+
# proc jlVmInitWithImg*(fpath: string) =
105+
# jl_init_with_image(JuliaBinDir.cstring, fpath.cstring)
106+
101107
proc jlVmInit*(nthreads: int) =
102108
putEnv("JULIA_NUM_THREADS", $nthreads)
103109
jlVmInit()
@@ -108,7 +114,7 @@ proc jlVmInit(pathToImage: string) {.used.} =
108114
if not jlVmIsInit():
109115
let jlBinDir = cstring(JuliaPath / "bin")
110116
jl_init_with_image(jlBinDir, pathToImage.cstring)
111-
loadJlRessources()
117+
# loadJlRessources()
112118
return
113119

114120
# raise newException(JlError, "jl_init_with_image(...) must be called once per process")

nimjl/glucose.nim

+82-53
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This file is named glucose because it gives you sugar ;)
22
# It contains most syntactic sugar to ease using Julia inside Nim
3-
import std/[os, strutils, strformat]
3+
import std/[os, strutils, strformat, tables]
44
import ./types
55
import ./cores
66
import ./functions
@@ -10,6 +10,50 @@ import ./private/jlcores
1010

1111
type Julia* = object
1212

13+
#####################################################
14+
# Interop and utility
15+
#####################################################
16+
proc `$`*(val: JlValue): string =
17+
jlCall("string", val).to(string)
18+
19+
proc `$`*(val: JlModule): string =
20+
jlCall("string", val).to(string)
21+
22+
proc `$`*[T](val: JlArray[T]): string =
23+
jlCall("string", val).to(string)
24+
25+
proc `$`*(val: JlFunc): string =
26+
jlCall("string", val).to(string)
27+
28+
proc `$`*(val: JlSym): string =
29+
jlCall("string", val).to(string)
30+
31+
# typeof is taken by Nim already
32+
proc jltypeof*(x: JlValue): JlValue =
33+
## Call the Julia function typeof
34+
jlCall("typeof", x)
35+
36+
proc len*(val: JlValue): int =
37+
##Call length
38+
jlCall("length", val).to(int)
39+
40+
proc firstindex*(val: JlValue): int =
41+
## Call firstindex
42+
jlCall("firstindex", val).to(int)
43+
44+
proc lastindex*(val: JlValue): int =
45+
## Call lastindex
46+
jlCall("lastindex", val).to(int)
47+
48+
template getproperty*(val: JlValue, propertyname: string): JlValue =
49+
## Call getproperty
50+
jlCall("getproperty", val, jlSym(propertyname))
51+
52+
template setproperty*(val: JlValue, propertyname: string, newval: untyped) =
53+
## Call setproperty
54+
discard jlCall("setproperty!", val, jlSym(propertyname), newval)
55+
56+
1357
proc init*(jl: type Julia, nthreads: int = 1) =
1458
jlVmInit(nthreads)
1559

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

83+
proc checkJlPkgSpec(installed: Table[string, string], package: JlPkgSpec) : bool =
84+
# Check if package is installed with the correct version
85+
86+
result = false
87+
if installed.contains(package.name):
88+
let installedVer = installed[package.name]
89+
var verCheck = ""
90+
if installedVer != "nothing":
91+
# Split + symbol for some reason Julia.Pkg sometimes use it even if it's outside of semver
92+
verCheck = installedVer.split('+')[0]
93+
94+
if package.version.isEmptyOrWhitespace():
95+
# If no Pkg version is specified, package presence is enough
96+
result = true
97+
else:
98+
# Else result is true if semver matches
99+
result = (verCheck == package.version)
100+
39101
# Workaround because named parameters do not work inside closure for proc defined in template
40102
# TODO : Should string be static ?
41103
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 = "") =
@@ -94,16 +156,26 @@ template init*(jl: type Julia, nthreads: int, body: untyped) =
94156
jl_init()
95157
# Module installation
96158
Julia.useModule("Pkg")
159+
160+
let
161+
jlExistingPkgStr = "Dict(x[2].name => string(x[2].version) for x in Pkg.dependencies())"
162+
jlPkgsExisting = jlEval(jlExistingPkgStr)
163+
installed = jlPkgsExisting.to(Table[string, string])
164+
165+
for pkgspec in packages:
166+
if not checkJlPkgSpec(installed, pkgspec):
167+
var exprs: seq[string] = @[jlExpr(":.", ":Pkg", "QuoteNode(:add)")]
168+
for key, field in pkgspec.fieldPairs():
169+
let fname = ":" & key
170+
if not isEmptyOrWhitespace(field):
171+
exprs.add jlExpr(":kw", fname, field)
172+
173+
let strexpr = jlExpr(":call", exprs)
174+
var jlexpr = jlEval(strexpr)
175+
# Will crash if version are invalid
176+
discard jlTopLevelEval(jlexpr)
177+
97178
for pkgspec in packages:
98-
var exprs: seq[string] = @[jlExpr(":.", ":Pkg", "QuoteNode(:add)")]
99-
for key, field in pkgspec.fieldPairs():
100-
let fname = ":" & key
101-
if not isEmptyOrWhitespace(field):
102-
exprs.add jlExpr(":kw", fname, field)
103-
let strexpr = jlExpr(":call", exprs)
104-
var jlexpr = jlEval(strexpr)
105-
# Will crash if version are invalid
106-
discard jlTopLevelEval(jlexpr)
107179
# TODO : handle precompilation ?
108180
# Julia.precompile()
109181
jlUsing(pkgspec.name)
@@ -133,49 +205,6 @@ proc includeFile*(jl: type Julia, fname: string) =
133205
# macro loadModule*(jl: type Julia, modname: untyped) =
134206
# TODO generate a proc ``modname`` that returns module
135207

136-
#####################################################
137-
# Interop and utility
138-
#####################################################
139-
proc `$`*(val: JlValue): string =
140-
jlCall("string", val).to(string)
141-
142-
proc `$`*(val: JlModule): string =
143-
jlCall("string", val).to(string)
144-
145-
proc `$`*[T](val: JlArray[T]): string =
146-
jlCall("string", val).to(string)
147-
148-
proc `$`*(val: JlFunc): string =
149-
jlCall("string", val).to(string)
150-
151-
proc `$`*(val: JlSym): string =
152-
jlCall("string", val).to(string)
153-
154-
# typeof is taken by Nim already
155-
proc jltypeof*(x: JlValue): JlValue =
156-
## Call the Julia function typeof
157-
jlCall("typeof", x)
158-
159-
proc len*(val: JlValue): int =
160-
##Call length
161-
jlCall("length", val).to(int)
162-
163-
proc firstindex*(val: JlValue): int =
164-
## Call firstindex
165-
jlCall("firstindex", val).to(int)
166-
167-
proc lastindex*(val: JlValue): int =
168-
## Call lastindex
169-
jlCall("lastindex", val).to(int)
170-
171-
template getproperty*(val: JlValue, propertyname: string): JlValue =
172-
## Call getproperty
173-
jlCall("getproperty", val, jlSym(propertyname))
174-
175-
template setproperty*(val: JlValue, propertyname: string, newval: untyped) =
176-
## Call setproperty
177-
discard jlCall("setproperty!", val, jlSym(propertyname), newval)
178-
179208
#####################################################
180209
# Syntactic sugar
181210
#####################################################

nimjl/private/jlarrays.nim

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import ../config
22
import ../types
33
import ./jlcores
4-
import std/strformat
4+
import std/[strformat, complex]
5+
6+
# template jlType*(T: typedesc[Complex32]): JlDataType = jlEval("ComplexF32")
7+
# template jlType*(T: typedesc[Complex64]): JlDataType = jlEval("ComplexF64")
58

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

2932
{.pop.}
3033

31-
proc julia_apply_array_type*[T: SomeNumber|bool|char](dim: int): ptr jl_value =
32-
let jl_type = cast[ptr jl_value](julia_type(T))
34+
proc julia_apply_array_type*[T: Complex32|Complex64|SomeNumber|bool|char](dim: int): ptr jl_value =
35+
when T is Complex32:
36+
let jl_type = jlEval("ComplexF32")
37+
elif T is Complex64:
38+
let jl_type = jlEval("ComplexF64")
39+
else:
40+
let jl_type = cast[ptr jl_value](julia_type(T))
3341
jl_apply_array_type(jl_type, dim.csize_t)
3442

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

0 commit comments

Comments
 (0)