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.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?fwiw the binding name symbols are made like:
(def FILE (sci/new-dynamic-var 'FILE nil))all the former bindings are available in the "required" code
it's like the latter sci/binding didn't have any affect
I'll reply tomorrow
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
https://gist.github.com/ingydotnet/27ee4009467feff6023b53f2369cfb4d is a minimal repro of my needs, but here it is working fine afaict.
I'll figure out what I messed up in my real code
Figured it out. I was binding a var that hadn't been added to the sci context.
@borkdude If you agree it is wrong and see an easy fix, can you paste it here?
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)) But now I'm running into the issues there that I was having...
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: fooyou need to first define a context, using (sci/init opts) and then use eval-string* with the context as that argument
ok, I'll try that
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?
Do I need to tack on a (ns orig-ns) to the code I'm eval-ing?
you can use eval-string+ to get back a map with the current ns
and then you can provide the return value back to eval-string+ to be in the same ns as last time
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"Thanks for the help. I'll give you a fixed example for the readme after I get it sorted, if you want...
Thanks!
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 workingI tried 'main where 'clojure.core is above but that caused other issues...
going afk for a while...
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}})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"]}
rathercheers
Can you specify a :file path with sci/eval-string+ ?
not using that function in particular, but you can do this:
(sci/binding [sci/file "foo.clj"]
...)