Created
October 21, 2024 14:19
-
-
Save oakmac/02122ae20206fe4fcc208e0ff8c35700 to your computer and use it in GitHub Desktop.
Standard Clojure Style v0.10.0 on Wolframite
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/wolframite/api/v1.clj b/src/wolframite/api/v1.clj | |
index 1ba746a..f8c2754 100644 | |
--- a/src/wolframite/api/v1.clj | |
+++ b/src/wolframite/api/v1.clj | |
@@ -1,7 +1,8 @@ | |
(ns wolframite.api.v1 | |
(:refer-clojure :exclude [eval]) | |
- (:require [wolframite.core :as core] | |
- [wolframite.lib.helpers :as helper])) | |
+ (:require | |
+ [wolframite.core :as core] | |
+ [wolframite.lib.helpers :as helper])) | |
(def start! | |
"Initialize Wolframite and start! the underlying Wolfram Kernel - required once before you make any eval calls. | |
@@ -88,5 +89,4 @@ | |
(comment | |
(start!) | |
(! '(+ 1 1)) | |
- (help! '(+ 1 1)) ;; => Doesn't seem to be opening a browser for me? | |
- ) | |
+ (help! '(+ 1 1))) ;; => Doesn't seem to be opening a browser for me? | |
diff --git a/src/wolframite/base/cep.clj b/src/wolframite/base/cep.clj | |
index 0877dcb..97950fa 100644 | |
--- a/src/wolframite/base/cep.clj | |
+++ b/src/wolframite/base/cep.clj | |
@@ -1,24 +1,24 @@ | |
-(ns wolframite.base.cep | |
- (:require | |
- [wolframite.lib.options :as options] | |
- [wolframite.base.convert :as convert] | |
- [wolframite.base.evaluate :as evaluate] | |
- [wolframite.base.parse :as parse])) | |
- | |
-(defn- identity-first [x & _] x) | |
- | |
-(defn cep | |
- "Convert-Evaluate-Parse pipeline. | |
- Convert: from clj data to jlink Expr | |
- Evaluate: the Expr on (some) Wolfram Engine | |
- Parse: returned result into clj data. | |
- Each stage can be skipped with appropriate `opts` `:flag` e.g. `:no-parse`" | |
- [expr {:keys [flags] | |
- :as opts}] | |
- (let [convert (if (options/flag?' flags :convert) convert/convert identity-first) | |
- evaluate (if (options/flag?' flags :evaluate) evaluate/evaluate identity-first) | |
- parse (if (options/flag?' flags :parse) parse/parse identity-first)] | |
- (-> expr | |
- (convert opts) | |
- (evaluate opts) | |
- (parse opts)))) | |
+(ns wolframite.base.cep | |
+ (:require | |
+ [wolframite.base.convert :as convert] | |
+ [wolframite.base.evaluate :as evaluate] | |
+ [wolframite.base.parse :as parse] | |
+ [wolframite.lib.options :as options])) | |
+ | |
+(defn- identity-first [x & _] x) | |
+ | |
+(defn cep | |
+ "Convert-Evaluate-Parse pipeline. | |
+ Convert: from clj data to jlink Expr | |
+ Evaluate: the Expr on (some) Wolfram Engine | |
+ Parse: returned result into clj data. | |
+ Each stage can be skipped with appropriate `opts` `:flag` e.g. `:no-parse`" | |
+ [expr {:keys [flags] | |
+ :as opts}] | |
+ (let [convert (if (options/flag?' flags :convert) convert/convert identity-first) | |
+ evaluate (if (options/flag?' flags :evaluate) evaluate/evaluate identity-first) | |
+ parse (if (options/flag?' flags :parse) parse/parse identity-first)] | |
+ (-> expr | |
+ (convert opts) | |
+ (evaluate opts) | |
+ (parse opts)))) | |
diff --git a/src/wolframite/base/convert.clj b/src/wolframite/base/convert.clj | |
index 529eb25..70b117d 100644 | |
--- a/src/wolframite/base/convert.clj | |
+++ b/src/wolframite/base/convert.clj | |
@@ -1,178 +1,179 @@ | |
-(ns wolframite.base.convert | |
- "Convert a Clojure expression into a Wolfram JLink expression" | |
- (:require [wolframite.impl.jlink-instance :as jlink-instance] | |
- [wolframite.impl.protocols :as proto] | |
- [wolframite.impl.wolfram-syms.intern :as intern] | |
- [wolframite.lib.options :as options] | |
- [wolframite.base.express :as express] | |
- [wolframite.base.expr :as expr] | |
- [wolframite.runtime.defaults :as defaults])) | |
- | |
-;; (remove-ns 'wolframite.base.convert) | |
- | |
-;; * defmulti and dispatch | |
- | |
-(defn- primitive? | |
- "Is `x` a 'primitive' value that can be directly turned into an Expr?" | |
- [x] | |
- (or (number? x) | |
- (string? x))) | |
- | |
-(defn- supported-primitive-array? [xs] | |
- ;; See jlink.Expr constructor for the types actually supported | |
- (some-> xs .getClass .componentType (#{Byte/TYPE Double/TYPE Integer/TYPE Long/TYPE Short/TYPE}))) | |
- | |
-(defn- dispatch [obj] | |
- (cond (and (list? obj) | |
- (empty? obj)) :null | |
- (seq? obj) :expr | |
- (or (vector? obj) | |
- (list? obj)) :list | |
- (ratio? obj) :rational | |
- (primitive? obj) :primitive | |
- (map? obj) :hash-map | |
- (symbol? obj) :symbol | |
- (nil? obj) :null | |
- (fn? obj) :fn-obj | |
- :else nil)) | |
- | |
-(defmulti convert | |
- "Convert a Wolframite clj expression into a JLink object representation" | |
- (fn [clj-expr _opts] (dispatch clj-expr))) ; TODO Pass jlink-instance in explicitly inst. of fetching from the global | |
- | |
-;; * Helpers | |
- | |
-(defn- simple-vector? [coll _] | |
- (and (sequential? coll) | |
- (not-any? dispatch coll))) | |
- | |
-(defn- simple-matrix? [coll opts] | |
- (and (sequential? coll) | |
- (every? #(simple-vector? % opts) coll))) | |
- | |
-(defn- cexpr-from-postfix-form [cexprs] | |
- (assert (sequential? cexprs)) | |
- (loop [cexpr (first cexprs) | |
- remaining (rest cexprs)] | |
- (if (seq remaining) | |
- (recur (list (first remaining) cexpr) (rest remaining)) | |
- cexpr))) | |
- | |
-(defn- cexpr-from-prefix-form [cexprs] | |
- (assert (sequential? cexprs)) | |
- (cexpr-from-postfix-form (reverse cexprs))) | |
- | |
-;; * Method impls | |
- | |
-(defmethod convert nil [obj _] | |
- ;; A fall-back implementation, for anything we do not handle directly ourselves elsewhere here. | |
- ;; Also triggered for any other unknown/unhandled type, e.g. a :kwd | |
- (cond | |
- (proto/expr? (jlink-instance/get) obj) | |
- obj ; already a jlink Expr | |
- | |
- (supported-primitive-array? obj) | |
- (proto/expr (jlink-instance/get) obj) | |
- | |
- :else | |
- (proto/->expr (jlink-instance/get) obj))) | |
- | |
-(defmethod convert :fn-obj [obj opts] | |
- ;; This normally means that the expression contained a reference to a var in wolframite.wolfram, | |
- ;; which has not been turned into a symbol for some reason, typically b/c it was not a fn call | |
- ;; (those do 'symbolify' themselves) => we check and do this here | |
- (if-let [fn-name (intern/interned-var-val->symbol obj)] | |
- (convert fn-name opts) | |
- (throw (IllegalArgumentException. | |
- (str "An expression contains a function object, which is not intern/wolfram-fn => " | |
- "don't know how to turn it into a symbol that Wolfram could interpret: " | |
- obj))))) | |
- | |
-(defmethod convert :null [_ opts] | |
- (convert 'Null opts)) | |
- | |
-(defmethod convert :rational [n opts] | |
- (convert (list 'Rational (.numerator n) (.denominator n)) opts)) | |
- | |
-(defmethod convert :primitive [primitive _opts] | |
- (proto/expr (jlink-instance/get) primitive)) | |
- | |
-(defn ->wolfram-str-expr | |
- "Turn `str` (a raw Wolfram expression string) into a value that `convert` will | |
- properly process as an expression, and not as a primitive string." | |
- [str] | |
- {::wolfram-str-expr str}) | |
- | |
-(defn- wolfram-str-expr->jlink-expr [wolfram-str-expr-map opts] | |
- (do (assert (= 1 (count wolfram-str-expr-map)) "::wolfram-str-expr must stand on its own") | |
- (assert (-> wolfram-str-expr-map vals first string?) "::wolfram-str-expr value must be a string") | |
- (express/express (::wolfram-str-expr wolfram-str-expr-map) opts))) | |
- | |
-(defmethod convert :hash-map [map opts] | |
- ;; A map could be either normal map value the user supplied, or it could be our magical | |
- ;; map used to mark the actual value for special treatment, namely to be interpreted as | |
- ;; Wolfram code in a string; see `->wolfram-str-expr` | |
- (if (::wolfram-str-expr map) | |
- (wolfram-str-expr->jlink-expr map opts) | |
- (convert (apply list 'Association (for [[key value] map] (list 'Rule key value))) opts))) | |
- | |
-(defmethod convert :symbol [sym {:keys [aliases] ::keys [args] :as opts}] | |
- (let [all-aliases (merge defaults/all-aliases aliases)] | |
- (if-let [alias-sym-or-fn (all-aliases sym)] | |
- (if (defaults/experimental-fn-alias? alias-sym-or-fn) | |
- (convert (alias-sym-or-fn args) opts) | |
- (convert alias-sym-or-fn opts)) | |
- ;; Numbered args of shorthand lambdas - Clojure's `#(+ %1 %2)` => Wolfs #1 and #1 = Slot[1] and Slot[2] | |
- (if-let [[_ ^String n] (re-matches #"%(\d*)" (str sym))] | |
- (let [n (Long/valueOf (if (= "" n) "1" n))] | |
- (convert (list 'Slot n) opts)) | |
- ;(let [s (str-utils/replace (str sym) #"\|(.*?)\|" #(str "\\\\[" (second %) "]"))] ) | |
- (let [s (str sym)] | |
- (if (re-find #"[^a-zA-Z0-9$\/]" s) | |
- (throw (ex-info (str "Unsupported symbol / unknown alias: Symbols passed to Mathematica must be alphanumeric" | |
- " (apart from forward slashes and dollar signs). Other symbols may" | |
- " only be used if there is defined a Wolframite alias for them." | |
- " Passed: " s | |
- " Known aliases: " (or (-> aliases keys sort seq) "N/A")) | |
- {:unknown-symbol s | |
- :known-symbols (keys aliases)})) | |
- (proto/expr (jlink-instance/get) :Expr/SYMBOL s))))))) | |
- | |
-(defn- convert-non-simple-list [elms opts] | |
- (let [converted-parts (map #(cond-> % (dispatch %) (convert opts)) elms)] | |
- (if (every? (partial proto/expr? (jlink-instance/get)) converted-parts) | |
- (proto/expr (jlink-instance/get) | |
- (cons (convert 'List opts) | |
- converted-parts)) | |
- (convert (to-array converted-parts) opts)))) | |
- | |
-(defmethod convert :list [coll opts] | |
- (cond (simple-matrix? coll opts) (convert (to-array-2d coll) opts) | |
- (simple-vector? coll opts) (convert (to-array coll) opts) | |
- :else (convert-non-simple-list coll opts))) | |
- | |
-(defmethod convert :expr [[head & tail :as clj-expr] opts] | |
- (let [macro head | |
- arg (first tail)] | |
- (cond (= 'clojure.core/deref macro) (convert (cexpr-from-prefix-form arg) opts) | |
- (= 'clojure.core/meta macro) (convert (cexpr-from-postfix-form arg) opts) | |
- (= 'var macro) (convert (list 'Function arg) opts) | |
- ;; convert '(whatever...) | |
- ;; Quoted symbol intended to be sent as Wolfram symbol | |
- (and (= 'quote macro) | |
- (symbol? arg)) (convert arg opts) | |
- (= 'quote macro) (throw (ex-info (str "Unsupported quoted expression:" | |
- (pr-str clj-expr)) | |
- {:expr clj-expr})) | |
- ;; Originally we called `(express/express arg opts)` but it fails b/c it only handles strings | |
- :else (expr/expr-from-parts | |
- (cons (convert head | |
- (cond-> opts | |
- (symbol? head) | |
- (assoc ::args tail))) | |
- (doall (map #(convert % opts) tail))))))) | |
- | |
-(comment | |
- (convert '(whatever 1) nil) | |
- (convert '(- 12 1 2) {}) | |
- (convert '(Plus 1 2) {})) | |
+(ns wolframite.base.convert | |
+ "Convert a Clojure expression into a Wolfram JLink expression" | |
+ (:require | |
+ [wolframite.base.expr :as expr] | |
+ [wolframite.base.express :as express] | |
+ [wolframite.impl.jlink-instance :as jlink-instance] | |
+ [wolframite.impl.protocols :as proto] | |
+ [wolframite.impl.wolfram-syms.intern :as intern] | |
+ [wolframite.lib.options :as options] | |
+ [wolframite.runtime.defaults :as defaults])) | |
+ | |
+;; (remove-ns 'wolframite.base.convert) | |
+ | |
+;; * defmulti and dispatch | |
+ | |
+(defn- primitive? | |
+ "Is `x` a 'primitive' value that can be directly turned into an Expr?" | |
+ [x] | |
+ (or (number? x) | |
+ (string? x))) | |
+ | |
+(defn- supported-primitive-array? [xs] | |
+ ;; See jlink.Expr constructor for the types actually supported | |
+ (some-> xs .getClass .componentType (#{Byte/TYPE Double/TYPE Integer/TYPE Long/TYPE Short/TYPE}))) | |
+ | |
+(defn- dispatch [obj] | |
+ (cond (and (list? obj) | |
+ (empty? obj)) :null | |
+ (seq? obj) :expr | |
+ (or (vector? obj) | |
+ (list? obj)) :list | |
+ (ratio? obj) :rational | |
+ (primitive? obj) :primitive | |
+ (map? obj) :hash-map | |
+ (symbol? obj) :symbol | |
+ (nil? obj) :null | |
+ (fn? obj) :fn-obj | |
+ :else nil)) | |
+ | |
+(defmulti convert | |
+ "Convert a Wolframite clj expression into a JLink object representation" | |
+ (fn [clj-expr _opts] (dispatch clj-expr))) ; TODO Pass jlink-instance in explicitly inst. of fetching from the global | |
+ | |
+;; * Helpers | |
+ | |
+(defn- simple-vector? [coll _] | |
+ (and (sequential? coll) | |
+ (not-any? dispatch coll))) | |
+ | |
+(defn- simple-matrix? [coll opts] | |
+ (and (sequential? coll) | |
+ (every? #(simple-vector? % opts) coll))) | |
+ | |
+(defn- cexpr-from-postfix-form [cexprs] | |
+ (assert (sequential? cexprs)) | |
+ (loop [cexpr (first cexprs) | |
+ remaining (rest cexprs)] | |
+ (if (seq remaining) | |
+ (recur (list (first remaining) cexpr) (rest remaining)) | |
+ cexpr))) | |
+ | |
+(defn- cexpr-from-prefix-form [cexprs] | |
+ (assert (sequential? cexprs)) | |
+ (cexpr-from-postfix-form (reverse cexprs))) | |
+ | |
+;; * Method impls | |
+ | |
+(defmethod convert nil [obj _] | |
+ ;; A fall-back implementation, for anything we do not handle directly ourselves elsewhere here. | |
+ ;; Also triggered for any other unknown/unhandled type, e.g. a :kwd | |
+ (cond | |
+ (proto/expr? (jlink-instance/get) obj) | |
+ obj ; already a jlink Expr | |
+ | |
+ (supported-primitive-array? obj) | |
+ (proto/expr (jlink-instance/get) obj) | |
+ | |
+ :else | |
+ (proto/->expr (jlink-instance/get) obj))) | |
+ | |
+(defmethod convert :fn-obj [obj opts] | |
+ ;; This normally means that the expression contained a reference to a var in wolframite.wolfram, | |
+ ;; which has not been turned into a symbol for some reason, typically b/c it was not a fn call | |
+ ;; (those do 'symbolify' themselves) => we check and do this here | |
+ (if-let [fn-name (intern/interned-var-val->symbol obj)] | |
+ (convert fn-name opts) | |
+ (throw (IllegalArgumentException. | |
+ (str "An expression contains a function object, which is not intern/wolfram-fn => " | |
+ "don't know how to turn it into a symbol that Wolfram could interpret: " | |
+ obj))))) | |
+ | |
+(defmethod convert :null [_ opts] | |
+ (convert 'Null opts)) | |
+ | |
+(defmethod convert :rational [n opts] | |
+ (convert (list 'Rational (.numerator n) (.denominator n)) opts)) | |
+ | |
+(defmethod convert :primitive [primitive _opts] | |
+ (proto/expr (jlink-instance/get) primitive)) | |
+ | |
+(defn ->wolfram-str-expr | |
+ "Turn `str` (a raw Wolfram expression string) into a value that `convert` will | |
+ properly process as an expression, and not as a primitive string." | |
+ [str] | |
+ {::wolfram-str-expr str}) | |
+ | |
+(defn- wolfram-str-expr->jlink-expr [wolfram-str-expr-map opts] | |
+ (do (assert (= 1 (count wolfram-str-expr-map)) "::wolfram-str-expr must stand on its own") | |
+ (assert (-> wolfram-str-expr-map vals first string?) "::wolfram-str-expr value must be a string") | |
+ (express/express (::wolfram-str-expr wolfram-str-expr-map) opts))) | |
+ | |
+(defmethod convert :hash-map [map opts] | |
+ ;; A map could be either normal map value the user supplied, or it could be our magical | |
+ ;; map used to mark the actual value for special treatment, namely to be interpreted as | |
+ ;; Wolfram code in a string; see `->wolfram-str-expr` | |
+ (if (::wolfram-str-expr map) | |
+ (wolfram-str-expr->jlink-expr map opts) | |
+ (convert (apply list 'Association (for [[key value] map] (list 'Rule key value))) opts))) | |
+ | |
+(defmethod convert :symbol [sym {:keys [aliases] ::keys [args] :as opts}] | |
+ (let [all-aliases (merge defaults/all-aliases aliases)] | |
+ (if-let [alias-sym-or-fn (all-aliases sym)] | |
+ (if (defaults/experimental-fn-alias? alias-sym-or-fn) | |
+ (convert (alias-sym-or-fn args) opts) | |
+ (convert alias-sym-or-fn opts)) | |
+ ;; Numbered args of shorthand lambdas - Clojure's `#(+ %1 %2)` => Wolfs #1 and #1 = Slot[1] and Slot[2] | |
+ (if-let [[_ ^String n] (re-matches #"%(\d*)" (str sym))] | |
+ (let [n (Long/valueOf (if (= "" n) "1" n))] | |
+ (convert (list 'Slot n) opts)) | |
+ ; (let [s (str-utils/replace (str sym) #"\|(.*?)\|" #(str "\\\\[" (second %) "]"))] ) | |
+ (let [s (str sym)] | |
+ (if (re-find #"[^a-zA-Z0-9$\/]" s) | |
+ (throw (ex-info (str "Unsupported symbol / unknown alias: Symbols passed to Mathematica must be alphanumeric" | |
+ " (apart from forward slashes and dollar signs). Other symbols may" | |
+ " only be used if there is defined a Wolframite alias for them." | |
+ " Passed: " s | |
+ " Known aliases: " (or (-> aliases keys sort seq) "N/A")) | |
+ {:unknown-symbol s | |
+ :known-symbols (keys aliases)})) | |
+ (proto/expr (jlink-instance/get) :Expr/SYMBOL s))))))) | |
+ | |
+(defn- convert-non-simple-list [elms opts] | |
+ (let [converted-parts (map #(cond-> % (dispatch %) (convert opts)) elms)] | |
+ (if (every? (partial proto/expr? (jlink-instance/get)) converted-parts) | |
+ (proto/expr (jlink-instance/get) | |
+ (cons (convert 'List opts) | |
+ converted-parts)) | |
+ (convert (to-array converted-parts) opts)))) | |
+ | |
+(defmethod convert :list [coll opts] | |
+ (cond (simple-matrix? coll opts) (convert (to-array-2d coll) opts) | |
+ (simple-vector? coll opts) (convert (to-array coll) opts) | |
+ :else (convert-non-simple-list coll opts))) | |
+ | |
+(defmethod convert :expr [[head & tail :as clj-expr] opts] | |
+ (let [macro head | |
+ arg (first tail)] | |
+ (cond (= 'clojure.core/deref macro) (convert (cexpr-from-prefix-form arg) opts) | |
+ (= 'clojure.core/meta macro) (convert (cexpr-from-postfix-form arg) opts) | |
+ (= 'var macro) (convert (list 'Function arg) opts) | |
+ ;; convert '(whatever...) | |
+ ;; Quoted symbol intended to be sent as Wolfram symbol | |
+ (and (= 'quote macro) | |
+ (symbol? arg)) (convert arg opts) | |
+ (= 'quote macro) (throw (ex-info (str "Unsupported quoted expression:" | |
+ (pr-str clj-expr)) | |
+ {:expr clj-expr})) | |
+ ;; Originally we called `(express/express arg opts)` but it fails b/c it only handles strings | |
+ :else (expr/expr-from-parts | |
+ (cons (convert head | |
+ (cond-> opts | |
+ (symbol? head) | |
+ (assoc ::args tail))) | |
+ (doall (map #(convert % opts) tail))))))) | |
+ | |
+(comment | |
+ (convert '(whatever 1) nil) | |
+ (convert '(- 12 1 2) {}) | |
+ (convert '(Plus 1 2) {})) | |
diff --git a/src/wolframite/base/evaluate.clj b/src/wolframite/base/evaluate.clj | |
index 9d75a4b..4cd3fee 100644 | |
--- a/src/wolframite/base/evaluate.clj | |
+++ b/src/wolframite/base/evaluate.clj | |
@@ -1,58 +1,59 @@ | |
-(ns wolframite.base.evaluate | |
- "The core of evaluation: send a converted JLink expression to a Wolfram Kernel for evaluation and return the result." | |
- (:require [wolframite.impl.protocols :as proto] | |
- [wolframite.lib.options :as options] | |
- [wolframite.base.convert :as convert])) | |
- | |
-(declare evaluate) | |
- | |
-(defn process-state [pid-expr {:keys [flags] :as opts}] | |
- (assert (options/flag?' flags :serial)) | |
- (let [state-expr (evaluate (convert/convert (list 'ProcessState pid-expr) opts) opts) | |
- state-prefix (first (.toString state-expr))] | |
- (cond (= \r state-prefix) [:running nil] | |
- (= \f state-prefix) [:finished (.part state-expr 1)] | |
- (= \q state-prefix) [:queued nil] | |
- :else | |
- (throw (Exception. (str "Error! State unrecognized: " state-expr)))))) | |
- | |
-(defn queue-run-or-wait [{:keys [flags config] :as opts}] | |
- (assert (options/flag?' flags :serial)) | |
- (let [lqr-atom (atom nil) | |
- lqr-time @lqr-atom | |
- nano-pi (* 1000000 (:poll-interval config)) | |
- run-in (when lqr-time (- (+ lqr-time nano-pi) (System/nanoTime)))] | |
- (if (or (nil? run-in) (neg? run-in)) | |
- (do | |
- ;; TODO: add debug logging "QueueRunning at time" | |
- (evaluate (convert/convert '(QueueRun) opts) opts) | |
- (swap! lqr-atom (fn [_] (System/nanoTime)))) | |
- ;; TODO: else branch: add debug logging "Sleeping for" | |
- (Thread/sleep ^long (quot run-in 1000000))))) | |
- | |
-(defn evaluate [expr {:keys [jlink-instance] | |
- :as opts}] | |
- {:pre [jlink-instance]} | |
- (assert (proto/expr? jlink-instance expr)) | |
- | |
- (if (options/flag?' (:flags opts) :serial) | |
- (proto/evaluate! jlink-instance expr) | |
- (let [opts' (update opts :flags conj :serial) ;; FIXME: make sure this is supposed to be `:serial`, it's what I gather from previous version of the code | |
- ;; Generate a new, unique symbol for a ref to the submitted computation (~ Java Future?) | |
- pid-expr (evaluate (convert/convert | |
- (list 'Unique | |
- ; Beware: technically, this is an invalid clj symbol due to the slashes: | |
- (symbol "Wolframite/Concurrent/process")) opts') | |
- opts)] | |
- ;; Submit the expr for evaluation on the next available parallel kernel | |
- (evaluate (convert/convert (list '= pid-expr (list 'ParallelSubmit expr)) opts') opts) | |
- (evaluate (convert/convert '(QueueRun) opts') opts) | |
- (loop [] | |
- (let [[state result] (process-state pid-expr opts)] | |
- (if (not= :finished state) | |
- (do | |
- (queue-run-or-wait opts) | |
- (recur)) | |
- (do | |
- (evaluate (convert/convert (list 'Remove pid-expr) opts') opts) | |
- result))))))) | |
+(ns wolframite.base.evaluate | |
+ "The core of evaluation: send a converted JLink expression to a Wolfram Kernel for evaluation and return the result." | |
+ (:require | |
+ [wolframite.base.convert :as convert] | |
+ [wolframite.impl.protocols :as proto] | |
+ [wolframite.lib.options :as options])) | |
+ | |
+(declare evaluate) | |
+ | |
+(defn process-state [pid-expr {:keys [flags] :as opts}] | |
+ (assert (options/flag?' flags :serial)) | |
+ (let [state-expr (evaluate (convert/convert (list 'ProcessState pid-expr) opts) opts) | |
+ state-prefix (first (.toString state-expr))] | |
+ (cond (= \r state-prefix) [:running nil] | |
+ (= \f state-prefix) [:finished (.part state-expr 1)] | |
+ (= \q state-prefix) [:queued nil] | |
+ :else | |
+ (throw (Exception. (str "Error! State unrecognized: " state-expr)))))) | |
+ | |
+(defn queue-run-or-wait [{:keys [flags config] :as opts}] | |
+ (assert (options/flag?' flags :serial)) | |
+ (let [lqr-atom (atom nil) | |
+ lqr-time @lqr-atom | |
+ nano-pi (* 1000000 (:poll-interval config)) | |
+ run-in (when lqr-time (- (+ lqr-time nano-pi) (System/nanoTime)))] | |
+ (if (or (nil? run-in) (neg? run-in)) | |
+ (do | |
+ ;; TODO: add debug logging "QueueRunning at time" | |
+ (evaluate (convert/convert '(QueueRun) opts) opts) | |
+ (swap! lqr-atom (fn [_] (System/nanoTime)))) | |
+ ;; TODO: else branch: add debug logging "Sleeping for" | |
+ (Thread/sleep ^long (quot run-in 1000000))))) | |
+ | |
+(defn evaluate [expr {:keys [jlink-instance] | |
+ :as opts}] | |
+ {:pre [jlink-instance]} | |
+ (assert (proto/expr? jlink-instance expr)) | |
+ | |
+ (if (options/flag?' (:flags opts) :serial) | |
+ (proto/evaluate! jlink-instance expr) | |
+ (let [opts' (update opts :flags conj :serial) ;; FIXME: make sure this is supposed to be `:serial`, it's what I gather from previous version of the code | |
+ ;; Generate a new, unique symbol for a ref to the submitted computation (~ Java Future?) | |
+ pid-expr (evaluate (convert/convert | |
+ (list 'Unique | |
+ ; Beware: technically, this is an invalid clj symbol due to the slashes: | |
+ (symbol "Wolframite/Concurrent/process")) opts') | |
+ opts)] | |
+ ;; Submit the expr for evaluation on the next available parallel kernel | |
+ (evaluate (convert/convert (list '= pid-expr (list 'ParallelSubmit expr)) opts') opts) | |
+ (evaluate (convert/convert '(QueueRun) opts') opts) | |
+ (loop [] | |
+ (let [[state result] (process-state pid-expr opts)] | |
+ (if (not= :finished state) | |
+ (do | |
+ (queue-run-or-wait opts) | |
+ (recur)) | |
+ (do | |
+ (evaluate (convert/convert (list 'Remove pid-expr) opts') opts) | |
+ result))))))) | |
diff --git a/src/wolframite/base/expr.clj b/src/wolframite/base/expr.clj | |
index 4e0badf..3a33916 100644 | |
--- a/src/wolframite/base/expr.clj | |
+++ b/src/wolframite/base/expr.clj | |
@@ -1,17 +1,16 @@ | |
-(ns wolframite.base.expr | |
- (:require [wolframite.impl.jlink-instance :as jlink-instance] | |
- [wolframite.impl.protocols :as proto])) | |
- | |
-(defn head-str [expr] | |
- (assert (proto/expr? (jlink-instance/get) expr)) | |
- (.toString (.head expr))) | |
- | |
-(defn parts [expr] | |
- (assert (proto/expr? (jlink-instance/get) expr)) | |
- (cons (.head expr) (seq (.args expr)))) | |
- | |
-(defn expr-from-parts [expr-coll] | |
- (assert (every? #(proto/expr? (jlink-instance/get) %) expr-coll)) | |
- (proto/expr (jlink-instance/get) expr-coll)) | |
- | |
- | |
+(ns wolframite.base.expr | |
+ (:require | |
+ [wolframite.impl.jlink-instance :as jlink-instance] | |
+ [wolframite.impl.protocols :as proto])) | |
+ | |
+(defn head-str [expr] | |
+ (assert (proto/expr? (jlink-instance/get) expr)) | |
+ (.toString (.head expr))) | |
+ | |
+(defn parts [expr] | |
+ (assert (proto/expr? (jlink-instance/get) expr)) | |
+ (cons (.head expr) (seq (.args expr)))) | |
+ | |
+(defn expr-from-parts [expr-coll] | |
+ (assert (every? #(proto/expr? (jlink-instance/get) %) expr-coll)) | |
+ (proto/expr (jlink-instance/get) expr-coll)) | |
diff --git a/src/wolframite/base/express.clj b/src/wolframite/base/express.clj | |
index 39d6218..7fde297 100644 | |
--- a/src/wolframite/base/express.clj | |
+++ b/src/wolframite/base/express.clj | |
@@ -1,24 +1,25 @@ | |
-(ns wolframite.base.express | |
- (:require [wolframite.impl.protocols :as proto])) | |
- | |
-(defn express | |
- "??? Convert a _string_ expression into JLink.Expr using Wolfram itself" | |
- [s {:keys [jlink-instance]}] | |
- {:pre [jlink-instance]} | |
- (assert (string? s) (str "Expected '" (pr-str s) "' to be a string but is " (type s))) | |
- ;; TODO: debug log: "express string>" | |
- (let [held-s (str "HoldComplete[" s "]") | |
- link (or (proto/kernel-link jlink-instance) | |
- (throw (IllegalStateException. | |
- (str "Something is wrong - there is no JLink instance, which shouldn't" | |
- " be possible. Try to stop and start Wolframite again.")))) | |
- output (io! | |
- (locking link | |
- (doto link | |
- (.evaluate held-s) | |
- (.waitForAnswer)) | |
- (.. link getExpr args)))] ; TODO (jakub) get rid of reflection, move into the protocol | |
- (if (= (count output) 1) | |
- (first output) | |
- (throw (Exception. (format "Invalid expression `%s`. Output lenght expected 1, was %d" | |
- s (count output))))))) | |
\ No newline at end of file | |
+(ns wolframite.base.express | |
+ (:require | |
+ [wolframite.impl.protocols :as proto])) | |
+ | |
+(defn express | |
+ "??? Convert a _string_ expression into JLink.Expr using Wolfram itself" | |
+ [s {:keys [jlink-instance]}] | |
+ {:pre [jlink-instance]} | |
+ (assert (string? s) (str "Expected '" (pr-str s) "' to be a string but is " (type s))) | |
+ ;; TODO: debug log: "express string>" | |
+ (let [held-s (str "HoldComplete[" s "]") | |
+ link (or (proto/kernel-link jlink-instance) | |
+ (throw (IllegalStateException. | |
+ (str "Something is wrong - there is no JLink instance, which shouldn't" | |
+ " be possible. Try to stop and start Wolframite again.")))) | |
+ output (io! | |
+ (locking link | |
+ (doto link | |
+ (.evaluate held-s) | |
+ (.waitForAnswer)) | |
+ (.. link getExpr args)))] ; TODO (jakub) get rid of reflection, move into the protocol | |
+ (if (= (count output) 1) | |
+ (first output) | |
+ (throw (Exception. (format "Invalid expression `%s`. Output lenght expected 1, was %d" | |
+ s (count output))))))) | |
diff --git a/src/wolframite/base/package.clj b/src/wolframite/base/package.clj | |
index 3fafe18..a35f1cf 100644 | |
--- a/src/wolframite/base/package.clj | |
+++ b/src/wolframite/base/package.clj | |
@@ -37,7 +37,7 @@ | |
- `alias` - Clojure symbol to be used for accessing the Wolfram context. This will effectively become a Clojure namespace | |
See [[intern-context!]] for details of turning the Wolfram context into a Clojure namespace." | |
-;; TODO: Should check that the symbol isn't already being used. | |
+ ;; TODO: Should check that the symbol isn't already being used. | |
([wl-eval path] | |
(let [context (-> path fs/file-name fs/strip-ext)] | |
(load! wl-eval path context (symbol context)))) | |
@@ -55,4 +55,4 @@ See [[intern-context!]] for details of turning the Wolfram context into a Clojur | |
(comment | |
(load! wolframite.core/eval "/tmp/fake.wl") | |
- (load! wolframite.core/eval "./resources/WolframPackageDemo.wl")) | |
\ No newline at end of file | |
+ (load! wolframite.core/eval "./resources/WolframPackageDemo.wl")) | |
diff --git a/src/wolframite/base/parse.clj b/src/wolframite/base/parse.clj | |
index fe444e9..ae8249f 100644 | |
--- a/src/wolframite/base/parse.clj | |
+++ b/src/wolframite/base/parse.clj | |
@@ -1,202 +1,202 @@ | |
-(ns wolframite.base.parse | |
- "Translate a jlink.Expr returned from an evaluation into Clojure data" | |
- (:require | |
- [clojure.set :as set] | |
- [wolframite.impl.jlink-instance :as jlink-instance] | |
- [wolframite.impl.protocols :as proto] | |
- [wolframite.lib.options :as options] | |
- [wolframite.base.expr :as expr] | |
- [clojure.string :as str])) | |
- | |
-(declare parse) | |
- | |
-(defn pascal->kebab | |
- [s] | |
- (some-> s | |
- (str/replace #"\w(\p{Upper})" (fn [[[p l] _]] (str p "-" (str/lower-case l)))) | |
- str/lower-case)) | |
- | |
-(defn entity-type->keyword [expr opts] | |
- (let [head (pascal->kebab (expr/head-str expr)) | |
- parts (reduce (fn [acc part] | |
- (into acc (cond | |
- (string? part) [(pascal->kebab part)] | |
- (coll? part) (reverse (map pascal->kebab part)) | |
- :else nil))) | |
- [head] | |
- (map #(parse % opts) (.args expr)))] | |
- ;; BAIL: if any of the parts are not recognized, | |
- ;; let it go through normal parsing | |
- (when (not-any? nil? parts) | |
- (keyword (str/join "." (butlast parts)) | |
- (last parts))))) | |
- | |
-(defn custom-parse-dispatch [expr _opts] | |
- (let [head (symbol (expr/head-str expr))] | |
- head)) | |
- | |
-(defn atom? [expr] | |
- (not (.listQ expr))) | |
- | |
-(defn simple-vector-type [expr] | |
- (proto/expr-element-type (jlink-instance/get) :vector expr)) | |
- | |
-(defn simple-matrix-type [expr] | |
- (proto/expr-element-type (jlink-instance/get) :matrix expr)) | |
- | |
-(defn simple-array-type [expr] | |
- (or (simple-vector-type expr) (simple-matrix-type expr))) | |
- | |
-;; FIXME: change name (it's more of a concrete type map) | |
-(defn bound-map [f coll {:keys [flags] :as opts}] | |
- (if (options/flag?' flags :vectors) | |
- (mapv #(f % opts) coll) | |
- (map #(f % opts) coll))) | |
- | |
-(defn parse-complex-list [expr opts] | |
- (bound-map parse (.args expr) opts)) | |
- | |
-(defn parse-integer [expr] | |
- (let [i (.asLong expr)] | |
- (if (and (<= i Integer/MAX_VALUE) | |
- (>= i Integer/MIN_VALUE)) | |
- (int i) | |
- (long i)))) | |
- | |
-(defn parse-rational [expr] | |
- (let [numer (parse-integer (.part expr 1)) | |
- denom (parse-integer (.part expr 2))] | |
- (/ numer denom))) | |
- | |
-(defn parse-symbol [expr {:keys [aliases/base-list]}] | |
- (let [alias->wolf (options/aliases base-list) | |
- smart-aliases (keep (fn [[alias wolf]] | |
- (when (fn? wolf) alias)) | |
- alias->wolf) | |
- wolf->smart-alias (into {} (for [[alias smart-fn] (select-keys alias->wolf smart-aliases) | |
- wolf-sym (-> smart-fn meta :wolframite.alias/targets) | |
- :when wolf-sym] | |
- [wolf-sym alias])) | |
- wolf->alias (-> (apply dissoc alias->wolf smart-aliases) | |
- set/map-invert | |
- (merge wolf->smart-alias)) | |
- s (.toString expr) | |
- sym (symbol (apply str (replace {\` \/} s)))] | |
- (if-let [alias (get wolf->alias sym)] | |
- alias | |
- (cond (= "True" s) true | |
- (= "False" s) false | |
- (= "Null" s) nil | |
- :else sym)))) | |
- | |
-(defn parse-hash-map [expr opts] | |
- (let [inside (first (.args expr)) | |
- ;; inside (first (.args expr)) | |
- all-rules? (every? true? (map #(= "Rule" (expr/head-str %)) (.args expr))) | |
- rules (cond (some-> inside (.listQ)) (parse inside opts) | |
- all-rules? (into {} | |
- (map (fn [kv] | |
- (bound-map (fn [x _opts] (parse x opts)) kv opts)) | |
- (.args expr))) | |
- (= "Dispatch" (expr/head-str inside)) (parse (first (.args inside)) opts) | |
- :else (assert (or (.listQ inside) | |
- (= "Dispatch" (expr/head-str inside))))) | |
- keys (map second rules) | |
- vals (map (comp second #(nth % 2)) rules)] | |
- (if (map? rules) | |
- rules | |
- (zipmap keys vals)))) | |
- | |
-(defn parse-simple-atom [expr type opts] | |
- (cond (= type :Expr/BIGINTEGER) (.asBigInteger expr) | |
- (= type :Expr/BIGDECIMAL) (.asBigDecimal expr) | |
- (= type :Expr/INTEGER) (parse-integer expr) | |
- (= type :Expr/REAL) (.asDouble expr) | |
- (= type :Expr/STRING) (.asString expr) | |
- (= type :Expr/RATIONAL) (parse-rational expr) | |
- (= type :Expr/SYMBOL) (parse-symbol expr opts))) | |
- | |
-;; parameters list used to be: [expr & [type]] (??) | |
-(defn parse-simple-vector [expr type {:keys [flags] :as opts}] | |
- (let [type (or type (simple-vector-type expr))] | |
- (if (and (options/flag?' flags :N) | |
- (some #{:Expr/INTEGER :Expr/BIGINTEGER :Expr/REAL :Expr/BIGDECIMAL} #{type})) | |
- ((if (options/flag?' flags :vectors) vec seq) | |
- (.asArray expr (proto/->expr-type (jlink-instance/get) :Expr/REAL) 1)) | |
- (bound-map (fn [e _opts] (parse-simple-atom e type opts)) (.args expr) opts)))) | |
- | |
-(defn parse-simple-matrix [expr type opts] | |
- (let [type (or type (simple-matrix-type expr))] | |
- (bound-map (fn process-bound-map [a _opts] | |
- (parse-simple-vector a type opts)) | |
- (.args expr) | |
- opts))) | |
- | |
-(defn parse-fn | |
- "Return a function that invokes the Wolfram expression `expr` (typically just a symbol naming a fn), | |
- converting any arguments given to it from Clojure to Wolfram and does the opposite conversion on the | |
- result. | |
- Ex.: `((parse/parse-fn 'Plus {:jlink-instance (jlink-instance/get)}) 1 2) ; => 3` | |
- | |
- Beware: Nesting such fns would result in multiple calls to Wolfram, which is inefficient. Prefer wl/eval in such cases." | |
- [expr opts] | |
- (fn [& args] | |
- (let [cep-fn (requiring-resolve `wolframite.base.cep/cep)] | |
- (cep-fn (apply list expr args) opts #_(update opts :flags #(options/set-flag % :as-expression)))))) | |
- | |
-(defn parse-generic-expression [expr opts] | |
- (-> (list) ;must start with a real list because the promise is that expressions will be converted to lists | |
- (into (map #(parse % opts) (rseq (vec (.args expr))))) | |
- (conj (parse (.head expr) opts)))) | |
- | |
-(defn parse-complex-atom [expr {:keys [flags] :as opts}] | |
- (let [head (expr/head-str expr)] | |
- (cond (.bigIntegerQ expr) (.asBigInteger expr) | |
- (.bigDecimalQ expr) (.asBigDecimal expr) | |
- (.integerQ expr) (parse-integer expr) | |
- (.realQ expr) (.asDouble expr) | |
- (.stringQ expr) (.asString expr) | |
- (.rationalQ expr) (parse-rational expr) | |
- (.symbolQ expr) (parse-symbol expr opts) | |
- (= "Association" head) (parse-hash-map expr opts) #_(parse-generic-expression expr opts) | |
- (= "Function" head) (parse-generic-expression expr opts) | |
- ;(if (and (options/flag?' flags :functions) | |
- ; (not (options/flag?' flags :full-form))) | |
- ; (parse-fn expr opts) | |
- ; (parse-generic-expression expr opts)) | |
- :else (parse-generic-expression expr opts)))) | |
- | |
-(defn standard-parse [expr {:keys [flags] :as opts}] | |
- (assert (proto/expr? (jlink-instance/get) expr)) | |
- (cond | |
- ;(options/flag?' flags :as-function) (parse-fn expr opts) | |
- (or (atom? expr) (options/flag?' flags :full-form)) (parse-complex-atom expr opts) | |
- (simple-vector-type expr) (parse-simple-vector expr nil opts) | |
- (simple-matrix-type expr) (parse-simple-matrix expr nil opts) | |
- :else (parse-complex-list expr opts))) | |
- | |
-(ns-unmap *ns* 'custom-parse) | |
-(defmulti custom-parse | |
- "Modify how Wolfram response is parsed into Clojure data. | |
- | |
- The dispatch-val should be a symbol, matched against the first one of the result list. | |
- | |
- Example: | |
- | |
- ```clj | |
- ; return a Java URL from a Hyperlink call, instead of `'(Hyperlink <label> <url string>)` | |
- (defmethod custom-parse 'Hyperlink [expr opts] | |
- (-> (second (.args expr)) ; 1st = label | |
- (parse/parse opts) | |
- java.net.URI.)) | |
- | |
- (wl/eval '(Hyperlink \"foo\" \"https://www.google.com\")) | |
- ; => #object[java.net.URI 0x3f5e5a46 \"https://www.google.com\"] | |
- ```" | |
- #'custom-parse-dispatch) | |
- | |
-(defmethod custom-parse :default [expr opts] | |
- (standard-parse expr opts)) | |
- | |
-(defn parse [expr opts] | |
- (custom-parse expr opts)) | |
+(ns wolframite.base.parse | |
+ "Translate a jlink.Expr returned from an evaluation into Clojure data" | |
+ (:require | |
+ [clojure.set :as set] | |
+ [clojure.string :as str] | |
+ [wolframite.base.expr :as expr] | |
+ [wolframite.impl.jlink-instance :as jlink-instance] | |
+ [wolframite.impl.protocols :as proto] | |
+ [wolframite.lib.options :as options])) | |
+ | |
+(declare parse) | |
+ | |
+(defn pascal->kebab | |
+ [s] | |
+ (some-> s | |
+ (str/replace #"\w(\p{Upper})" (fn [[[p l] _]] (str p "-" (str/lower-case l)))) | |
+ str/lower-case)) | |
+ | |
+(defn entity-type->keyword [expr opts] | |
+ (let [head (pascal->kebab (expr/head-str expr)) | |
+ parts (reduce (fn [acc part] | |
+ (into acc (cond | |
+ (string? part) [(pascal->kebab part)] | |
+ (coll? part) (reverse (map pascal->kebab part)) | |
+ :else nil))) | |
+ [head] | |
+ (map #(parse % opts) (.args expr)))] | |
+ ;; BAIL: if any of the parts are not recognized, | |
+ ;; let it go through normal parsing | |
+ (when (not-any? nil? parts) | |
+ (keyword (str/join "." (butlast parts)) | |
+ (last parts))))) | |
+ | |
+(defn custom-parse-dispatch [expr _opts] | |
+ (let [head (symbol (expr/head-str expr))] | |
+ head)) | |
+ | |
+(defn atom? [expr] | |
+ (not (.listQ expr))) | |
+ | |
+(defn simple-vector-type [expr] | |
+ (proto/expr-element-type (jlink-instance/get) :vector expr)) | |
+ | |
+(defn simple-matrix-type [expr] | |
+ (proto/expr-element-type (jlink-instance/get) :matrix expr)) | |
+ | |
+(defn simple-array-type [expr] | |
+ (or (simple-vector-type expr) (simple-matrix-type expr))) | |
+ | |
+;; FIXME: change name (it's more of a concrete type map) | |
+(defn bound-map [f coll {:keys [flags] :as opts}] | |
+ (if (options/flag?' flags :vectors) | |
+ (mapv #(f % opts) coll) | |
+ (map #(f % opts) coll))) | |
+ | |
+(defn parse-complex-list [expr opts] | |
+ (bound-map parse (.args expr) opts)) | |
+ | |
+(defn parse-integer [expr] | |
+ (let [i (.asLong expr)] | |
+ (if (and (<= i Integer/MAX_VALUE) | |
+ (>= i Integer/MIN_VALUE)) | |
+ (int i) | |
+ (long i)))) | |
+ | |
+(defn parse-rational [expr] | |
+ (let [numer (parse-integer (.part expr 1)) | |
+ denom (parse-integer (.part expr 2))] | |
+ (/ numer denom))) | |
+ | |
+(defn parse-symbol [expr {:keys [aliases/base-list]}] | |
+ (let [alias->wolf (options/aliases base-list) | |
+ smart-aliases (keep (fn [[alias wolf]] | |
+ (when (fn? wolf) alias)) | |
+ alias->wolf) | |
+ wolf->smart-alias (into {} (for [[alias smart-fn] (select-keys alias->wolf smart-aliases) | |
+ wolf-sym (-> smart-fn meta :wolframite.alias/targets) | |
+ :when wolf-sym] | |
+ [wolf-sym alias])) | |
+ wolf->alias (-> (apply dissoc alias->wolf smart-aliases) | |
+ set/map-invert | |
+ (merge wolf->smart-alias)) | |
+ s (.toString expr) | |
+ sym (symbol (apply str (replace {\` \/} s)))] | |
+ (if-let [alias (get wolf->alias sym)] | |
+ alias | |
+ (cond (= "True" s) true | |
+ (= "False" s) false | |
+ (= "Null" s) nil | |
+ :else sym)))) | |
+ | |
+(defn parse-hash-map [expr opts] | |
+ (let [inside (first (.args expr)) | |
+ ;; inside (first (.args expr)) | |
+ all-rules? (every? true? (map #(= "Rule" (expr/head-str %)) (.args expr))) | |
+ rules (cond (some-> inside (.listQ)) (parse inside opts) | |
+ all-rules? (into {} | |
+ (map (fn [kv] | |
+ (bound-map (fn [x _opts] (parse x opts)) kv opts)) | |
+ (.args expr))) | |
+ (= "Dispatch" (expr/head-str inside)) (parse (first (.args inside)) opts) | |
+ :else (assert (or (.listQ inside) | |
+ (= "Dispatch" (expr/head-str inside))))) | |
+ keys (map second rules) | |
+ vals (map (comp second #(nth % 2)) rules)] | |
+ (if (map? rules) | |
+ rules | |
+ (zipmap keys vals)))) | |
+ | |
+(defn parse-simple-atom [expr type opts] | |
+ (cond (= type :Expr/BIGINTEGER) (.asBigInteger expr) | |
+ (= type :Expr/BIGDECIMAL) (.asBigDecimal expr) | |
+ (= type :Expr/INTEGER) (parse-integer expr) | |
+ (= type :Expr/REAL) (.asDouble expr) | |
+ (= type :Expr/STRING) (.asString expr) | |
+ (= type :Expr/RATIONAL) (parse-rational expr) | |
+ (= type :Expr/SYMBOL) (parse-symbol expr opts))) | |
+ | |
+;; parameters list used to be: [expr & [type]] (??) | |
+(defn parse-simple-vector [expr type {:keys [flags] :as opts}] | |
+ (let [type (or type (simple-vector-type expr))] | |
+ (if (and (options/flag?' flags :N) | |
+ (some #{:Expr/INTEGER :Expr/BIGINTEGER :Expr/REAL :Expr/BIGDECIMAL} #{type})) | |
+ ((if (options/flag?' flags :vectors) vec seq) | |
+ (.asArray expr (proto/->expr-type (jlink-instance/get) :Expr/REAL) 1)) | |
+ (bound-map (fn [e _opts] (parse-simple-atom e type opts)) (.args expr) opts)))) | |
+ | |
+(defn parse-simple-matrix [expr type opts] | |
+ (let [type (or type (simple-matrix-type expr))] | |
+ (bound-map (fn process-bound-map [a _opts] | |
+ (parse-simple-vector a type opts)) | |
+ (.args expr) | |
+ opts))) | |
+ | |
+(defn parse-fn | |
+ "Return a function that invokes the Wolfram expression `expr` (typically just a symbol naming a fn), | |
+ converting any arguments given to it from Clojure to Wolfram and does the opposite conversion on the | |
+ result. | |
+ Ex.: `((parse/parse-fn 'Plus {:jlink-instance (jlink-instance/get)}) 1 2) ; => 3` | |
+ | |
+ Beware: Nesting such fns would result in multiple calls to Wolfram, which is inefficient. Prefer wl/eval in such cases." | |
+ [expr opts] | |
+ (fn [& args] | |
+ (let [cep-fn (requiring-resolve `wolframite.base.cep/cep)] | |
+ (cep-fn (apply list expr args) opts #_(update opts :flags #(options/set-flag % :as-expression)))))) | |
+ | |
+(defn parse-generic-expression [expr opts] | |
+ (-> (list) ; must start with a real list because the promise is that expressions will be converted to lists | |
+ (into (map #(parse % opts) (rseq (vec (.args expr))))) | |
+ (conj (parse (.head expr) opts)))) | |
+ | |
+(defn parse-complex-atom [expr {:keys [flags] :as opts}] | |
+ (let [head (expr/head-str expr)] | |
+ (cond (.bigIntegerQ expr) (.asBigInteger expr) | |
+ (.bigDecimalQ expr) (.asBigDecimal expr) | |
+ (.integerQ expr) (parse-integer expr) | |
+ (.realQ expr) (.asDouble expr) | |
+ (.stringQ expr) (.asString expr) | |
+ (.rationalQ expr) (parse-rational expr) | |
+ (.symbolQ expr) (parse-symbol expr opts) | |
+ (= "Association" head) (parse-hash-map expr opts) #_(parse-generic-expression expr opts) | |
+ (= "Function" head) (parse-generic-expression expr opts) | |
+ ; (if (and (options/flag?' flags :functions) | |
+ ; (not (options/flag?' flags :full-form))) | |
+ ; (parse-fn expr opts) | |
+ ; (parse-generic-expression expr opts)) | |
+ :else (parse-generic-expression expr opts)))) | |
+ | |
+(defn standard-parse [expr {:keys [flags] :as opts}] | |
+ (assert (proto/expr? (jlink-instance/get) expr)) | |
+ (cond | |
+ ; (options/flag?' flags :as-function) (parse-fn expr opts) | |
+ (or (atom? expr) (options/flag?' flags :full-form)) (parse-complex-atom expr opts) | |
+ (simple-vector-type expr) (parse-simple-vector expr nil opts) | |
+ (simple-matrix-type expr) (parse-simple-matrix expr nil opts) | |
+ :else (parse-complex-list expr opts))) | |
+ | |
+(ns-unmap *ns* 'custom-parse) | |
+(defmulti custom-parse | |
+ "Modify how Wolfram response is parsed into Clojure data. | |
+ | |
+ The dispatch-val should be a symbol, matched against the first one of the result list. | |
+ | |
+ Example: | |
+ | |
+ ```clj | |
+ ; return a Java URL from a Hyperlink call, instead of `'(Hyperlink <label> <url string>)` | |
+ (defmethod custom-parse 'Hyperlink [expr opts] | |
+ (-> (second (.args expr)) ; 1st = label | |
+ (parse/parse opts) | |
+ java.net.URI.)) | |
+ | |
+ (wl/eval '(Hyperlink \"foo\" \"https://www.google.com\")) | |
+ ; => #object[java.net.URI 0x3f5e5a46 \"https://www.google.com\"] | |
+ ```" | |
+ #'custom-parse-dispatch) | |
+ | |
+(defmethod custom-parse :default [expr opts] | |
+ (standard-parse expr opts)) | |
+ | |
+(defn parse [expr opts] | |
+ (custom-parse expr opts)) | |
diff --git a/src/wolframite/core.clj b/src/wolframite/core.clj | |
index 0cc3e77..22ebd1a 100644 | |
--- a/src/wolframite/core.clj | |
+++ b/src/wolframite/core.clj | |
@@ -32,21 +32,21 @@ | |
" | |
(:refer-clojure :exclude [eval]) | |
(:require | |
- [babashka.fs :as fs] | |
- [clojure.set :as set] | |
- [clojure.tools.logging :as log] | |
- [wolframite.base.cep :as cep] | |
- [wolframite.base.convert :as convert] | |
- [wolframite.base.evaluate :as evaluate] | |
- [wolframite.base.express :as express] | |
- [wolframite.base.package :as package] | |
- [wolframite.base.parse :as parse] | |
- [wolframite.impl.jlink-instance :as jlink-instance] | |
- [wolframite.impl.protocols :as proto] | |
- [wolframite.runtime.defaults :as defaults] | |
- [wolframite.runtime.jlink :as jlink] | |
- [wolframite.runtime.system :as system] | |
- [wolframite.wolfram :as w])) | |
+ [babashka.fs :as fs] | |
+ [clojure.set :as set] | |
+ [clojure.tools.logging :as log] | |
+ [wolframite.base.cep :as cep] | |
+ [wolframite.base.convert :as convert] | |
+ [wolframite.base.evaluate :as evaluate] | |
+ [wolframite.base.express :as express] | |
+ [wolframite.base.package :as package] | |
+ [wolframite.base.parse :as parse] | |
+ [wolframite.impl.jlink-instance :as jlink-instance] | |
+ [wolframite.impl.protocols :as proto] | |
+ [wolframite.runtime.defaults :as defaults] | |
+ [wolframite.runtime.jlink :as jlink] | |
+ [wolframite.runtime.system :as system] | |
+ [wolframite.wolfram :as w])) | |
(defonce ^{:deprecated true, :private true} kernel-link-atom (atom nil)) ; FIXME (jakub) DEPRECATED, access it via the jlink-instance instead | |
@@ -113,10 +113,10 @@ | |
Requires [[start!]] to be called first." | |
[] | |
(zipmap | |
- [:wolfram-version :wolfram-kernel-name :max-license-processes] | |
- (eval '[$VersionNumber | |
- (SystemInformation "Kernel", "ProductKernelName") | |
- (SystemInformation "Kernel", "MaxLicenseProcesses")]))) | |
+ [:wolfram-version :wolfram-kernel-name :max-license-processes] | |
+ (eval '[$VersionNumber | |
+ (SystemInformation "Kernel", "ProductKernelName") | |
+ (SystemInformation "Kernel", "MaxLicenseProcesses")]))) | |
(defn start! | |
"Initialize Wolframite and start! the underlying Wolfram Kernel - required once before you make any eval calls. | |
@@ -247,7 +247,7 @@ | |
- `alias` - Clojure symbol to be used for accessing the Wolfram context. This will effectively become a Clojure namespace | |
See `package/intern-context!` for details of turning the Wolfram context into a Clojure namespace." | |
-;; TODO: Should check that the symbol isn't already being used. | |
+ ;; TODO: Should check that the symbol isn't already being used. | |
([path] | |
(let [context (-> path fs/file-name fs/strip-ext)] | |
(package/load! eval path context (symbol context)))) | |
diff --git a/src/wolframite/impl/jlink_instance.clj b/src/wolframite/impl/jlink_instance.clj | |
index c950391..89341db 100644 | |
--- a/src/wolframite/impl/jlink_instance.clj | |
+++ b/src/wolframite/impl/jlink_instance.clj | |
@@ -1,7 +1,9 @@ | |
(ns wolframite.impl.jlink-instance | |
- (:require [wolframite.impl.protocols :as proto]) | |
- (:import [wolframite.impl.protocols JLink]) | |
- (:refer-clojure :exclude [get reset!])) | |
+ (:refer-clojure :exclude [get reset!]) | |
+ (:require | |
+ [wolframite.impl.protocols :as proto]) | |
+ (:import | |
+ (wolframite.impl.protocols JLink))) | |
;; The actual impl. of the JLink interface, set at runtime | |
(defonce jlink-instance (atom nil)) | |
diff --git a/src/wolframite/impl/jlink_proto_impl.clj b/src/wolframite/impl/jlink_proto_impl.clj | |
index a0457fc..f83a299 100644 | |
--- a/src/wolframite/impl/jlink_proto_impl.clj | |
+++ b/src/wolframite/impl/jlink_proto_impl.clj | |
@@ -1,11 +1,12 @@ | |
(ns wolframite.impl.jlink-proto-impl | |
"The 'real' implementation of JLink, which does depend on JLink classes and thus | |
cannot be loaded/required until JLink is on the classpath." | |
- (:require [clojure.tools.logging :as log] | |
- [wolframite.impl.protocols :as proto]) | |
- (:import (clojure.lang BigInt) | |
- [com.wolfram.jlink Expr KernelLink MathCanvas MathLink MathLinkException MathLinkFactory | |
- PacketListener PacketArrivedEvent PacketPrinter])) | |
+ (:require | |
+ [clojure.tools.logging :as log] | |
+ [wolframite.impl.protocols :as proto]) | |
+ (:import | |
+ (clojure.lang BigInt) | |
+ (com.wolfram.jlink Expr KernelLink MathCanvas MathLink MathLinkException MathLinkFactory PacketArrivedEvent PacketListener PacketPrinter))) | |
(defn- array? [x] | |
(some-> x class .isArray)) | |
@@ -15,8 +16,8 @@ | |
(cond | |
(sequential? primitive-or-exprs) | |
(Expr. | |
- ^Expr (first primitive-or-exprs) | |
- ^"[Lcom.wolfram.jlink.Expr;" (into-array Expr (rest primitive-or-exprs))) | |
+ ^Expr (first primitive-or-exprs) | |
+ ^"[Lcom.wolfram.jlink.Expr;" (into-array Expr (rest primitive-or-exprs))) | |
;; Here, primitive-or-exprs could be an int, a String, long[], or similar | |
(array? primitive-or-exprs) | |
@@ -69,7 +70,7 @@ | |
(let [expr (.getExpr link)] | |
(when-not (.symbolQ expr) | |
;; not sure why these are sent, not useful; e.g. Get when a Get call failed etc. | |
- {:type :message :content expr})) | |
+ {:type :message :content expr})) | |
nil) | |
(swap! capture conj))) | |
@@ -77,7 +78,7 @@ | |
(comment | |
(let [link (proto/kernel-link ((requiring-resolve 'wolframite.impl.jlink-instance/get)))] | |
- ;(.removePacketListener link packet-listener) | |
+ ; (.removePacketListener link packet-listener) | |
(.addPacketListener link packet-listener) | |
,) | |
,) | |
@@ -153,7 +154,7 @@ | |
;; Note: The call below ensures we actually try to connect to the kernel | |
(.discardAnswer)) | |
(reset! kernel-link-atom))] | |
- ;(.getError kernel-link) (.getErrorMessage kernel-link) | |
+ ; (.getError kernel-link) (.getErrorMessage kernel-link) | |
kernel-link) | |
(catch MathLinkException e | |
(if (= (ex-message e) "MathLink connection was lost.") | |
@@ -193,9 +194,9 @@ | |
^String (apply str (replace {\/ \`} name)))) | |
(->expr [_this obj] ; fallback for transforming anything we don't handle manually, via JLink itself | |
(.getExpr | |
- (doto (MathLinkFactory/createLoopbackLink) | |
- (.put obj) | |
- (.endPacket)))) | |
+ (doto (MathLinkFactory/createLoopbackLink) | |
+ (.put obj) | |
+ (.endPacket)))) | |
(expr? [_this x] | |
(instance? Expr x)) | |
(expr-element-type [_this container-type expr] | |
@@ -242,6 +243,6 @@ | |
(defn create [kernel-link-atom opts] | |
(map->JLinkImpl | |
- {:opts opts | |
- :kernel-link-atom kernel-link-atom | |
- :packet-listener (->InfoPacketCaptureListener (atom nil))})) | |
+ {:opts opts | |
+ :kernel-link-atom kernel-link-atom | |
+ :packet-listener (->InfoPacketCaptureListener (atom nil))})) | |
diff --git a/src/wolframite/impl/protocols.clj b/src/wolframite/impl/protocols.clj | |
index e6bb94f..61b5034 100644 | |
--- a/src/wolframite/impl/protocols.clj | |
+++ b/src/wolframite/impl/protocols.clj | |
@@ -1,5 +1,6 @@ | |
(ns wolframite.impl.protocols | |
- (:import (java.awt Component))) | |
+ (:import | |
+ (java.awt Component))) | |
(defprotocol JLink | |
"A protocol to divorce the code from a direct dependency on JLink, so that it can be loaded | |
@@ -60,4 +61,4 @@ | |
(^Component make-math-canvas! [this kernel-link] | |
(throw (IllegalStateException. "JLink not loaded!"))) | |
(jlink-package-name [this] | |
- (throw (IllegalStateException. "JLink not loaded!")))) | |
+ (throw (IllegalStateException. "JLink not loaded!")))) | |
diff --git a/src/wolframite/impl/wolfram_syms/intern.clj b/src/wolframite/impl/wolfram_syms/intern.clj | |
index b7f2bf8..db77efd 100644 | |
--- a/src/wolframite/impl/wolfram_syms/intern.clj | |
+++ b/src/wolframite/impl/wolfram_syms/intern.clj | |
@@ -1,8 +1,10 @@ | |
(ns wolframite.impl.wolfram-syms.intern | |
"Interning of Wolfram symbols as Clojure vars, for convenience." | |
- (:require [clojure.walk :as walk] | |
- [clojure.walk]) | |
- (:import (clojure.lang IMeta))) | |
+ (:require | |
+ [clojure.walk :as walk] | |
+ [clojure.walk]) | |
+ (:import | |
+ (clojure.lang IMeta))) | |
(defn interned-var-val->symbol | |
"Turns the value of an [[clj-intern]]-ed Wolfram symbols into said symbol, if possible - otherwise, returns nil. | |
@@ -87,4 +89,4 @@ | |
(if (qualified-symbol? form) | |
(symbol (name form)) | |
form)) | |
- form)) | |
\ No newline at end of file | |
+ form)) | |
diff --git a/src/wolframite/impl/wolfram_syms/wolfram_syms.clj b/src/wolframite/impl/wolfram_syms/wolfram_syms.clj | |
index c1efb62..d2c2595 100644 | |
--- a/src/wolframite/impl/wolfram_syms/wolfram_syms.clj | |
+++ b/src/wolframite/impl/wolfram_syms/wolfram_syms.clj | |
@@ -1,6 +1,7 @@ | |
(ns wolframite.impl.wolfram-syms.wolfram-syms | |
"Support for loading available symbols (fns & more) from Wolfram" | |
- (:require [wolframite.impl.wolfram-syms.intern :as intern])) | |
+ (:require | |
+ [wolframite.impl.wolfram-syms.intern :as intern])) | |
(defn fetch-all-wolfram-symbols [wl-eval] | |
(doall (->> (wl-eval '(EntityValue (WolframLanguageData) ["Name", "PlaintextUsage"] "EntityPropertyAssociation")) | |
@@ -32,7 +33,7 @@ | |
(doall (->> (fetch-all-wolfram-symbols wl-eval) | |
(map (fn [{:keys [sym doc]}] | |
(intern/clj-intern | |
- sym | |
- {:intern/ns-sym ns-sym | |
- :intern/extra-meta {:doc (when (string? doc) ; could be `(Missing "NotAvailable")` | |
- doc)}})))))) | |
+ sym | |
+ {:intern/ns-sym ns-sym | |
+ :intern/extra-meta {:doc (when (string? doc) ; could be `(Missing "NotAvailable")` | |
+ doc)}})))))) | |
diff --git a/src/wolframite/impl/wolfram_syms/write_ns.clj b/src/wolframite/impl/wolfram_syms/write_ns.clj | |
index 582e5e2..48021bb 100644 | |
--- a/src/wolframite/impl/wolfram_syms/write_ns.clj | |
+++ b/src/wolframite/impl/wolfram_syms/write_ns.clj | |
@@ -3,14 +3,15 @@ | |
autocompletion (even in editors using static code analysis) and linters (clj-kondo only does | |
static code)" | |
(:require | |
- [clojure.string :as str] | |
- [clojure.java.io :as io] | |
[clojure.edn :as edn] | |
+ [clojure.java.io :as io] | |
+ [clojure.string :as str] | |
[wolframite.core :as core] | |
[wolframite.impl.wolfram-syms.intern :as intern] | |
[wolframite.impl.wolfram-syms.wolfram-syms :as wolfram-syms] | |
[wolframite.runtime.defaults :as defaults]) | |
- (:import (java.io FileNotFoundException PushbackReader))) | |
+ (:import | |
+ (java.io FileNotFoundException PushbackReader))) | |
(comment | |
(-> (io/resource "wolframite/impl/wolfram_syms/write_ns/includes.clj") | |
@@ -97,24 +98,24 @@ | |
(spit path | |
(str/join "\n" | |
(concat | |
- (map pr-str wolfram-ns-heading) | |
+ (map pr-str wolfram-ns-heading) | |
;; Add version info, similar to clojure's *clojure-version*; marked dynamic so | |
;; that clj doesn't complain about those *..* | |
- [(format "(def ^:dynamic *wolfram-version* %s)" wolfram-version)] | |
- [(format "(def ^:dynamic *wolfram-kernel-name* \"%s\")" wolfram-kernel-name)] | |
- (map pr-str (make-defs all-syms)) | |
- (map pr-str (make-wolfram-ns-footer all-syms)) | |
- [(inclusions-body-str!)] ; the w/fn macro etc. | |
- (some-> aliases (aliases->defs all-syms) (->> (map pr-str)))))) | |
+ [(format "(def ^:dynamic *wolfram-version* %s)" wolfram-version)] | |
+ [(format "(def ^:dynamic *wolfram-kernel-name* \"%s\")" wolfram-kernel-name)] | |
+ (map pr-str (make-defs all-syms)) | |
+ (map pr-str (make-wolfram-ns-footer all-syms)) | |
+ [(inclusions-body-str!)] ; the w/fn macro etc. | |
+ (some-> aliases (aliases->defs all-syms) (->> (map pr-str)))))) | |
(catch FileNotFoundException e | |
(throw (ex-info (format "Could not write to %s - does the parent dir exist?" | |
path) | |
{:path path, :cause (ex-message e)}))))))) | |
-;(defmacro make-wolf-defs [] | |
+; (defmacro make-wolf-defs [] | |
; `(do ~@(make-defs))) | |
; USAGE: Use | |
-;(macroexpand '(make-wolf-defs)) | |
+; (macroexpand '(make-wolf-defs)) | |
(comment | |
@@ -125,5 +126,5 @@ | |
(load-file "src/wolframite/wolfram.clj")) | |
(write-ns! | |
- "src/wolframite/wolfram.clj" | |
- {:aliases '{I Integrate}})) | |
+ "src/wolframite/wolfram.clj" | |
+ {:aliases '{I Integrate}})) | |
diff --git a/src/wolframite/impl/wolfram_syms/write_ns/includes.clj b/src/wolframite/impl/wolfram_syms/write_ns/includes.clj | |
index fcc1f87..c4e481f 100644 | |
--- a/src/wolframite/impl/wolfram_syms/write_ns/includes.clj | |
+++ b/src/wolframite/impl/wolfram_syms/write_ns/includes.clj | |
@@ -1,18 +1,18 @@ | |
(ns wolframite.impl.wolfram-syms.write-ns.includes | |
"The content is to be included in the generated `wolframite.wolfram` ns." | |
+ (:refer-clojure :only [defmacro let list]) | |
(:require | |
- clojure.walk | |
- wolframite.impl.wolfram-syms.intern) | |
- (:refer-clojure :only [defmacro let list])) | |
+ [clojure.walk] | |
+ [wolframite.impl.wolfram-syms.intern])) | |
;; Cursive: resolve as defn; Kondo: TBD | |
;; TODO: Do we want to enable specs?! | |
-;(s/fdef fn | |
+; (s/fdef fn | |
; :args (s/cat :params :clojure.core.specs.alpha/params+body | |
; :body any?) | |
; :ret any?) | |
-;;--INCLUDE-START-- | |
+;; --INCLUDE-START-- | |
(defmacro fn | |
"Creates a Wolfram anonymous function with the given arguments and single expression body. | |
Example usage: `(wl/eval (w/Map (w/fn [x] (w/Plus x 1)) [1 2 3]))`" | |
diff --git a/src/wolframite/lib/helpers.clj b/src/wolframite/lib/helpers.clj | |
index 138bfe6..5e129f1 100644 | |
--- a/src/wolframite/lib/helpers.clj | |
+++ b/src/wolframite/lib/helpers.clj | |
@@ -1,6 +1,7 @@ | |
(ns wolframite.lib.helpers | |
- (:require [clojure.java.browse :refer [browse-url]] | |
- [wolframite.impl.wolfram-syms.intern :as intern])) | |
+ (:require | |
+ [clojure.java.browse :refer [browse-url]] | |
+ [wolframite.impl.wolfram-syms.intern :as intern])) | |
(defn help-link [fn-sym] | |
(format "https://reference.wolfram.com/language/ref/%s.html" fn-sym)) | |
diff --git a/src/wolframite/lib/options.clj b/src/wolframite/lib/options.clj | |
index 5bd815d..b57fef3 100644 | |
--- a/src/wolframite/lib/options.clj | |
+++ b/src/wolframite/lib/options.clj | |
@@ -1,46 +1,47 @@ | |
-(ns wolframite.lib.options | |
- (:require [wolframite.runtime.defaults :as defaults])) | |
- | |
-(defn filter-params [current-options] | |
- (into {} (filter #(keyword? (key %)) current-options))) | |
- | |
-(defn filter-flags [current-options] | |
- (into {} (filter #(set? (first %)) current-options))) | |
- | |
-(defn filter-flag-sets | |
- [flag current-options] | |
- (filter #(some #{flag} %) (keys (filter-flags current-options)))) | |
- | |
-(defn flags-into | |
- "Conjoins each flag in flags into hash-map *flags*, setting each such flag as the value | |
- of any keys that contain flag as an element." | |
- [current-options flags] | |
- (into current-options | |
- (for [flag flags | |
- active-set (filter-flag-sets flag current-options)] | |
- [active-set flag]))) | |
- | |
-(defn params-into | |
- "Conjoins each key-value pair in options into hash-map *options*, conjoining each such pair | |
- only if options-keys contains that key." | |
- [current-options params] | |
- (into current-options | |
- (filter #(some #{(first %)} (keys (filter-params current-options))) params))) | |
- | |
-(defn options-into [current-options params flags] | |
- (flags-into (params-into current-options params) flags)) | |
- | |
-(defn flag? [current-options flag] | |
- (not (nil? (some #{flag} (vals (filter-flags current-options)))))) | |
- | |
-(defn flag?' [user-flags flag] | |
- (flag? (flags-into defaults/default-options user-flags) flag)) | |
- | |
-;; | |
- | |
-(defn set-flag [flags f] | |
- (flags-into flags [f])) | |
- | |
-(defn aliases [base-list] | |
- (or (defaults/default-options base-list) | |
- (defaults/default-options :clojure-aliases))) | |
+(ns wolframite.lib.options | |
+ (:require | |
+ [wolframite.runtime.defaults :as defaults])) | |
+ | |
+(defn filter-params [current-options] | |
+ (into {} (filter #(keyword? (key %)) current-options))) | |
+ | |
+(defn filter-flags [current-options] | |
+ (into {} (filter #(set? (first %)) current-options))) | |
+ | |
+(defn filter-flag-sets | |
+ [flag current-options] | |
+ (filter #(some #{flag} %) (keys (filter-flags current-options)))) | |
+ | |
+(defn flags-into | |
+ "Conjoins each flag in flags into hash-map *flags*, setting each such flag as the value | |
+ of any keys that contain flag as an element." | |
+ [current-options flags] | |
+ (into current-options | |
+ (for [flag flags | |
+ active-set (filter-flag-sets flag current-options)] | |
+ [active-set flag]))) | |
+ | |
+(defn params-into | |
+ "Conjoins each key-value pair in options into hash-map *options*, conjoining each such pair | |
+ only if options-keys contains that key." | |
+ [current-options params] | |
+ (into current-options | |
+ (filter #(some #{(first %)} (keys (filter-params current-options))) params))) | |
+ | |
+(defn options-into [current-options params flags] | |
+ (flags-into (params-into current-options params) flags)) | |
+ | |
+(defn flag? [current-options flag] | |
+ (not (nil? (some #{flag} (vals (filter-flags current-options)))))) | |
+ | |
+(defn flag?' [user-flags flag] | |
+ (flag? (flags-into defaults/default-options user-flags) flag)) | |
+ | |
+;; | |
+ | |
+(defn set-flag [flags f] | |
+ (flags-into flags [f])) | |
+ | |
+(defn aliases [base-list] | |
+ (or (defaults/default-options base-list) | |
+ (defaults/default-options :clojure-aliases))) | |
diff --git a/src/wolframite/runtime/defaults.clj b/src/wolframite/runtime/defaults.clj | |
index d4f5fa0..76f96d5 100644 | |
--- a/src/wolframite/runtime/defaults.clj | |
+++ b/src/wolframite/runtime/defaults.clj | |
@@ -1,7 +1,8 @@ | |
(ns wolframite.runtime.defaults | |
"Flags and aliases for the Wolfram runtime." | |
- (:require clojure.set | |
- [clojure.walk :as walk])) | |
+ (:require | |
+ [clojure.set] | |
+ [clojure.walk :as walk])) | |
"TODO: | |
- Consider function that finds all non-numeric symbols (and not '- ') that start with a '-' and replace them with Minus[<symbol>] | |
@@ -13,14 +14,14 @@ | |
#{:parse :no-parse} :parse | |
#{:evaluate :no-evaluate} :evaluate | |
#{:convert :no-convert} :convert | |
- ;#{:hash-maps :no-hash-maps} :hash-maps | |
- ;#{:functions :no-functions} :functions ;; ?? parse (Function ...) into our parse-fn instance?! | |
+ ; #{:hash-maps :no-hash-maps} :hash-maps | |
+ ; #{:functions :no-functions} :functions ;; ?? parse (Function ...) into our parse-fn instance?! | |
#{:aliases :no-aliases} :aliases | |
#{:N :no-N} :no-N ; :N -> use Expr.asArray on matrix' rows | |
- ;#{:verbose :no-verbose} :no-verbose | |
- ;#{:as-function | |
+ ; #{:verbose :no-verbose} :no-verbose | |
+ ; #{:as-function | |
; :as-expression} :as-expression | |
- ;#{:restore-defaults | |
+ ; #{:restore-defaults | |
; :no-restore-defaults} :no-restore-defaults | |
#{:full-form | |
:clojure-form} :clojure-form}) | |
@@ -104,7 +105,7 @@ | |
:parse true | |
:functions true | |
:aliases true | |
- ;:as-expression true | |
+ ; :as-expression true | |
:clojure-form true ;; FIXME: better name | |
:N false}) | |
diff --git a/src/wolframite/runtime/system.clj b/src/wolframite/runtime/system.clj | |
index f60e1af..49cb7a1 100644 | |
--- a/src/wolframite/runtime/system.clj | |
+++ b/src/wolframite/runtime/system.clj | |
@@ -135,8 +135,8 @@ | |
(#{"osx" "macos" "Mac OS X" "mac"} os-str) :mac | |
(or (#{"win" "windows"} os-str) (str/starts-with? os-str "Windows")) :windows | |
:else (throw | |
- (ex-info (str "Did not recognise " os-str " as a supported OS.") | |
- {:os os-str}))))) | |
+ (ex-info (str "Did not recognise " os-str " as a supported OS.") | |
+ {:os os-str}))))) | |
(defn detect-os | |
"Tries to determine the current operating system. | |
@@ -191,7 +191,7 @@ | |
The longest parental directory path that is common to both input paths. | |
" | |
[path1 path2] | |
- ;; TODO: This should be moved to some utility library. | |
+ ;; TODO: This should be moved to some utility library. | |
(let [sep "/" | |
rx-sep (re-pattern sep)] | |
(->> [path1 path2] | |
diff --git a/src/wolframite/specs.clj b/src/wolframite/specs.clj | |
index 012901f..a3370bf 100644 | |
--- a/src/wolframite/specs.clj | |
+++ b/src/wolframite/specs.clj | |
@@ -1,8 +1,9 @@ | |
(ns wolframite.specs | |
- (:require [wolframite.runtime.defaults :as defaults] | |
- [clojure.spec.alpha :as s] | |
- [clojure.spec.test.alpha :as st] | |
- clojure.set)) | |
+ (:require | |
+ [clojure.set] | |
+ [clojure.spec.alpha :as s] | |
+ [clojure.spec.test.alpha :as st] | |
+ [wolframite.runtime.defaults :as defaults])) | |
(s/def :wl/flag defaults/all-flags) | |
(s/def :wl/flags (s/coll-of :wl/flag)) | |
@@ -13,8 +14,8 @@ | |
(s/def :wl/opts-map (s/keys :opt [:wl/flags :wl/aliases])) | |
(s/def :wl/args (s/alt | |
- :no-options (s/cat :body any?) | |
- :with-options (s/cat :opts-kw? :wl/opts-kw :opts-map (s/nilable :wl/opts-map) :body any?))) | |
+ :no-options (s/cat :body any?) | |
+ :with-options (s/cat :opts-kw? :wl/opts-kw :opts-map (s/nilable :wl/opts-map) :body any?))) | |
(comment ;; spec examples | |
@@ -33,12 +34,8 @@ | |
(s/conform :wl/args [:opts {:flags [:parse/as-function :debug/verbose :convert/hash-maps]} '(Dot [1 3 4] [5 4 6])]) | |
- | |
(let [args #_['(Dot [1 3 4] [5 4 6])] [:opts {:flags [:parse/as-function :debug/verbose :convert/hash-maps]} '(Dot [1 3 4] [5 4 6])] | |
[options-flag {:keys [opts-map body]}] (s/conform :wl/args args)] | |
(if (= :with-options options-flag) | |
[opts-map body] | |
- [body])) | |
- | |
- | |
- ) | |
+ [body]))) | |
diff --git a/src/wolframite/tools/clerk_helper.clj b/src/wolframite/tools/clerk_helper.clj | |
index efc9236..103f4e5 100644 | |
--- a/src/wolframite/tools/clerk_helper.clj | |
+++ b/src/wolframite/tools/clerk_helper.clj | |
@@ -1,11 +1,12 @@ | |
(ns wolframite.tools.clerk-helper | |
- (:require [wolframite.core :as wl] | |
- [nextjournal.clerk :as clerk] | |
- [nextjournal.clerk.webserver :as webserver] | |
- [nextjournal.beholder :as beholder] | |
- ;; [clj-http.client :as client] | |
- [clojure.java.io :as io] | |
- [wolframite.tools.hiccup :as h])) | |
+ (:require | |
+ ;; [clj-http.client :as client] | |
+ [clojure.java.io :as io] | |
+ [nextjournal.beholder :as beholder] | |
+ [nextjournal.clerk :as clerk] | |
+ [nextjournal.clerk.webserver :as webserver] | |
+ [wolframite.core :as wl] | |
+ [wolframite.tools.hiccup :as h])) | |
(defn view [form & {:keys [folded?]}] | |
(clerk/html (h/view* form folded?))) | |
diff --git a/src/wolframite/tools/experimental.clj b/src/wolframite/tools/experimental.clj | |
index 2e3eb71..ef887fa 100644 | |
--- a/src/wolframite/tools/experimental.clj | |
+++ b/src/wolframite/tools/experimental.clj | |
@@ -1,11 +1,13 @@ | |
(ns wolframite.tools.experimental | |
"Don't use. Subject to change without prior notice." | |
- (:require [wolframite.core :as wl] | |
- [wolframite.impl.jlink-instance :as jlink-instance] | |
- [wolframite.impl.protocols :as proto]) | |
- (:import [javax.swing JFrame JPanel SwingUtilities] | |
- [java.awt Dimension] | |
- [com.wolfram.jlink MathGraphicsJPanel])) | |
+ (:require | |
+ [wolframite.core :as wl] | |
+ [wolframite.impl.jlink-instance :as jlink-instance] | |
+ [wolframite.impl.protocols :as proto]) | |
+ (:import | |
+ (com.wolfram.jlink MathGraphicsJPanel) | |
+ (java.awt Dimension) | |
+ (javax.swing JFrame JPanel SwingUtilities))) | |
(defonce ^:private app (promise)) | |
diff --git a/src/wolframite/tools/graphics.clj b/src/wolframite/tools/graphics.clj | |
index 75df755..de55cf1 100644 | |
--- a/src/wolframite/tools/graphics.clj | |
+++ b/src/wolframite/tools/graphics.clj | |
@@ -3,14 +3,16 @@ | |
;; Wolfram has You use MathCanvas when you want an AWT component and MathGraphicsJPanel when you | |
;; want a Swing component - see https://reference.wolfram.com/language/JLink/tutorial/CallingJavaFromTheWolframLanguage.html#20608 | |
;; Notice that KernelLink also has evaluateToImage() and evaluateToTypeset() methods | |
- (:require [wolframite.impl.jlink-instance :as jlink-instance] | |
- [wolframite.impl.protocols :as proto] | |
- [wolframite.core :as wl]) | |
- (:import (java.awt Color Component Frame) | |
- (java.awt.image BufferedImage) | |
- (javax.imageio ImageIO) | |
- (java.io ByteArrayInputStream) | |
- (java.awt.event WindowAdapter ActionEvent))) | |
+ (:require | |
+ [wolframite.core :as wl] | |
+ [wolframite.impl.jlink-instance :as jlink-instance] | |
+ [wolframite.impl.protocols :as proto]) | |
+ (:import | |
+ (java.awt Color Component Frame) | |
+ (java.awt.event ActionEvent WindowAdapter) | |
+ (java.awt.image BufferedImage) | |
+ (java.io ByteArrayInputStream) | |
+ (javax.imageio ImageIO))) | |
(defn scaled | |
[x factor] | |
@@ -79,4 +81,3 @@ | |
(ImageIO/read (ByteArrayInputStream. (.evaluateToImage (proto/kernel-link (jlink-instance/get)) "GeoGraphics[]" (int width) (int height) 600 true)))))) | |
;; doesn't make much difference (maybe a bit), seems like we can go lower dpi, but we already get maximum by default (?) | |
- | |
diff --git a/src/wolframite/tools/hiccup.clj b/src/wolframite/tools/hiccup.clj | |
index e38f2b3..94a8a92 100644 | |
--- a/src/wolframite/tools/hiccup.clj | |
+++ b/src/wolframite/tools/hiccup.clj | |
@@ -1,9 +1,11 @@ | |
(ns wolframite.tools.hiccup | |
- (:require [wolframite.core :as wl] | |
- [scicloj.kindly.v4.kind :as kind] | |
- [wolframite.impl.jlink-instance :as jlink-instance] | |
- [wolframite.impl.protocols :as proto]) | |
- (:import (java.util Base64))) | |
+ (:require | |
+ [scicloj.kindly.v4.kind :as kind] | |
+ [wolframite.core :as wl] | |
+ [wolframite.impl.jlink-instance :as jlink-instance] | |
+ [wolframite.impl.protocols :as proto]) | |
+ (:import | |
+ (java.util Base64))) | |
(defn bytes->b64encodedString | |
[^bytes bs] | |
@@ -11,23 +13,23 @@ | |
(defn img [b64img] | |
(kind/hiccup | |
- [:img {:src (format "data:image/jpeg;base64,%s" b64img) | |
- :style {:margin-top "1rem"}}])) | |
+ [:img {:src (format "data:image/jpeg;base64,%s" b64img) | |
+ :style {:margin-top "1rem"}}])) | |
(defn view* [form folded?] | |
(let [wl-str (wl/->wl form {:output-fn str}) | |
input-img (.evaluateToImage (proto/kernel-link (jlink-instance/get)) wl-str 0 0 0 true) | |
b64img (bytes->b64encodedString input-img)] | |
(kind/hiccup | |
- [:div | |
- [:details {:open (not folded?)} | |
- [:summary [:h5 {:style {:display "inline" | |
- :cursor "pointer" | |
- :padding "0.5rem 1rem 0 1rem"}} | |
- wl-str]] | |
- [:div.wl-results | |
- [:hr] | |
- (img b64img)]]]))) | |
+ [:div | |
+ [:details {:open (not folded?)} | |
+ [:summary [:h5 {:style {:display "inline" | |
+ :cursor "pointer" | |
+ :padding "0.5rem 1rem 0 1rem"}} | |
+ wl-str]] | |
+ [:div.wl-results | |
+ [:hr] | |
+ (img b64img)]]]))) | |
(defn view | |
"View a given Wolframite `form` as Hiccup, Kindly compatible. | |
diff --git a/src/wolframite/tools/portal.clj b/src/wolframite/tools/portal.clj | |
index 07d24a6..82d1c07 100644 | |
--- a/src/wolframite/tools/portal.clj | |
+++ b/src/wolframite/tools/portal.clj | |
@@ -1,7 +1,8 @@ | |
(ns wolframite.tools.portal | |
"Integration with https://djblue.github.io/portal/" | |
- (:require [portal.api :as p] | |
- [wolframite.tools.hiccup :as h])) | |
+ (:require | |
+ [portal.api :as p] | |
+ [wolframite.tools.hiccup :as h])) | |
(defn view [form & {:keys [folded?]}] | |
(p/submit (with-meta (h/view* form folded?) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment