From c4c8a0f985e27d1856a724b77c94b9b4dc8ab4ee Mon Sep 17 00:00:00 2001 From: Yang Ming-Tian <1178715749@qq.com> Date: Wed, 27 Dec 2023 16:51:08 +0800 Subject: [PATCH] Add a REPL (#35) * Upgrade Geni to v0.0.42 * Upgrade Clojure to v1.11.1 * Fix dependencies for REPL * Implement Datajure REPL --- project.clj | 18 +++++++++++++++++- src/datajure/dsl.clj | 41 ++++++++++++++++++++++++++++++++++++++++- src/datajure/repl.clj | 31 +++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 src/datajure/repl.clj diff --git a/project.clj b/project.clj index 7eb4aac..ff3ad00 100644 --- a/project.clj +++ b/project.clj @@ -21,8 +21,24 @@ "--add-opens=java.base/java.util.concurrent=ALL-UNNAMED" "--add-opens=java.base/sun.nio.ch=ALL-UNNAMED"] :profiles {:uberjar {:aot :all - :jvm-opts ["-Dclojure.compiler.direct-linking=true"]} + :plugins [[arctype/log4j2-plugins-cache "1.0.0"]] + :middleware [leiningen.log4j2-plugins-cache/middleware] + :manifest {"Multi-Release" true} + :dependencies [[log4j/log4j "1.2.17"] + [org.apache.logging.log4j/log4j-core "2.21.0"] + ;; Arrow + [org.apache.arrow/arrow-memory-core "4.0.0"] + [org.apache.arrow/arrow-vector "4.0.0" + :exclusions [commons-codec com.fasterxml.jackson.core/jackson-databind]] + ;; Spark + [org.apache.spark/spark-avro_2.12 "3.3.3"] + [org.apache.spark/spark-core_2.12 "3.3.3"] + [org.apache.spark/spark-hive_2.12 "3.3.3"] + [org.apache.spark/spark-mllib_2.12 "3.3.3"] + [org.apache.spark/spark-sql_2.12 "3.3.3"] + [org.apache.spark/spark-streaming_2.12 "3.3.3"]]} :test {:dependencies [[org.apache.logging.log4j/log4j-core "2.21.0"]]} + :repl {:dependencies [[org.apache.logging.log4j/log4j-core "2.21.0"]]} :provided {:dependencies [[com.fasterxml.jackson.core/jackson-core "2.15.3"] [com.fasterxml.jackson.core/jackson-annotations "2.15.3"] [org.apache.spark/spark-core_2.12 "3.3.3" :exclusions [org.apache.logging.log4j/log4j-slf4j-impl]] diff --git a/src/datajure/dsl.clj b/src/datajure/dsl.clj index b2e6e78..6633493 100644 --- a/src/datajure/dsl.clj +++ b/src/datajure/dsl.clj @@ -1,5 +1,7 @@ (ns datajure.dsl - (:refer-clojure :exclude [print])) + (:refer-clojure :exclude [print]) + (:require [datajure.repl :as repl]) + (:gen-class)) (require '[tech.v3.dataset :as ds] '[tablecloth.api :as tc] @@ -130,3 +132,40 @@ "Choose `back` as the backend implementation." [back] (reset! backend back)) + +(def init-eval + "The initial form evaluated when the REPL starts up." + '(do + (require + '[tech.v3.dataset :as ds] + '[tablecloth.api :as tc] + '[clojask.dataframe :as ck] + '[zero-one.geni.core :as g] + '[datajure.dsl :as dtj]))) + +(defn- custom-stream [script-path] + (try + (-> (str (slurp script-path) "\nexit\n") + (.getBytes "UTF-8") + (java.io.ByteArrayInputStream.)) + (catch Exception _ + (println (str "Cannot find file " script-path "!")) + (System/exit 1)))) + +(defn -main + "The Datajure CLI entrypoint. + + It does the following: + - Prints a welcome note. + - Launches an nREPL server, which writes to `.nrepl-port` for a + text editor to connect to. + - Starts a REPL(-y). + " + [& args] + (println repl/welcome-note) + (let [script-path (if (empty? args) nil (first args))] + (repl/launch-repl (merge {:port (+ 65001 (rand-int 500)) + :custom-eval init-eval} + (when script-path + {:input-stream (custom-stream script-path)})))) + (System/exit 0)) \ No newline at end of file diff --git a/src/datajure/repl.clj b/src/datajure/repl.clj new file mode 100644 index 0000000..887f9dd --- /dev/null +++ b/src/datajure/repl.clj @@ -0,0 +1,31 @@ +(ns datajure.repl + (:require + [clojure.string] + [clojure.java.io :as io] + [nrepl.server] + [reply.main])) + +(defn- client [opts] + (let [port (:port opts) + host (or (:host opts) "127.0.0.1") + default-opts {:color true :history-file ".nrepl-history"} + opts (assoc (merge default-opts opts) :attach (str host ":" port))] + (reply.main/launch-nrepl opts))) + +(defn datajure-prompt + "Custom Datajure REPL prompt." + [ns-] + (str "datajure-repl (" ns- ")> ")) + +(defn launch-repl + "Starts an nREPL server and steps into a REPL-y." + [opts] + (let [port (:port opts) + server (nrepl.server/start-server :port port)] + (doto (io/file ".nrepl-port") .deleteOnExit (spit port)) + (client (merge {:custom-prompt datajure-prompt} opts)) + (nrepl.server/stop-server server))) + +(def welcome-note + "A REPL welcome note." + (str "Datajure " "1.0.1")) \ No newline at end of file