sci

Ingy döt Net 2024-02-18T16:00:47.988789Z

I am struggling to implement a load-file function for ys where it slurps a ys file, compiles ys to clj and then reads/evals in the current sci context. I found this promising example in the SCI readme:

(ns 
    (:require [sci.core :as sci]
              [ :as io]))

(spit "example1.clj" "(defn foo [] :foo)")
(spit "example2.clj" "(load-file \"example1.clj\")")

(let [load-file (fn [file]
                  (let [file (io/file file)
                        source (slurp file)]
                    (sci/with-bindings
                      {sci/ns @sci/ns
                       sci/file (.getAbsolutePath file)}
                      (sci/eval-string source opts))))
      opts {:namespaces {'clojure.core {'load-file load-file}}}]
  (sci/eval-string "(load-file \"example2.clj\") (foo)" opts))
but I believe it has an error in (sci/eval-string source opts) since opts appears to not be defined yet.

Ingy döt Net 2024-02-19T20:31:37.212109Z

just getting to what you last said. my process starts with something like:

(sci/binding
        [sci/file file
         ARGV args
         CWD (str (babashka.fs/cwd))
         ENV (into {} (System/getenv))
         FILE file
         VERSION ys-version]
         (sci/eval-string* @ys/sci-ctx clj-code-string))
and later in a "require" implementation I do something like:
ret (sci/binding
             [FILE ys-file
              XYZZY 1234]
              (sci/eval-string+ @sci-ctx clj-code-string))
but I don't get the latter bindings. Am I calling that right?

Ingy döt Net 2024-02-19T20:34:12.181629Z

fwiw the binding name symbols are made like:

(def FILE (sci/new-dynamic-var 'FILE nil))

Ingy döt Net 2024-02-19T20:35:22.025279Z

all the former bindings are available in the "required" code

Ingy döt Net 2024-02-19T20:36:07.321189Z

it's like the latter sci/binding didn't have any affect

borkdude 2024-02-19T20:42:24.391759Z

I'll reply tomorrow

borkdude 2024-02-19T21:08:17.843129Z

btw it depends on what code you're calling and if the result is lazy so if you can make a more self-contained small(est) repro (without a large project) that would be helpful to nail things down. back tomorrow

✅ 1
Ingy döt Net 2024-02-20T00:45:00.396379Z

https://gist.github.com/ingydotnet/27ee4009467feff6023b53f2369cfb4d is a minimal repro of my needs, but here it is working fine afaict.

Ingy döt Net 2024-02-20T00:45:26.832839Z

I'll figure out what I messed up in my real code

Ingy döt Net 2024-02-20T02:47:19.097519Z

Figured it out. I was binding a var that hadn't been added to the sci context.

👍 1
Ingy döt Net 2024-02-18T16:02:02.516829Z

@borkdude If you agree it is wrong and see an easy fix, can you paste it here?

Ingy döt Net 2024-02-18T16:53:58.907909Z

I got this to work...

(ns 
    (:require [sci.core :as sci]
              [ :as io]))

; (spit "example1.clj" "(defn foo [] :foo)")
(spit "example1.clj" "(println \"example1.clj\")")

(spit "example2.clj" "(load-file \"example1.clj\")")

(let [opts (atom {})
      load-file (fn [file]
                  (let [file (io/file file)
                        source (slurp file)]
                    (sci/with-bindings
                      {sci/ns @sci/ns
                       sci/file (.getAbsolutePath file)}
                      (sci/eval-string source @opts))))
      namespaces {:namespaces
                  {'clojure.core
                   {'load-file load-file
                    'println clojure.core/println}}}]

  (reset! opts namespaces)

;  (sci/eval-string "(load-file \"example2.clj\") (foo)" @opts))

  (sci/eval-string "(load-file \"example2.clj\")" @opts))

Ingy döt Net 2024-02-18T16:55:12.453479Z

But now I'm running into the issues there that I was having...

Ingy döt Net 2024-02-18T16:56:17.066749Z

If i use the original commented-out lines I get:

Execution error (ExceptionInfo) at sci.impl.utils/throw-error-with-location (utils.cljc:41).
Could not resolve symbol: foo

borkdude 2024-02-18T16:57:10.887249Z

you need to first define a context, using (sci/init opts) and then use eval-string* with the context as that argument

Ingy döt Net 2024-02-18T16:57:41.914889Z

ok, I'll try that

Ingy döt Net 2024-02-18T17:00:35.571609Z

I have one other thing in addition... My analog of your example1.clj is (ns xyz) (defn foo [] :foo) How would I eval-string* that and return to the calling ns?

Ingy döt Net 2024-02-18T17:02:00.059689Z

Do I need to tack on a (ns orig-ns) to the code I'm eval-ing?

borkdude 2024-02-18T17:03:51.030969Z

you can use eval-string+ to get back a map with the current ns

borkdude 2024-02-18T17:04:17.872329Z

and then you can provide the return value back to eval-string+ to be in the same ns as last time

Ingy döt Net 2024-02-18T17:15:44.775539Z

Thought that eval-string+ was a typo for a sec. Not in the readme, but found it 🙂

(defn eval-string+
  "Evaluates string `s` in the context of `ctx` (as produced with
  `init`).

  Options:
  *`:ns` - the namespace to start evaluation in (defaults to the value of `sci/ns`)

  Returns map with:
  * `:val` - the evaluated value
  * `:ns` - the namespace object"

Ingy döt Net 2024-02-18T17:16:29.186189Z

Thanks for the help. I'll give you a fixed example for the readme after I get it sorted, if you want...

👍 1
borkdude 2024-02-18T17:25:00.273619Z

Thanks!

👍 1
Ingy döt Net 2024-02-18T18:38:34.751699Z

I think I got it all sorted. One small question. My default ns is user but I never said to use that. My context starts like;

(sci/init
    {:namespaces
     {'clojure.core (clojure-core-ns)
I want my default ns to be called main. Can't get that working

Ingy döt Net 2024-02-18T18:39:33.661479Z

I tried 'main where 'clojure.core is above but that caused other issues...

Ingy döt Net 2024-02-18T18:42:10.905109Z

going afk for a while...

borkdude 2024-02-18T18:53:03.616759Z

user is always the default ns in Clojure. you can change with

(sci/binding [sci/ns (sci/create-ns 'main)]
  ...)
around your calls or:
(sci/eval-string+ ctx "string" {:ns (sci/create-ns 'main}})

borkdude 2024-02-18T19:00:57.838239Z

user=> (def ctx (sci/init {:namespaces {'main {}}}))
#'user/ctx
user=> (sci/eval-string+ ctx "(def x 1)" {:ns (sci/create-ns 'main)})
{:val #'main/x, :ns #object[sci.lang.Namespace 0x7a57c5d9 "main"]}
rather

Ingy döt Net 2024-02-18T20:02:29.312489Z

cheers

Ingy döt Net 2024-02-18T20:52:36.826679Z

Can you specify a :file path with sci/eval-string+ ?

borkdude 2024-02-18T21:01:10.838659Z

not using that function in particular, but you can do this:

(sci/binding [sci/file "foo.clj"]
  ...)

👍 1