diff --git a/README.md b/README.md index ff7000b4..0382d1c4 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Goals: Non-goals: * Performance -* Provide a mixed Clojure/bash DSL (see portability). +* Provide a mixed Clojure/bash DSL (see portability). * Replace existing shells. Babashka is a tool you can use inside existing shells like bash and it is designed to play well with them. It does not aim to replace them. Reasons why babashka may not be the right fit for your use case: @@ -162,24 +162,26 @@ namespaces. If not all vars are available, they are enumerated explicitly. - [`clojure.tools.cli`](https://github.com/clojure/tools.cli) aliased as `tools.cli` - [`clojure.data.csv`](https://github.com/clojure/data.csv) aliased as `csv` -From Java the following is available: +The following Java classes are available: -- `Integer`: - - static methods: `parseInt` -- `File`: - - static methods: `createTempFile` - - instance methods: `.canRead`, `.canWrite`, `.delete`, - `.deleteOnExit`, `.exists`, `.getAbsoluteFile`, `.getCanonicalFile`, - `.getCanonicalPath`, `.getName`, `.getParent`, `.getParentFile`, - `.getPath`, `.isAbsolute`, `.isDirectory`, `.isFile`, `.isHidden`, - `.lastModified`, `.length`, `.list`, `.listFiles`, `.mkdir`, - `.mkdirs`, `.renameTo`, `.setLastModified`, `.setReadOnly`, - `.setReadable`, `.toPath`, `.toURI`. -- `System`: - - static methods: `exit`, `getProperty`, `setProperty`, `getProperties`, `getenv` -- `Thread`: - - static methods: `sleep` -- `java.util.regex.Pattern` (all static and instance methods and constants) +``` +ArithmeticException +AssertionError +Boolean +Class +Double +Exception +clojure.lang.ExceptionInfo +java.io.File +Integer +java.io.File +java.util.regex.Pattern +String +System +Thread +``` + +More classes can be added by request. Special vars: @@ -442,8 +444,7 @@ Differences with Clojure: - No first class vars. Note that you can define and redefine global values with `def` / `defn`, but there is no `var` indirection. -- Java classes and interop are not available. For a selection of classes we mimic constructors and interop by having - functions like `Exception.` and `.getCanonicalPath`. +- A subset of Java classes are supported. - Only the `clojure.core`, `clojure.set` and `clojure.string` namespaces are available from Clojure. diff --git a/reflection.json b/reflection.json index c829f5fa..05ee8773 100644 --- a/reflection.json +++ b/reflection.json @@ -1,4 +1,26 @@ [ + { + "name":"java.lang.ArithmeticException", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, + { + "name":"java.lang.AssertionError", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, + { + "name":"java.lang.Boolean", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, + { + "name":"java.io.BufferedReader", + "allPublicMethods":true + }, { "name": "java.lang.Class", "allDeclaredConstructors": true, @@ -7,9 +29,45 @@ "allPublicMethods": true }, { - "name":"java.io.BufferedReader", + "name":"java.lang.Double", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, + { + "name":"java.lang.Exception", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, + { + "name": "clojure.lang.ExceptionInfo", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, + { + "name":"java.lang.Integer", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, + { + "name":"java.io.File", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, + { + "name":"java.util.concurrent.LinkedBlockingQueue", "allPublicMethods":true }, + { + "name":"java.util.regex.Pattern", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, { "name":"java.lang.Process", "allPublicMethods":true @@ -20,14 +78,25 @@ }, { "name":"java.lang.String", - "allPublicMethods":true + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + } + , + { + "name":"java.lang.System", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true + }, + { + "name":"java.lang.Thread", + "allPublicMethods":true, + "allPublicFields": true, + "allPublicConstructors": true }, { "name":"java.lang.UNIXProcess", "allPublicMethods":true - }, - { - "name":"java.util.concurrent.LinkedBlockingQueue", - "allPublicMethods":true } ] diff --git a/sci b/sci index cb1dc313..419d39d2 160000 --- a/sci +++ b/sci @@ -1 +1 @@ -Subproject commit cb1dc3139a53c680b2bdc1038d2c6e26b975ee8e +Subproject commit 419d39d24f9e3676d9922218e747fc277a921a72 diff --git a/src/babashka/impl/File.clj b/src/babashka/impl/File.clj deleted file mode 100644 index d7bcec26..00000000 --- a/src/babashka/impl/File.clj +++ /dev/null @@ -1,87 +0,0 @@ -(ns babashka.impl.File - {:no-doc true} - (:refer-clojure :exclude [list]) - (:require [clojure.java.io :as io] - [clojure.string :as str])) - -(set! *warn-on-reflection* true) -;; also see https://gist.github.com/plexus/68594ba9b5e3f0d63fe84dcae31c4a53 - -(defmacro gen-wrapper-fn [method-name] - `(defn ~method-name {:bb/export true} - [~(with-meta 'x {:tag 'java.io.File})] - (~(symbol (str "." method-name)) ~'x))) - -(defmacro gen-wrapper-fn-2 [method-name] - `(defn ~method-name {:bb/export true} - [~(with-meta 'x {:tag 'java.io.File}) ~'y] - (~(symbol (str "." method-name)) ~'x ~'y))) - -(gen-wrapper-fn canExecute) -(gen-wrapper-fn canRead) -(gen-wrapper-fn canWrite) -(defn createTempFile - ([^String prefix ^String suffix] - (java.io.File/createTempFile prefix suffix)) - ([^String prefix ^String suffix ^java.io.File dir] - (java.io.File/createTempFile prefix suffix dir))) -(gen-wrapper-fn delete) -(gen-wrapper-fn deleteOnExit) -(gen-wrapper-fn exists) -(gen-wrapper-fn getAbsoluteFile) -(gen-wrapper-fn getCanonicalFile) -(gen-wrapper-fn getCanonicalPath) -(gen-wrapper-fn getName) -(gen-wrapper-fn getParent) -(gen-wrapper-fn getParentFile) -(gen-wrapper-fn getPath) -(gen-wrapper-fn isAbsolute) -(gen-wrapper-fn isDirectory) -(gen-wrapper-fn isFile) -(gen-wrapper-fn isHidden) -(gen-wrapper-fn lastModified) -(gen-wrapper-fn length) -(gen-wrapper-fn list) -(gen-wrapper-fn listFiles) -(gen-wrapper-fn mkdir) -(gen-wrapper-fn mkdirs) -(gen-wrapper-fn-2 renameTo) -(defn ^:bb/export setExecutable - ([^java.io.File f b] - (.setExecutable f b)) - ([^java.io.File f b ownerOnly] - (.setExecutable f b ownerOnly))) -(gen-wrapper-fn-2 setLastModified) -(gen-wrapper-fn-2 setReadable) -(gen-wrapper-fn setReadOnly) -(defn ^:bb/export setWritable - ([^java.io.File f b] - (.setWritable f b)) - ([^java.io.File f b ownerOnly] - (.setWritable f b ownerOnly))) -(gen-wrapper-fn toPath) -(gen-wrapper-fn toURI) - -(def file-bindings - (-> (reduce (fn [acc [k v]] - (if (-> v meta :bb/export) - (assoc acc (symbol (str "." k)) - @v) - acc)) - {} - (ns-publics *ns*)) - ;; static methods - (assoc (symbol "File/createTempFile") createTempFile) - (assoc (symbol "java.io.File/createTempFile") createTempFile))) - -(comment - (canRead (clojure.java.io/file "README.md")) - (canWrite (clojure.java.io/file "README.md")) - (exists (clojure.java.io/file "README.md")) - (renameTo (io/file "/tmp/script2.clj") (io/file "/tmp/script.clj")) - (.setWritable (io/file "/tmp/script.clj") true true) - (meta #'toURI) - (meta #'canWrite) - ;; for README.md: - (str/join ", " (map #(format "`%s`" %) (sort (keys file-bindings)))) - ) diff --git a/src/babashka/impl/Integer.clj b/src/babashka/impl/Integer.clj deleted file mode 100644 index 75002bf8..00000000 --- a/src/babashka/impl/Integer.clj +++ /dev/null @@ -1,17 +0,0 @@ -(ns babashka.impl.Integer - {:no-doc true} - (:refer-clojure :exclude [list])) - -(set! *warn-on-reflection* true) - -(defn parseInt - ([^String x] (Integer/parseInt x)) - ([^String x ^long radix] - (Integer/parseInt x radix))) - -(def integer-bindings - {'Integer/parseInt parseInt}) - -(comment - - ) diff --git a/src/babashka/impl/Pattern.clj b/src/babashka/impl/Pattern.clj deleted file mode 100644 index aa0d0f62..00000000 --- a/src/babashka/impl/Pattern.clj +++ /dev/null @@ -1,72 +0,0 @@ -(ns babashka.impl.Pattern - {:no-doc true}) - -(set! *warn-on-reflection* true) - -(defmacro gen-wrapper-fn [method-name] - `(defn ~method-name {:bb/export true} - [~(with-meta 'x {:tag 'java.util.regex.Pattern})] - (~(symbol (str "." method-name)) ~'x))) - -(defmacro gen-wrapper-fn-2 [method-name] - `(defn ~method-name {:bb/export true} - [~(with-meta 'x {:tag 'java.util.regex.Pattern}) ~'y] - (~(symbol (str "." method-name)) ~'x ~'y))) - -(defmacro gen-constants [& constant-names] - (let [defs (for [constant-name constant-names - :let [full-name (symbol (str "java.util.regex.Pattern/" constant-name)) - meta {:bb/export-constant true - :bb/full-name (list 'quote full-name)} - constant-name (with-meta constant-name meta)]] - `(def ~constant-name - ~full-name))] - `(do ~@defs))) - -(gen-constants CANON_EQ CASE_INSENSITIVE COMMENTS DOTALL LITERAL MULTILINE - UNICODE_CASE UNICODE_CHARACTER_CLASS UNIX_LINES) - -(gen-wrapper-fn asPredicate) - -(defn compile* - ([^String s] - (java.util.regex.Pattern/compile s)) - ([^String s ^long flags] - (java.util.regex.Pattern/compile s flags))) - -(gen-wrapper-fn flags) - -(gen-wrapper-fn-2 matcher) - -(defn matches [^String regex ^CharSequence input] - (java.util.regex.Pattern/matches regex input)) - -(gen-wrapper-fn pattern) - -(defn pattern-quote [s] - (java.util.regex.Pattern/quote s)) - -(defn ^:bb/export split - ([^java.util.regex.Pattern p ^CharSequence input] - (.split p input)) - ([^java.util.regex.Pattern p ^CharSequence input ^long limit] - (.split p input limit))) - -(gen-wrapper-fn-2 splitAsStream) - -(def pattern-bindings - (-> (reduce (fn [acc [k v]] - (let [m (meta v)] - (cond (:bb/export m) - (assoc acc (symbol (str "." k)) - @v), - (:bb/export-constant m) - (assoc acc (symbol (:bb/full-name m)) - @v) - :else acc))) - {} - (ns-publics *ns*)) - ;; static method - (assoc (symbol "java.util.regex.Pattern/compile") compile*) - (assoc (symbol "java.util.regex.Pattern/quote") pattern-quote) - (assoc (symbol "java.util.regex.Pattern/matches") matches))) diff --git a/src/babashka/impl/System.clj b/src/babashka/impl/System.clj deleted file mode 100644 index a937e990..00000000 --- a/src/babashka/impl/System.clj +++ /dev/null @@ -1,28 +0,0 @@ -(ns babashka.impl.System - {:no-doc true}) - -(defn get-env - ([] (System/getenv)) - ([s] (System/getenv s))) - -(defn get-property - ([s] - (System/getProperty s)) - ([s d] - (System/getProperty s d))) - -(defn set-property [k v] - (System/setProperty k v)) - -(defn get-properties [] - (System/getProperties)) - -(defn exit [n] - (throw (ex-info "" {:bb/exit-code n}))) - -(def system-bindings - {'System/getenv get-env - 'System/getProperty get-property - 'System/setProperty set-property - 'System/getProperties get-properties - 'System/exit exit}) diff --git a/src/babashka/impl/Thread.clj b/src/babashka/impl/Thread.clj deleted file mode 100644 index d224d986..00000000 --- a/src/babashka/impl/Thread.clj +++ /dev/null @@ -1,9 +0,0 @@ -(ns babashka.impl.Thread - {:no-doc true}) - -(defn sleep - ([millis] (Thread/sleep millis)) - ([millis nanos] (Thread/sleep millis nanos))) - -(def thread-bindings - {'Thread/sleep sleep}) diff --git a/src/babashka/main.clj b/src/babashka/main.clj index f2f7d222..3f4accf9 100644 --- a/src/babashka/main.clj +++ b/src/babashka/main.clj @@ -1,20 +1,12 @@ (ns babashka.main {:no-doc true} (:require - [babashka.impl.File :refer [file-bindings]] - [babashka.impl.Integer :refer [integer-bindings]] - [babashka.impl.Double :refer [double-bindings]] - [babashka.impl.Boolean :refer [boolean-bindings]] - [babashka.impl.Pattern :refer [pattern-bindings]] - [babashka.impl.System :refer [system-bindings]] - [babashka.impl.Thread :refer [thread-bindings]] [babashka.impl.async :refer [async-namespace]] [babashka.impl.clojure.core :refer [core-extras]] [babashka.impl.clojure.java.io :refer [io-namespace]] [babashka.impl.clojure.stacktrace :refer [print-stack-trace]] [babashka.impl.conch :refer [conch-namespace]] [babashka.impl.csv :as csv] - [babashka.impl.exceptions :refer [exception-bindings]] [babashka.impl.pipe-signal-handler :refer [handle-pipe! pipe-signal-received?]] [babashka.impl.socket-repl :as socket-repl] [babashka.impl.tools.cli :refer [tools-cli-namespace]] @@ -150,16 +142,6 @@ Everything after that is bound to *command-line-args*.")) (str/replace x #"^#!.*" "")) (throw (Exception. (str "File does not exist: " file)))))) -(def bindings - (merge system-bindings - file-bindings - thread-bindings - integer-bindings - double-bindings - boolean-bindings - exception-bindings - pattern-bindings)) - (defn read-edn [] (edn/read {;;:readers *data-readers* :eof ::EOF} *in*)) @@ -167,7 +149,7 @@ Everything after that is bound to *command-line-args*.")) (defn load-file* [ctx file] (let [s (slurp file)] (sci/eval-string s ctx))) - + (defn eval* [ctx form] (sci/eval-string (pr-str form) ctx)) @@ -180,6 +162,9 @@ Everything after that is bound to *command-line-args*.")) ;; hang until SIGINT @(promise))) +(defn exit [n] + (throw (ex-info "" {:bb/exit-code n}))) + (defn main [& args] (handle-pipe!) @@ -223,10 +208,36 @@ Everything after that is bound to *command-line-args*.")) 'me.raynes.conch.low-level conch-namespace 'clojure.core.async async-namespace 'clojure.data.csv csv/csv-namespace} - :bindings (assoc bindings '*command-line-args* command-line-args) + :bindings {'*command-line-args* command-line-args + 'java.lang.System/exit exit ;; override exit, so we have more control + 'System/exit exit} :env env - :features #{:bb}} - ctx (update ctx :bindings assoc 'eval #(eval* ctx %) + :features #{:bb} + :classes {'java.lang.ArithmeticException ArithmeticException + 'java.lang.AssertionError AssertionError + 'java.lang.Boolean Boolean + 'java.lang.Class Class + 'java.lang.Double Double + 'java.lang.Exception Exception + 'clojure.lang.ExceptionInfo clojure.lang.ExceptionInfo + 'java.lang.Integer Integer + 'java.io.File java.io.File + 'java.util.regex.Pattern java.util.regex.Pattern + 'java.lang.String String + 'java.lang.System System + 'java.lang.Thread Thread} + :imports '{ArithmeticException java.lang.ArithmeticException + AssertionError java.lang.AssertionError + Boolean java.lang.Boolean + Class java.lang.Class + Double java.lang.Double + Exception java.lang.Exception + Integer java.lang.Integer + File java.io.File + String java.lang.String + System java.lang.System + Thread java.lang.Thread}} + ctx (update ctx :bindings assoc 'eval #(eval* ctx %) 'load-file #(load-file* ctx %)) _preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim) (sci/eval-string ctx)) exit-code