Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- (ns polydact.core
- (:import (clojure.lang IFn)
- (org.graalvm.polyglot Context Value)
- (org.graalvm.polyglot.proxy ProxyArray ProxyExecutable ProxyObject)))
- (set! *warn-on-reflection* true)
- (comment
- (do
- (def context
- (.build (Context/newBuilder (into-array ["js"]))))
- (defn ^Value eval-js [code]
- (.eval ^Context context "js" code)))
- (.as (eval-js "Number.MAX_VALUE") Object)
- ;=> 1.7976931348623157E308
- (type *1)
- ;=> java.lang.Double
- (.as (eval-js "[{}]") Object)
- ;=> {"0" {}}
- (.as (eval-js "[{}]") java.util.List)
- ;=> ({})
- #_cool!)
- (defn- execute
- [^Value execable & args]
- (.execute execable (object-array args)))
- (declare value->clj)
- (defmacro ^:private reify-ifn
- "Convenience macro for reifying IFn for executable polyglot Values."
- [v]
- (let [invoke-arity
- (fn [n]
- (let [args (map #(symbol (str "arg" (inc %))) (range n))]
- (if (seq args)
- ;; TODO test edge case for final `invoke` arity w/varargs
- `(~'invoke [this# ~@args] (value->clj (execute ~v ~@args)))
- `(~'invoke [this#] (value->clj (execute ~v))))))]
- `(reify IFn
- ~@(map invoke-arity (range 22))
- (~'applyTo [this# args#] (value->clj (apply execute ~v args#))))))
- (macroexpand '(reify-ifn v))
- (defn proxy-fn
- "Returns a ProxyExecutable instance for given function, allowing it to be
- invoked from polyglot contexts."
- [f]
- (reify ProxyExecutable
- (execute [_this args]
- (apply f (map value->clj args)))))
- (defn value->clj
- "Returns a Clojure (or Java) value for given polyglot Value if possible,
- otherwise throws."
- [^Value v]
- (cond
- (.isNull v) nil
- (.isHostObject v) (.asHostObject v)
- (.isBoolean v) (.asBoolean v)
- (.isString v) (.asString v)
- (.isNumber v) (.as v Number)
- (.canExecute v) (reify-ifn v)
- (.hasArrayElements v) (into []
- (for [i (range (.getArraySize v))]
- (value->clj (.getArrayElement v i))))
- (.hasMembers v) (into {}
- (for [k (.getMemberKeys v)]
- [k (value->clj (.getMember v k))]))
- :else (throw (Exception. "Unsupported value"))))
- (comment
- (def js->clj (comp value->clj eval-js))
- (js->clj "[{}]")
- ;=> [{}]
- (js->clj "false")
- ;=> false
- (js->clj "3 / 3.33")
- ;=> 0.9009009009009009
- (js->clj "123123123123123123123123123123123")
- ;=> 1.2312312312312312E32
- (def doubler (js->clj "(n) => {return n * 2;}"))
- (doubler 2)
- ;=> 4
- (js->clj "m = {foo: 1, bar: '2', baz: {0: false}};")
- ;=> {"foo" 1, "bar" "2", "baz" {"0" false}}
- (def factorial
- (eval-js "
- var m = [];
- function factorial (n) {
- if (n == 0 || n == 1) return 1;
- if (m[n] > 0) return m[n];
- return m[n] = factorial(n - 1) * n;
- }
- x = {fn: factorial, memos: m};"))
- ((get (value->clj factorial) "fn") 12)
- ;=> 479001600
- (get (value->clj factorial) "memos")
- ;=> [nil nil 2 6 24 120 720 5040 40320 362880 3628800 39916800 479001600]
- ((get (value->clj factorial) "fn") 24)
- ;=> 6.204484017332394E23
- (get (value->clj factorial) "memos")
- ;=> [nil nil 2 6 24 120 720 5040 40320 362880 3628800 39916800 479001600 ... truncated for brevity]
- (eval-js "var foo = 0xFFFF")
- (eval-js "console.log(foo);")
- ;=> #object[org.graalvm.polyglot.Value 0x3f9d2028 "undefined"]
- ;65535
- (js->clj "1 + '1'")
- ;=> "11"
- (js->clj "['foo', 10, 2].sort()")
- ;=> [10 2 "foo"]
- (def js-aset
- (js->clj "(arr, idx, val) => { arr[idx] = val; return arr; }"))
- (js-aset (ProxyArray/fromArray (object-array [1 2 3])) 1 nil)
- ;=> [1 nil 3]
- (sort [{:b nil} \a 1 "a" "A" #{\a} :foo -1 0 {:a nil} "bar"])
- (def js-sort
- (js->clj "(...vs) => { return vs.sort(); }"))
- (apply js-sort [{:b nil} \a 1 "a" "A" #{\a} :foo -1 0 {:a nil} "bar"])
- ;=> [-1 0 1 "A" #{\a} :foo {:a nil} {:b nil} "a" "a" "bar"]
- (def variadic-fn
- (js->clj "(x, y, ...z) => { return [x, y, z]; }"))
- (apply variadic-fn :foo :bar (range 3))
- ;=> [:foo :bar [0 1 2]]
- (def ->json
- (js->clj "(x) => { return JSON.stringify(x); }"))
- (->json [1 2 3])
- ;=> "[1,2,3]"
- (->json (ProxyObject/fromMap {"foo" 1, "bar" nil}))
- ;=> "{\"foo\":1,\"bar\":null}"
- (def json->
- (js->clj "(x) => { return JSON.parse(x); }"))
- (json-> (->json [1 2 3]))
- ;=> [1 2 3]
- (json-> (->json (ProxyObject/fromMap {"foo" 1})))
- ;=> {"foo" 1}
- (def json-object
- (js->clj "(m) => { return m.foo + m.foo; }"))
- (json-object (ProxyObject/fromMap {"foo" 1}))
- ;=> 2
- (def clj-lambda
- (js->clj "
- m = {foo: [1, 2, 3],
- bar: {
- baz: ['a', 'z']
- }};
- (fn) => { return fn(m); }
- "))
- (clj-lambda
- (proxy-fn #(clojure.walk/prewalk
- (fn [v] (if (and (vector? v)
- (not (map-entry? v)))
- (vec (reverse v))
- v))
- %)))
- ;=> {"foo" [3 2 1], "bar" {"baz" ["z" "a"]}}
- (def js-reduce
- (let [reduce (js->clj "(f, coll) => { return coll.reduce(f); }")
- reduce-init (js->clj "(f, coll, init) => { return coll.reduce(f, init); }")]
- (fn
- ([f coll] (reduce f coll))
- ([f init coll] (reduce-init f coll init)))))
- (js-reduce + (range 10))
- ;=> 45
- (js-reduce + -5.5 (range 10))
- ;=> 39.5
- (js-reduce (fn [acc elem]
- (assoc acc (keyword (str elem)) (doubler elem)))
- {}
- (range 5))
- ;=> {:0 0, :1 2, :2 4, :3 6, :4 8}
- (def log-coll
- (js->clj "(coll) => { for (i in coll) console.log(coll[i]); }"))
- (log-coll (repeatedly 3 #(do (prn 'sleeping)
- (Thread/sleep 100)
- (rand))))
- (log-coll (range))
- "nice")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement