SciNim / Datamancer

A dataframe library with a dplyr like API
https://scinim.github.io/Datamancer/datamancer.html
MIT License
130 stars 6 forks source link

Datamancer works not with nimscript #70

Open diegovskytl opened 1 month ago

diegovskytl commented 1 month ago

I tried to run the example code from Datamancer's documentation:

import datamancer

let s1: seq[int] = @[22, 54, 34]
let s2: seq[float] = @[1.87, 1.75, 1.78]
let s3: seq[string] = @["Mike", "Laura", "Sue"]

let dfAutoNamed = toDf(s1, s2, s3)

echo dfAutoNamed

As a compiled binary works great. Out of curiosity I tried to run it as a Nimscript but I get the following output:

command: nim datamancer_intro.nims

output:

Hint: used config file '/opt/homebrew/Cellar/nim/2.0.8/nim/config/nim.cfg' [Conf]
Hint: used config file '/opt/homebrew/Cellar/nim/2.0.8/nim/config/config.nims' [Conf]
/Users/nox/.nimble/pkgs2/nimblas-0.3.0-d5033749759fc7a2a316acf623635dcb6d69d32a/nimblas/private/common.nim(52, 7) Hint: Using BLAS library with name: libblas.dylib [User]
/Users/nox/.nimble/pkgs2/arraymancer-0.7.32-e1fa31ad09f0199e93c1bf9959e1f3af5bdacc08/arraymancer/tensor/init_cpu.nim(245, 18) template/generic instantiation of `randomTensorCpu` from here
/Users/nox/.nimble/pkgs2/arraymancer-0.7.32-e1fa31ad09f0199e93c1bf9959e1f3af5bdacc08/arraymancer/tensor/init_cpu.nim(218, 18) template/generic instantiation of `allocCpuStorage` from here
/Users/nox/.nimble/pkgs2/arraymancer-0.7.32-e1fa31ad09f0199e93c1bf9959e1f3af5bdacc08/arraymancer/laser/tensor/datatypes.nim(110, 29) template/generic instantiation of `finalizer` from here
/Users/nox/.nimble/pkgs2/arraymancer-0.7.32-e1fa31ad09f0199e93c1bf9959e1f3af5bdacc08/arraymancer/laser/tensor/datatypes.nim(77, 23) Error: attempting to call undeclared routine: 'deallocShared'

Is there a solution or workaround?

It would be nice for Datamancer to work in scripts so when REPL is available for Nim, a tool similar to R/Jupyter Notebooks could be built.

Vindaar commented 1 month ago

So there's two things to mention:

1) We can make (most of) Datamancer work with NimScript by making it pick the recently added JS backend (see #62). It more or less works, I've tried it locally (Note that it still throws some errors when using nim secret due to std/random). 2) With Nim secret there is a REPL already that uses NimScript. If we ever get a good real REPL, it'll end up compiling the code anyway and Datamancer will work as expected using the "default" backend.

For 1), here is the patch I used. Apply it to a local Datamancer version of yours and your nimscript code should run. Maybe I'll merge it one of these days, but at the moment I don't think it's extremely useful.

diff --git a/src/datamancer/column.nim b/src/datamancer/column.nim
index bf71741..34f9e2f 100644
--- a/src/datamancer/column.nim
+++ b/src/datamancer/column.nim
@@ -1,7 +1,7 @@
-when not defined(js):
-  import arraymancer/tensor
-else:
+when defined(js) or defined(NimScript):
   import seq_tensor
+else:
+  import arraymancer/tensor

 import std / [sugar, strformat, tables, macros, strutils]
 import value
diff --git a/src/datamancer/dataframe.nim b/src/datamancer/dataframe.nim
index cdd8025..4a5dc5e 100644
--- a/src/datamancer/dataframe.nim
+++ b/src/datamancer/dataframe.nim
@@ -1,12 +1,12 @@
 import std / [macros, tables, strutils, options, sets, hashes, math,
               sequtils, stats, strformat, algorithm, typetraits]

-when not defined(js):
-  import arraymancer/tensor
-  export tensor
-else:
+when defined(js) or defined(NimScript):
   import seq_tensor
   export seq_tensor
+else:
+  import arraymancer/tensor
+  export tensor

 import value
 export value
diff --git a/src/datamancer/formulaExp.nim b/src/datamancer/formulaExp.nim
index 8f12ecb..8c1640a 100644
--- a/src/datamancer/formulaExp.nim
+++ b/src/datamancer/formulaExp.nim
@@ -709,7 +709,7 @@ proc convertLoop(p: Preface, dtype, fctColResType, loop: NimNode,
                  fnKind: FormulaKind,
                  generateLoop: bool): NimNode =
   let memCopyable = ["float", "int", "bool"]
-  when defined(js):
+  when defined(js) or defined(NimScript):
     let isMemcopyable = false
   else:
     let isMemCopyable = dtype.strVal in memCopyable and
diff --git a/src/datamancer/io.nim b/src/datamancer/io.nim
index 8bc6eb9..f0f9611 100644
--- a/src/datamancer/io.nim
+++ b/src/datamancer/io.nim
@@ -1,7 +1,7 @@
 import dataframe, value, column

 import std / [streams, strutils, tables, parsecsv, sequtils,  strformat, os]
-when not defined(js):
+when not (defined(js) or defined(NimScript)):
   import memfiles
   # for reading CSV files from URLs (former) and `showBrowsers` (latter)
   import httpclient, browsers
@@ -79,8 +79,7 @@ proc readCsv*(s: Stream,
       result[colHeaders[i]].add parser.rowEntry(col)
   parser.close()

-
-when defined(js):
+when defined(js) or defined(NimScript):
   type
     MemoryView[T] = seq[T]

@@ -109,7 +108,7 @@ template copyBuf(data: MemoryView[char], buf: var string,
   let nIdx = idx - colStart
   if nIdx > 0:
     buf.setLen(nIdx) # will auto reallocate if `len` is larger than capacity!
-    when defined(js):
+    when defined(js) or defined(NimScript):
       for i in 0 ..< nIdx:
         buf[i] = data[colStart + i]
     else:
@@ -667,7 +666,7 @@ proc parseCsvString*(csvData: string,
                             skipInitialSpace, quote, maxGuesses, lineBreak, eat,
                             allowLineBreaks = allowLineBreaks)

-when not defined(js):
+when not (defined(js) or defined(NimScript)):
   proc readCsvFromUrl(url: string,
                 sep: char = ',',
                 header: string = "",
@@ -749,7 +748,7 @@ when not defined(js):
     ## parser using `std/parsecsv` is available under the name `readCsvAlt`. However,
     ## it does not return a full `DataFrame`. You need to call `toDf` on the result.
     if fname.startsWith("http://") or fname.startsWith("https://"):
-      when not defined(js):
+      when not (defined(js) or defined(NimScript)):
         return readCsvFromUrl(fname, sep=sep, header=header, skipLines=skipLines,
                               toSkip=toSkip, colNames=colNames)
       else:
@@ -757,7 +756,7 @@ when not defined(js):
     let fname = fname.expandTilde()
     result = newDataFrame()
     try:
-      when not defined(js):
+      when not (defined(js) or defined(NimScript)):
         var ff = memfiles.open(fname)
         var lineCnt = 0
         for slice in memSlices(ff, delim = lineBreak, eat = eat):
@@ -891,7 +890,7 @@ proc toHtml*[C: ColumnLike](df: DataTable[C], tmpl = ""): string =
   body.add "</tbody>"
   result = tmpl % (header & body)

-when not defined(js):
+when not (defined(js) or defined(NimScript)):
   proc showBrowser*[C: ColumnLike](
     df: DataTable[C], fname = "df.html", path = getTempDir(), toRemove = false,
     htmlTmpl = "", title = "") =