← Home

SCI - Small Clojure Interpreter

22 August, 2024

The sci is the foundation library for clojure(script) application which allow evaluating from user input or used to provide "Clojure" DSL within the application.

The following are notes we collected while using and exploring how it works.

We can find out which namespaces is available by default inside sci like this:

(cljs.pprint/pprint
  (-> (sci.impl.opts/init {}) ;; <--- Initialize the context
      :env  ;; <--- This is the environment
      deref
      :namespaces
      keys
      (->> (sort-by str) (into []))))
[clojure.core
 clojure.edn
 clojure.repl
 clojure.set
 clojure.string
 clojure.template
 clojure.walk
 sci.impl.deftype
 sci.impl.protocols
 sci.impl.records
 user]

The environment contains below keys:

[:async-load-fn
 :class->opts
 :imports
 :js-libs
 :load-fn
 :namespaces
 :ns-aliases
 :public-class
 :raw-classes]

To allow other clojure(script) libraries to be used from sci eval-string, we must provide the var mappings values for :namespaces option, as shown in the example below.

(sci.core/eval-string
  "(e/->infix (e/square (e/sin (e/+ 'a 3))))"
  {:namespaces {'emmy.env {'+ emmy.env/+
                           '->infix emmy.env/->infix
                           'square emmy.env/square
                           'sin emmy.env/sin}}
   :ns-aliases {'e 'emmy.env}})
;; =>
"sinĀ²(a + 3)"

Some popular library which is known to be used inside sci, e.g. emmy, already provide some means of config to ease the process. Above example can be simplified like below.

(sci.core/eval-string 
  "(e/->infix (e/square (e/sin (e/+ 'a 3))))"
  (merge 
    emmy.sci/config
    {:ns-aliases '{e emmy.env}}))
"sinĀ²(a + 3)"